diff --git a/.gitattributes b/.gitattributes index 1ff0c423042b..aed94030e6e8 100644 --- a/.gitattributes +++ b/.gitattributes @@ -61,3 +61,5 @@ #*.PDF diff=astextplain #*.rtf diff=astextplain #*.RTF diff=astextplain + +*.h linguist-language=C++ diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index abffc2f8522a..9aed62a5c8da 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -6,7 +6,7 @@ We are happy to hear your feedback for the future of Files. Check the submitted similar feedback. You can add your feedback to existing issues or you can open a new issue. We always look at your feedback when we decide what to work on next and we look forward to hearing your ideas. Remember that -all community interactions must abide by the [Code of Conduct](https://github.com/files-community/Files/blob/main/CODE_OF_CONDUCT.md). +all community interactions must abide by the [Code of Conduct](./CODE_OF_CONDUCT.md). ## Finding issues you can help with Looking for something to work on? diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 3a7986154b67..b4927a44d3e1 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1 @@ -github: [yaira2, Josh65-2201, marcelwgn, gave92] +github: [yair100, Josh65-2201, "0x5BFA"] diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 17c1934b96de..119acc654dbf 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,8 +1,16 @@ name: Bug Report -description: Create a bug report to help improve Files -labels: [bug] +description: Create a bug report to help improve Files. +type: 'Bug' +title: 'Bug: ' body: + # Tip to warn of checking for existing issues + - type: markdown + attributes: + value: | + > [!TIP] + > Have you checked for similar issues? There's a possibility the bug you are experiencing has already been reported. Please do a thorough search before creating a new issue. + # Description - type: textarea attributes: @@ -23,22 +31,12 @@ body: validations: required: true - # Requirements - - type: textarea - attributes: - label: Requirements - description: describe the expected behavior (eg. Fix crash that occurs when...). If left blank, the issue will likely be closed without discussion. - value: | - - e.g. Fix crash that occurs when... - validations: - required: true - # Files Version - type: input id: files_version attributes: label: Files Version - description: Which version of Files are you using? To copy your Files version, access it from Settings -> About -> Copy -> Files version + description: Which version of Files are you using? To copy your Files version, access it from Settings -> About -> Copy -> Files version. placeholder: "e.g. 2.0.34.0" validations: required: true @@ -48,11 +46,32 @@ body: id: windows_version attributes: label: Windows Version - description: Which version of Windows are you using? To copy your Windows version, access it from Settings -> About -> Copy -> Windows version + description: Which version of Windows are you using? To copy your Windows version, access it from Settings -> About -> Copy -> Windows version. placeholder: "e.g. 10.0.22621.1848" validations: required: true + # User ID + - type: input + id: user_id + attributes: + label: User ID + description: Your User ID is a GUID that can be matched up with bug reports in Sentry. To copy your User ID, access it from Settings -> About -> Copy -> User ID. + placeholder: "74750836-5885-4eeb-964f-8177cb4babf6" + validations: + required: false + + # Note that the user should always upload the report rather than pasting it. + - type: markdown + attributes: + value: | + > [!IMPORTANT] + > Please **upload** the log file by clicking "Attach files". + > + > The log file can be accessed from **Settings → About → Open log location**. If you cannot access the UI, you can locate the file [manually](https://files.community/docs/getting-started/faq#:~:text=How%20to%20Locate%20the%20Log%20File). + > + > Feel free to remove potentially sensitive information such as usernames or drive names before uploading the file. + # Log File - type: textarea attributes: @@ -60,8 +79,6 @@ body: description: | Please upload the log file as an attachment (DO NOT COPY OR PASTE THE CONTENTS INTO THIS FIELD) placeholder: | - To upload the log, access it from Settings -> About -> Open log location; Or - - Select the `debug.log` file from `%localappdata%\Packages\49306atecsolution.FilesUWP_et10x9a9vyk8t\LocalState` - - Drag and drop the file to upload as an attachment + Please upload the log file as an attachment (DO NOT COPY OR PASTE THE CONTENTS INTO THIS FIELD) validations: required: true diff --git a/.github/ISSUE_TEMPLATE/code_quality_issue.yml b/.github/ISSUE_TEMPLATE/code_quality_issue.yml index 0f30c6c07e48..b1613fa819b7 100644 --- a/.github/ISSUE_TEMPLATE/code_quality_issue.yml +++ b/.github/ISSUE_TEMPLATE/code_quality_issue.yml @@ -1,19 +1,33 @@ name: Code Quality Issue description: Create a code quality issue to help Files keep a clean codebase -labels: [codebase quality] +type: 'Code quality' +title: 'Code Quality: ' body: + + # Tip to warn of checking for existing issues + - type: markdown + attributes: + value: | + > [!TIP] + > Have you checked for similar code quality issues? There's a possibility your suggestion is already being tracked. Please do a thorough search before creating a new issue. + + # Issue body - type: textarea attributes: label: Description description: A clear and concise description of what the code quality issue is. validations: required: true + + # Related code - type: textarea attributes: label: Concerned code description: A list of the different files and/or areas of the code concerned by the issue. validations: required: true + + # Gains - type: textarea attributes: label: Gains @@ -24,6 +38,8 @@ body: - eg. Clarifying the responsibility of class C. validations: required: true + + # Requirements - type: textarea attributes: label: Requirements @@ -32,6 +48,8 @@ body: - eg. Using a specific design pattern. - eg. Separating Interface I into three new interfaces I1, I2 and I3. - eg. Regrouping the duplicated process into a new helper. + + # Remarks - type: textarea attributes: label: Comments diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index b7a493f528ae..31ea2d803bad 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,8 +1,16 @@ name: Feature Request -description: This project thrives from differentiation from competing apps. Suggest an idea for Files -labels: [feature request] +description: This project thrives from differentiation from competing apps. Suggest an idea for Files. +type: 'Feature request' +title: 'Feature: ' body: + # Tip to warn of checking for existing issues + - type: markdown + attributes: + value: | + > [!TIP] + >Have you checked for similar feature requests? There's a possibility your suggestion is already being tracked. Please do a thorough search before creating a new issue. + # Description - type: textarea attributes: @@ -23,7 +31,7 @@ body: - type: textarea attributes: label: Requirements - description: Describe all the requirements to make your idea happen + description: Describe all the requirements to make your idea happen. value: | - This proposal will accomplish X - This proposal will accomplish Y @@ -36,7 +44,7 @@ body: id: files_version attributes: label: Files Version - description: Which version of Files are you using? To copy your Files version, access it from Settings -> About -> Copy -> Files version + description: Which version of Files are you using? To copy your Files version, access it from Settings -> About -> Copy -> Files version. placeholder: 'e.g. 2.0.36.0' validations: required: true @@ -46,7 +54,7 @@ body: id: windows_version attributes: label: Windows Version - description: Which version of Windows are you using? To copy your Windows version, access it from Settings -> About -> Copy -> Windows version + description: Which version of Windows are you using? To copy your Windows version, access it from Settings -> About -> Copy -> Windows version. placeholder: "e.g. 10.0.22621.1848" validations: required: true diff --git a/.github/NOTICE.md b/.github/NOTICE.md index 4d35e76928f5..ce6e5bb8960d 100644 --- a/.github/NOTICE.md +++ b/.github/NOTICE.md @@ -199,35 +199,6 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` -## INI File Parser - -**Source**: [https://github.com/rickyah/ini-parser](https://github.com/rickyah/ini-parser) - -### License - -``` -The MIT License (MIT) - -Copyright (c) 2008 Ricardo Amores Hernández - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -``` - ## SevenZipSharp **Source**: [https://github.com/files-community/SevenZipSharp](https://github.com/files-community/SevenZipSharp) @@ -645,16 +616,16 @@ A "contributor" is any person that distributes its contribution under this licen (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. ``` -## Vanara +## libgit2sharp -**Source**: [https://github.com/dahall/Vanara](https://github.com/dahall/Vanara) +**Source**: [https://github.com/libgit2/libgit2sharp](https://github.com/libgit2/libgit2sharp) ### License ``` MIT License -Copyright (c) 2017 David Hall +Copyright (c) LibGit2Sharp contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -663,58 +634,57 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. ``` -## WinUIEx +## DiscUtils -**Source**: [https://github.com/dotMorten/WinUIEx](https://github.com/dotMorten/WinUIEx) +**Source**: [https://github.com/DiscUtils/DiscUtils](https://github.com/DiscUtils/DiscUtils) ### License ``` -MIT License - -Copyright (c) 2021 Morten Nielsen +Copyright (c) 2008-2011, Kenneth Bell +Copyright (c) 2014, Quamotion -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. ``` -## libgit2sharp +## NaturalStringComparer -**Source**: [https://github.com/libgit2/libgit2sharp](https://github.com/libgit2/libgit2sharp) +**Source**: [https://github.com/GihanSoft/NaturalStringComparer](https://github.com/GihanSoft/NaturalStringComparer) ### License ``` MIT License -Copyright (c) LibGit2Sharp contributors +Copyright (c) 2018 Mohammad Babayi Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -723,43 +693,30 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. ``` -## DiscUtils +## Dongle.GuidRVAGen -**Source**: [https://github.com/DiscUtils/DiscUtils](https://github.com/DiscUtils/DiscUtils) +**Source**: [https://github.com/dongle-the-gadget/GuidRVAGen](https://github.com/dongle-the-gadget/GuidRVAGen) ### License ``` -Copyright (c) 2008-2011, Kenneth Bell -Copyright (c) 2014, Quamotion +Copyright (c) 2024 Dongle -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. -``` \ No newline at end of file +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +``` diff --git a/.github/PRIVACY.md b/.github/PRIVACY.md index 3f84777afbd5..75829f6c6ebf 100644 --- a/.github/PRIVACY.md +++ b/.github/PRIVACY.md @@ -1,31 +1,3 @@ # Privacy Policy -###### Effective date: June 2, 2024 - -This Privacy Policy ("Policy") for Files ("we", "us", or "our") describes how and why we collect, store, use, and disclose information about users ("you", "your") when you use our services ("Services"), and our desktop application (the "Application", "Files"). By using our Services, you acknowledge and consent to the practices described in this Policy. - -## Table of Contents - -1. Information We Collect -2. How We Use Your Information -3. Changes to This Privacy Policy - -## Information We Collect - -**Diagnostic Information.** Our Application collects non-personably identifiable diagnostic data such as fault analysis and performance logs. This information may include technical details about your device, such as its make, model, operating system, and Application version. In addition, Files may store log files containing diagnostic information on the user's device. These files are not shared with us by default. - -**Usage Information.** We collect non-identifiable information about your use of our Services, including but not limited to interactions within the Application, enabled user preferences (Application settings), and usage patterns. Our Application uses Sentry for error reporting, please review the Sentry Privacy Policy here: https://sentry.io/privacy/. - -Furthermore, we use certain Microsoft services such as Microsoft Partner Center to collect usage data, to learn more about how data is collected, used, and disclosed by Microsoft and its subsidiaries, please review the Microsoft Privacy Policy Statement available here: https://privacy.microsoft.com/privacystatement - -Please note that any information we collect is non-identifiable and does not include any personal data. We do not share collected information with any third parties. - -## How We Use Collected Information - -**Mitigate Reliability Issues.** We may use the aggregated information to diagnose and address any unexpected issues that may arise during the use of our Application. This may include analyzing diagnostic data, such as fault analysis and performance logs, to identify the root cause of the issue and develop a solution to resolve it. - -**Identify Usage Trends.** We may use the information to identify and analyze usage trends for our Services and Application. This may include analyzing aggregated usage data to understand how users interact with our Services and to measure their utilization intensity. We may use this analysis to improve the functionality, usability, and performance of our Services and to inform future development decisions. - -## Changes to This Privacy Policy - -We may modify, update, or amend this Privacy Policy from time to time to reflect changes made to our Application. When we change this Policy in a material manner, we will inform you of such changes by updating the 'Effective date' notice. It is your responsibility to review this Privacy Policy periodically to stay informed of any updates. Your continued use of the Services after any modifications to this Policy constitutes your acceptance of such changes. +The privacy policy has been moved to here https://files.community/privacy \ No newline at end of file diff --git a/.github/README.md b/.github/README.md index af7d0f82e80b..a0b1f0493c68 100644 --- a/.github/README.md +++ b/.github/README.md @@ -1,5 +1,5 @@

- Files hero image + Files hero image

@@ -25,16 +25,14 @@ You can also use the preview version alongside the stable release to get early a - - - + + - + - - - + +

## Building from source @@ -50,4 +48,4 @@ Looking for a place to start? Check out the [task board](https://github.com/orgs ## Screenshots -![Files](../assets/FilesScreenshot.png) +![Files](./assets/FilesScreenshot.png) diff --git a/.github/assets/ClassicInstallerBadge-dark.png b/.github/assets/ClassicInstallerBadge-dark.png new file mode 100644 index 000000000000..a1ecea01a2b8 Binary files /dev/null and b/.github/assets/ClassicInstallerBadge-dark.png differ diff --git a/.github/assets/ClassicInstallerBadge-light.png b/.github/assets/ClassicInstallerBadge-light.png new file mode 100644 index 000000000000..be0886d1bd36 Binary files /dev/null and b/.github/assets/ClassicInstallerBadge-light.png differ diff --git a/.github/assets/FilesScreenshot.png b/.github/assets/FilesScreenshot.png new file mode 100644 index 000000000000..21109046b11a Binary files /dev/null and b/.github/assets/FilesScreenshot.png differ diff --git a/.github/assets/ReadmeHero.png b/.github/assets/ReadmeHero.png new file mode 100644 index 000000000000..ccd33a74cfbc Binary files /dev/null and b/.github/assets/ReadmeHero.png differ diff --git a/.github/assets/StoreBadge-dark.png b/.github/assets/StoreBadge-dark.png new file mode 100644 index 000000000000..d739cc59ab80 Binary files /dev/null and b/.github/assets/StoreBadge-dark.png differ diff --git a/.github/assets/StoreBadge-light.png b/.github/assets/StoreBadge-light.png new file mode 100644 index 000000000000..2fe14bbb0b10 Binary files /dev/null and b/.github/assets/StoreBadge-light.png differ diff --git a/.github/dependabot.yml b/.github/dependabot.yml index aec207e4342a..5ca153e5848f 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,7 +1,7 @@ version: 2 updates: -- package-ecosystem: nuget - directory: "/" - schedule: - interval: daily - open-pull-requests-limit: 10 + - package-ecosystem: nuget + directory: / + schedule: + interval: daily + open-pull-requests-limit: 10 diff --git a/.github/scripts/Configure-AppxManifest.ps1 b/.github/scripts/Configure-AppxManifest.ps1 new file mode 100644 index 000000000000..aaada2c0cb14 --- /dev/null +++ b/.github/scripts/Configure-AppxManifest.ps1 @@ -0,0 +1,183 @@ +# Copyright (c) Files Community +# Licensed under the MIT License. + +param( + [string]$Branch = "", # This has to correspond with one of the AppEnvironment enum values + [string]$PackageManifestPath = "", + [string]$Publisher = "", + [string]$WorkingDir = "", + [string]$SecretBingMapsKey = "", + [string]$SecretSentry = "", + [string]$SecretGitHubOAuthClientId = "" +) + +# Load Package.appxmanifest +[xml]$xmlDoc = Get-Content $PackageManifestPath + +# Add namespaces +$nsmgr = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable) +$nsmgr.AddNamespace("pkg", "http://schemas.microsoft.com/appx/manifest/foundation/windows10") +$nsmgr.AddNamespace("rescap", "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities") +$nsmgr.AddNamespace("uap", "http://schemas.microsoft.com/appx/manifest/uap/windows10") +$nsmgr.AddNamespace("uap5", "http://schemas.microsoft.com/appx/manifest/uap/windows10/5") +$ap = $xmlDoc.SelectSingleNode("/pkg:Package/pkg:Applications/pkg:Application/pkg:Extensions/uap:Extension[@Category='windows.protocol']/uap:Protocol", $nsmgr) +$aea = $xmlDoc.SelectSingleNode("/pkg:Package/pkg:Applications/pkg:Application/pkg:Extensions/uap5:Extension[@Category='windows.appExecutionAlias']/uap5:AppExecutionAlias", $nsmgr) +$ea = $xmlDoc.SelectSingleNode("/pkg:Package/pkg:Applications/pkg:Application/pkg:Extensions/uap5:Extension[@Category='windows.appExecutionAlias']/uap5:AppExecutionAlias/uap5:ExecutionAlias", $nsmgr) + +# Update the publisher +$xmlDoc.Package.Identity.Publisher = $Publisher + +if ($Branch -eq "SideloadPreview") +{ + # Set identities + $xmlDoc.Package.Identity.Name="FilesPreview" + $xmlDoc.Package.Properties.DisplayName="Files - Preview" + $xmlDoc.Package.Applications.Application.VisualElements.DisplayName="Files - Preview" + $xmlDoc.Package.Applications.Application.VisualElements.DefaultTile.ShortName="Files - Preview" + + # Update app protocol and execution alias + $ap.SetAttribute("Name", "files-preview"); + $ea.SetAttribute("Alias", "files-preview.exe"); + + # Save modified Package.appxmanifest + $xmlDoc.Save($PackageManifestPath) + + Get-ChildItem $WorkingDir -Include *.csproj, *.appxmanifest, *.xaml -recurse | ForEach-Object -Process ` + { ` + (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "Assets\\AppTiles\\Dev", "Assets\AppTiles\Preview" }) | ` + Set-Content $_ -NoNewline ` + } + + Get-ChildItem $WorkingDir -Include *.cs, *.cpp -recurse | ForEach-Object -Process ` + { ` + (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "files-dev", "files-preview" }) | ` + Set-Content $_ -NoNewline ` + } + +} +elseif ($Branch -eq "StorePreview") +{ + # Set identities + $xmlDoc.Package.Identity.Name="49306atecsolution.FilesPreview" + $xmlDoc.Package.Properties.DisplayName="Files - Preview" + $xmlDoc.Package.Applications.Application.VisualElements.DisplayName="Files - Preview" + $xmlDoc.Package.Applications.Application.VisualElements.DefaultTile.ShortName="Files - Preview" + + # Remove capability that is only used for the sideload package + $nsmgr = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable) + $nsmgr.AddNamespace("pkg", "http://schemas.microsoft.com/appx/manifest/foundation/windows10") + $nsmgr.AddNamespace("rescap", "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities") + $pm = $xmlDoc.SelectSingleNode("/pkg:Package/pkg:Capabilities/rescap:Capability[@Name='packageManagement']", $nsmgr) + $xmlDoc.Package.Capabilities.RemoveChild($pm) + + # Update app protocol and execution alias + $ap.SetAttribute("Name", "files-preview"); + $ea.SetAttribute("Alias", "files-preview.exe"); + + $xmlDoc.Save($PackageManifestPath) + + Get-ChildItem $WorkingDir -Include *.csproj, *.appxmanifest, *.xaml -recurse | ForEach-Object -Process ` + { ` + (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "Assets\\AppTiles\\Dev", "Assets\AppTiles\Preview" }) | ` + Set-Content $_ -NoNewline ` + } + + Get-ChildItem $WorkingDir -Include *.cs, *.cpp -recurse | ForEach-Object -Process ` + { ` + (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "files-dev", "files-preview" }) | ` + Set-Content $_ -NoNewline ` + } + +} +elseif ($Branch -eq "SideloadStable") +{ + # Set identities + $xmlDoc.Package.Identity.Name="Files" + $xmlDoc.Package.Properties.DisplayName="Files" + $xmlDoc.Package.Applications.Application.VisualElements.DisplayName="Files" + $xmlDoc.Package.Applications.Application.VisualElements.DefaultTile.ShortName="Files" + + # Update app protocol and execution alias + $ap.SetAttribute("Name", "files-stable"); + $ea.SetAttribute("Alias", "files-stable.exe"); + + # Save modified Package.appxmanifest + $xmlDoc.Save($PackageManifestPath) + + Get-ChildItem $WorkingDir -Include *.csproj, *.appxmanifest, *.xaml -recurse | ForEach-Object -Process ` + { ` + (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "Assets\\AppTiles\\Dev", "Assets\AppTiles\Release" }) | ` + Set-Content $_ -NoNewline ` + } + + Get-ChildItem $WorkingDir -Include *.cs, *.cpp -recurse | ForEach-Object -Process ` + { ` + (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "files-dev", "files-stable" }) | ` + Set-Content $_ -NoNewline ` + } + +} +elseif ($Branch -eq "StoreStable") +{ + # Set identities + $xmlDoc.Package.Identity.Name="49306atecsolution.FilesUWP" + $xmlDoc.Package.Properties.DisplayName="Files App" + $xmlDoc.Package.Applications.Application.VisualElements.DisplayName="Files" + $xmlDoc.Package.Applications.Application.VisualElements.DefaultTile.ShortName="Files" + + # Remove capability that is only used for the sideload package + $pm = $xmlDoc.SelectSingleNode("/pkg:Package/pkg:Capabilities/rescap:Capability[@Name='packageManagement']", $nsmgr) + $xmlDoc.Package.Capabilities.RemoveChild($pm) + + # Update app protocol and execution alias + $ap.SetAttribute("Name", "files-stable"); + $ea.SetAttribute("Alias", "files-stable.exe"); + + # Save modified Package.appxmanifest + $xmlDoc.Save($PackageManifestPath) + + Get-ChildItem $WorkingDir -Include *.csproj, *.appxmanifest, *.xaml -recurse | ForEach-Object -Process ` + { ` + (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "Assets\\AppTiles\\Dev", "Assets\AppTiles\Release" }) | ` + Set-Content $_ -NoNewline ` + } + + Get-ChildItem $WorkingDir -Include *.cs, *.cpp -recurse | ForEach-Object -Process ` + { ` + (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "files-dev", "files-stable" }) | ` + Set-Content $_ -NoNewline ` + } + +} + +# Remove unused tile assets so they don't end up in the package +$keepTiles = if ($Branch -match 'Preview') { 'Preview' } elseif ($Branch -match 'Stable') { 'Release' } else { $null } +foreach ($folder in @('Dev', 'Preview', 'Release')) { + if ($folder -eq $keepTiles) { continue } + $tilePath = Join-Path $WorkingDir "src\Files.App\Assets\AppTiles\$folder" + if (Test-Path $tilePath) { Remove-Item $tilePath -Recurse -Force } +} + +Get-ChildItem $WorkingDir -Include *.cs -recurse | ForEach-Object -Process ` +{ ` + (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "cd_app_env_placeholder", $Branch }) | ` + Set-Content $_ -NoNewline ` +} + +Get-ChildItem $WorkingDir -Include *.cs -recurse | ForEach-Object -Process ` +{ ` + (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "bingmapskey.secret", "$SecretBingMapsKey" }) | ` + Set-Content $_ -NoNewline ` +} + +Get-ChildItem $WorkingDir -Include *.cs -recurse | ForEach-Object -Process ` +{ + (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "sentry.secret", "$SecretSentry" }) | ` + Set-Content $_ -NoNewline ` +} + +Get-ChildItem $WorkingDir -Include *.cs -recurse | ForEach-Object -Process ` +{ ` + (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "githubclientid.secret", "$SecretGitHubOAuthClientId" }) | ` + Set-Content $_ -NoNewline ` +} diff --git a/scripts/Convert-TrxToMarkdown.ps1 b/.github/scripts/Convert-TrxToMarkdown.ps1 similarity index 98% rename from scripts/Convert-TrxToMarkdown.ps1 rename to .github/scripts/Convert-TrxToMarkdown.ps1 index f96429ffae76..c04a91cc4561 100644 --- a/scripts/Convert-TrxToMarkdown.ps1 +++ b/.github/scripts/Convert-TrxToMarkdown.ps1 @@ -1,5 +1,5 @@ -# Copyright (c) 2024 Files Community -# Licensed under the MIT License. See the LICENSE. +# Copyright (c) Files Community +# Licensed under the MIT License. # Abstract: # This script analyzes the trx file that is the result of executing dotnet test and diff --git a/.github/scripts/Create-MsixBundle.ps1 b/.github/scripts/Create-MsixBundle.ps1 new file mode 100644 index 000000000000..af21e22d64a7 --- /dev/null +++ b/.github/scripts/Create-MsixBundle.ps1 @@ -0,0 +1,275 @@ +# Copyright (c) Files Community +# Licensed under the MIT License. + +# Creates an .msixbundle from individual per-platform .msix packages produced +# by single-project MSIX packaging. Optionally generates an .appinstaller file +# for sideload deployments, and an .msixupload for Store submissions. + +param( + [Parameter(Mandatory)] + [string]$AppxPackageDir, + + [Parameter(Mandatory)] + [string]$BundleName, + + [string]$Version = "", + + [string]$PackageManifestPath = "", + + [string]$AppInstallerUri = "", + + [ValidateSet("Sideload", "StoreUpload", "SideloadOnly")] + [string]$BuildMode = "Sideload" +) + +$ErrorActionPreference = "Stop" + +function Ensure-Directory([string]$Path) { + if (-not (Test-Path $Path)) { New-Item -ItemType Directory -Path $Path | Out-Null } +} + +# Canonical arch names for the WAP-compatible folder layout +$archMap = @{ 'arm64' = 'ARM64'; 'x64' = 'x64'; 'x86' = 'x86' } + +# --- Locate makeappx.exe --- + +$sdkRoot = "${env:ProgramFiles(x86)}\Windows Kits\10\bin" +$makeAppx = Get-ChildItem -Path $sdkRoot -Filter "makeappx.exe" -Recurse -ErrorAction SilentlyContinue | + Where-Object { $_.DirectoryName -match '\\x64$' } | + Sort-Object { [version]($_.DirectoryName -replace '^.*\\bin\\([^\\]+)\\.*$','$1') } -Descending | + Select-Object -First 1 +if ($null -eq $makeAppx) { + Write-Error "makeappx.exe not found under '$sdkRoot'" + exit 1 +} +Write-Host "Using makeappx: $($makeAppx.FullName)" + +# --- Discover per-platform .msix packages --- + +$msixFiles = Get-ChildItem -Path $AppxPackageDir -Filter "*.msix" -Recurse | + Where-Object { $_.DirectoryName -notmatch '\\Dependencies\\' } +if ($msixFiles.Count -eq 0) { + Write-Error "No .msix files found in '$AppxPackageDir'" + exit 1 +} +Write-Host "Found $($msixFiles.Count) .msix package(s):" +$msixFiles | ForEach-Object { Write-Host " $_" } + +if ($Version -eq "" -and $PackageManifestPath -ne "" -and (Test-Path $PackageManifestPath)) { + [xml]$versionManifest = Get-Content $PackageManifestPath + $Version = $versionManifest.Package.Identity.Version + Write-Host "Version from manifest: $Version" +} +if ($Version -eq "") { + $Version = $msixFiles[0].BaseName -replace '^[^_]+_([^_]+)_.*$','$1' + Write-Host "Detected version from filename: $Version" +} + +$platformList = $msixFiles | ForEach-Object { $_.BaseName -replace '.*_(\w+)$','$1' } | Sort-Object -Descending +$platforms = $platformList -join '_' + +# --- Create .msixbundle --- + +$mappingDir = Join-Path $AppxPackageDir "_bundletemp" +if (Test-Path $mappingDir) { Remove-Item $mappingDir -Recurse -Force } +New-Item -ItemType Directory -Path $mappingDir | Out-Null +foreach ($msix in $msixFiles) { Copy-Item $msix.FullName -Destination $mappingDir } + +$bundlePath = Join-Path $AppxPackageDir "$BundleName.msixbundle" +if (Test-Path $bundlePath) { Remove-Item $bundlePath -Force } + +Write-Host "Creating msixbundle at: $bundlePath" +& $makeAppx.FullName bundle /d $mappingDir /p $bundlePath /bv $Version /o +if ($LASTEXITCODE -ne 0) { + Write-Error "MakeAppx bundle creation failed with exit code $LASTEXITCODE" + exit 1 +} +Remove-Item $mappingDir -Recurse -Force +Write-Host "Successfully created: $bundlePath" + +# --- Reorganize into WAP-compatible folder structure for CDN upload --- +# Target layout: {BundleName}_{Version}_Test/{BundleName}_{Version}_{platforms}.msixbundle +# {BundleName}_{Version}_Test/Dependencies/{arch}/... + +$bundleFolder = "${BundleName}_${Version}_Test" +$bundleFolderPath = Join-Path $AppxPackageDir $bundleFolder +Ensure-Directory $bundleFolderPath + +$organizedBundleName = "${BundleName}_${Version}_${platforms}.msixbundle" +$organizedBundlePath = Join-Path $bundleFolderPath $organizedBundleName +Move-Item $bundlePath $organizedBundlePath -Force +Write-Host "Moved bundle to: $organizedBundlePath" + +# --- Merge dependency folders from each per-platform build --- + +$organizedDepsDir = Join-Path $bundleFolderPath "Dependencies" +foreach ($msix in $msixFiles) { + $perPlatDepsDir = Join-Path $msix.DirectoryName "Dependencies" + if (-not (Test-Path $perPlatDepsDir)) { continue } + + Get-ChildItem -Path $perPlatDepsDir -Directory | ForEach-Object { + $archName = $_.Name + $canonicalArch = $archMap[$archName] + if (-not $canonicalArch) { return } + if (-not ($platformList -contains $archName)) { return } + + $targetArchDir = Join-Path $organizedDepsDir $canonicalArch + Ensure-Directory $targetArchDir + Get-ChildItem -Path $_.FullName -File | ForEach-Object { + $destFile = Join-Path $targetArchDir $_.Name + if (-not (Test-Path $destFile)) { + Copy-Item $_.FullName -Destination $destFile + Write-Host " Copied dependency: $destFile" + } + } + } +} + +# --- Add VCLibs framework packages (not produced by single-project builds) --- + +$vcLibsSdkBase = "${env:ProgramFiles(x86)}\Microsoft SDKs\Windows Kits\10\ExtensionSDKs" +$vcLibsPackages = @( + @{ SdkFolder = "Microsoft.VCLibs"; FileTemplate = "Microsoft.VCLibs.{0}.14.00.appx" }, + @{ SdkFolder = "Microsoft.VCLibs.Desktop"; FileTemplate = "Microsoft.VCLibs.{0}.14.00.Desktop.appx" } +) + +foreach ($platform in $platformList) { + $canonicalArch = $archMap[$platform] + if (-not $canonicalArch) { continue } + + $targetArchDir = Join-Path $organizedDepsDir $canonicalArch + Ensure-Directory $targetArchDir + + foreach ($vcLib in $vcLibsPackages) { + $fileName = $vcLib.FileTemplate -f $canonicalArch + $destPath = Join-Path $targetArchDir $fileName + if (Test-Path $destPath) { continue } + + $sdkPath = Join-Path $vcLibsSdkBase "$($vcLib.SdkFolder)\14.0\Appx\Retail\$platform\$fileName" + if (Test-Path $sdkPath) { + Copy-Item $sdkPath -Destination $destPath + Write-Host " Copied VCLibs from SDK: $destPath" + } else { + Write-Host " Downloading $fileName for $platform..." + try { + Invoke-WebRequest -Uri "https://aka.ms/$fileName" -OutFile $destPath -UseBasicParsing + Write-Host " Downloaded VCLibs: $destPath" + } catch { + Write-Warning " Failed to download $fileName for ${platform}: $_" + } + } + } +} + +# --- Collect symbol files and clean up per-platform build folders --- + +foreach ($msix in $msixFiles) { + Get-ChildItem -Path $msix.DirectoryName -Filter "*.appxsym" -ErrorAction SilentlyContinue | ForEach-Object { + $symPlatform = $_.BaseName -replace '.*_(\w+)$','$1' + $newSymPath = Join-Path $bundleFolderPath "${BundleName}_${Version}_${symPlatform}.appxsym" + Move-Item $_.FullName $newSymPath -Force + Write-Host "Moved symbol file to: $newSymPath" + } +} + +Get-ChildItem -Path $AppxPackageDir -Filter "*.msixupload" -ErrorAction SilentlyContinue | + ForEach-Object { Remove-Item $_.FullName -Force; Write-Host "Removed per-platform upload: $($_.Name)" } + +foreach ($msix in $msixFiles) { + if (Test-Path $msix.DirectoryName) { + Remove-Item $msix.DirectoryName -Recurse -Force + Write-Host "Removed per-platform dir: $($msix.DirectoryName)" + } +} + +# --- Generate .msixupload for Store submissions --- + +if ($BuildMode -eq "StoreUpload") { + $uploadName = "${BundleName}_${Version}_${platforms}_bundle.msixupload" + $uploadPath = Join-Path $AppxPackageDir $uploadName + if (Test-Path $uploadPath) { Remove-Item $uploadPath -Force } + + Write-Host "Creating msixupload at: $uploadPath" + $zipPath = "$uploadPath.zip" + Compress-Archive -Path $organizedBundlePath -DestinationPath $zipPath -Force + Move-Item $zipPath $uploadPath -Force + Write-Host "Successfully created: $uploadPath" +} + +# --- Generate .appinstaller for sideload deployments --- + +if ($AppInstallerUri -ne "" -and ($BuildMode -eq "Sideload" -or $BuildMode -eq "SideloadOnly")) { + $appInstallerPath = Join-Path $AppxPackageDir "$BundleName.appinstaller" + + if ($PackageManifestPath -ne "" -and (Test-Path $PackageManifestPath)) { + [xml]$manifestXml = Get-Content $PackageManifestPath + $packageName = $manifestXml.Package.Identity.Name + $packagePublisher = $manifestXml.Package.Identity.Publisher + } else { + Write-Warning "PackageManifestPath not provided; falling back to BundleName for identity" + $packageName = $BundleName + $packagePublisher = "" + } + + $bundleUri = "${AppInstallerUri}${bundleFolder}/${organizedBundleName}" + + # Build dependency XML entries by reading each package's embedded manifest + $dependencyEntries = "" + if (Test-Path $organizedDepsDir) { + Add-Type -AssemblyName System.IO.Compression.FileSystem -ErrorAction SilentlyContinue + Get-ChildItem -Path $organizedDepsDir -Recurse -Include *.appx, *.msix | ForEach-Object { + $depArch = Split-Path (Split-Path $_.FullName -Parent) -Leaf + + $depZip = [System.IO.Compression.ZipFile]::OpenRead($_.FullName) + try { + $entry = $depZip.Entries | Where-Object { $_.FullName -eq "AppxManifest.xml" } | Select-Object -First 1 + if ($null -eq $entry) { return } + $reader = New-Object System.IO.StreamReader($entry.Open()) + [xml]$depManifest = $reader.ReadToEnd() + $reader.Close() + } finally { $depZip.Dispose() } + + $nsMgr = New-Object System.Xml.XmlNamespaceManager($depManifest.NameTable) + $nsMgr.AddNamespace("pkg", "http://schemas.microsoft.com/appx/manifest/foundation/windows10") + $id = $depManifest.SelectSingleNode("/pkg:Package/pkg:Identity", $nsMgr) + if ($null -eq $id) { return } + + $arch = $id.GetAttribute("ProcessorArchitecture") + if ([string]::IsNullOrEmpty($arch)) { $arch = $depArch } + $depUri = "${AppInstallerUri}${bundleFolder}/Dependencies/$depArch/$($_.Name)" + + $dependencyEntries += @" + + +"@ + } + } + + if ($dependencyEntries -eq "") { + Write-Warning "No dependency packages found" + } else { + Write-Host "Discovered dependencies:$dependencyEntries" + } + + @" + + + + $dependencyEntries + + +"@ | Set-Content -Path $appInstallerPath -Encoding UTF8 + + Write-Host "Created appinstaller at: $appInstallerPath" +} diff --git a/scripts/Generate-SelfCertPfx.ps1 b/.github/scripts/Generate-SelfCertPfx.ps1 similarity index 90% rename from scripts/Generate-SelfCertPfx.ps1 rename to .github/scripts/Generate-SelfCertPfx.ps1 index d794dcdf56c4..ac07d8efc2ed 100644 --- a/scripts/Generate-SelfCertPfx.ps1 +++ b/.github/scripts/Generate-SelfCertPfx.ps1 @@ -1,5 +1,5 @@ -# Copyright (c) 2024 Files Community -# Licensed under the MIT License. See the LICENSE. +# Copyright (c) Files Community +# Licensed under the MIT License. # Abstract: # This script generates a self-signed certificate for the temporary packaging as a pfx file. diff --git a/.github/workflows/cd-preview.yml b/.github/workflows/cd-preview.yml deleted file mode 100644 index df6e9d543a67..000000000000 --- a/.github/workflows/cd-preview.yml +++ /dev/null @@ -1,152 +0,0 @@ -# Copyright (c) 2024 Files Community -# Licensed under the MIT License. See the LICENSE. - -# Abstract: -# Deploys Files Preview (Sideload). -# -# Workflow: -# 1. Configure manifest, logo and secrets -# 2. Restore, build and package Files -# 3. Publish the appinstaller to files.community -# 4. Sign the package -# 5. Publish the package to Azure - -name: Files CD (Preview) - -on: - workflow_dispatch: - -jobs: - build: - runs-on: windows-latest - environment: Deployments - strategy: - fail-fast: false - matrix: - configuration: [Preview] - platform: [x64] - env: - SOLUTION_NAME: 'Files.sln' - CONFIGURATION: '${{ matrix.configuration }}' - PLATFORM: '${{ matrix.platform }}' - APPX_BUNDLE_PLATFORMS: 'x64|arm64' - WORKING_DIR: '${{ github.workspace }}' # D:\a\Files\Files\ - ARTIFACTS_STAGING_DIR: '${{ github.workspace }}\artifacts' - APPX_PACKAGE_DIR: '${{ github.workspace }}\artifacts\AppxPackages' - PACKAGE_PROJECT_DIR: 'src\Files.App (Package)' - PACKAGE_PROJECT_PATH: 'src\Files.App (Package)\Files.Package.wapproj' - PACKAGE_MANIFEST_PATH: 'src\Files.App (Package)\Package.appxmanifest' - TEST_PROJECT_PATH: 'tests\Files.InteractionTests\Files.InteractionTests.csproj' - APP_INSTALLER_SIDELOAD_URL: 'https://cdn.files.community/files/preview/' - - steps: - - name: Checkout the repository - uses: actions/checkout@v4 - - name: Setup MSBuild - uses: microsoft/setup-msbuild@v2 - - name: Setup NuGet - uses: NuGet/setup-nuget@v2 - - name: Setup .NET 8 - uses: actions/setup-dotnet@v4 - with: - dotnet-version: '8.0.x' - - - name: Configure the package manifest, logo, and secrets - shell: pwsh - run: | - . './scripts/Configure-AppxManifest.ps1' ` - -Branch "$env:CONFIGURATION" ` - -PackageManifestPath "$env:PACKAGE_MANIFEST_PATH" ` - -Publisher "$env:SIDELOAD_PUBLISHER_SECRET" ` - -WorkingDir "$env:WORKING_DIR" ` - -SecretBingMapsKey "$env:SECRET_BINGMAPS_KEY" ` - -SecretSentry "$env:SECRET_SENTRY" ` - -SecretGitHubOAuthClientId "$env:SECRET_GITHUB_OAUTH_CLIENT_ID" - env: - SIDELOAD_PUBLISHER_SECRET: ${{ secrets.SIDELOAD_PUBLISHER_SECRET }} - SECRET_BINGMAPS_KEY: ${{ secrets.BING_MAPS_SECRET }} - SECRET_SENTRY: ${{ secrets.SENTRY_SECRET }} - SECRET_GITHUB_OAUTH_CLIENT_ID: ${{ secrets.GH_OAUTH_CLIENT_ID }} - - - name: Use Windows SDK Preview - shell: cmd - run: | - for /f %%a in ('dir /b /a:d %localappdata%\Microsoft\VisualStudio\17*') do echo UsePreviews=True>%localappdata%\Microsoft\VisualStudio\%%a\sdk.txt - - - name: Restore NuGet - shell: pwsh - run: 'nuget restore $env:SOLUTION_NAME' - - - name: Restore Files - shell: pwsh - run: | - msbuild $env:SOLUTION_NAME ` - -t:Restore ` - -p:Platform=$env:PLATFORM ` - -p:Configuration=$env:CONFIGURATION ` - -p:PublishReadyToRun=true - - - name: Build & package Files - shell: pwsh - run: | - msbuild "$env:PACKAGE_PROJECT_PATH" ` - -t:Build ` - -t:_GenerateAppxPackage ` - -p:Platform=$env:PLATFORM ` - -p:Configuration=$env:CONFIGURATION ` - -p:AppxBundlePlatforms=$env:APPX_BUNDLE_PLATFORMS ` - -p:AppxPackageDir="$env:APPX_PACKAGE_DIR" ` - -p:AppxBundle=Always ` - -p:UapAppxPackageBuildMode=Sideload ` - -p:GenerateAppInstallerFile=True ` - -p:AppInstallerUri=$env:APP_INSTALLER_SIDELOAD_URL - - - name: Remove empty files from the packages - shell: bash - run: find $ARTIFACTS_STAGING_DIR -empty -delete - - - name: Update appinstaller schema - run: | - $newSchema = "http://schemas.microsoft.com/appx/appinstaller/2018" - $localFilePath = "${{ env.APPX_PACKAGE_DIR }}/Files.Package.appinstaller" - $fileContent = Get-Content $localFilePath - $fileContent = $fileContent.Replace("http://schemas.microsoft.com/appx/appinstaller/2017/2", $newSchema) - $fileContent | Set-Content $localFilePath - - - name: Sign Files with Azure Trusted Signing - uses: azure/trusted-signing-action@v0.3.20 - with: - azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} - azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} - azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} - endpoint: https://eus.codesigning.azure.net/ - code-signing-account-name: ${{ secrets.SIGNING_ACCOUNT_NAME }} - certificate-profile-name: ${{ secrets.SIGNING_PROFILE_NAME }} - files-folder: ${{ env.APPX_PACKAGE_DIR }} - files-folder-filter: msixbundle - files-folder-recurse: true - files-folder-depth: 4 - file-digest: SHA256 - timestamp-rfc3161: http://timestamp.acs.microsoft.com - timestamp-digest: SHA256 - - - name: Login to Azure - uses: azure/login@v2 - with: - creds: ${{ secrets.AZURE_CREDENTIALS }} - - - name: Upload to Azure blob storage - uses: azure/powershell@v2 - with: - inlineScript: | - az storage blob upload-batch --account-name "filescommunity" --destination "files" --destination-path "preview" --source ${{ env.APPX_PACKAGE_DIR }} --overwrite true - azPSVersion: "latest" - - - name: Logout from Azure - run: 'az logout' - - - name: Upload the packages to GitHub Actions - uses: actions/upload-artifact@v4 - with: - name: 'Appx Packages (${{ env.CONFIGURATION }}, ${{ env.PLATFORM }})' - path: ${{ env.ARTIFACTS_STAGING_DIR }} diff --git a/.github/workflows/cd-sideload-preview.yml b/.github/workflows/cd-sideload-preview.yml new file mode 100644 index 000000000000..cdc8c0ece560 --- /dev/null +++ b/.github/workflows/cd-sideload-preview.yml @@ -0,0 +1,175 @@ +# Copyright (c) Files Community +# Licensed under the MIT License. + +# Abstract: +# Deploys Files Preview (Sideload). +# +# Workflow: +# 1. Configure manifest, logo and secrets +# 2. Restore, build and package Files +# 3. Publish the appinstaller to files.community +# 4. Sign the package +# 5. Publish the package to Azure + +name: Files CD (Sideload Preview) + +on: + workflow_dispatch: + +jobs: + build: + runs-on: windows-2025-vs2026 + environment: Deployments + strategy: + fail-fast: false + matrix: + configuration: [Release] + platform: [x64] + env: + SOLUTION_NAME: 'Files.slnx' + CONFIGURATION: '${{ matrix.configuration }}' + PLATFORM: '${{ matrix.platform }}' + APPX_BUNDLE_PLATFORMS: 'x64|arm64' + WORKING_DIR: '${{ github.workspace }}' # D:\a\Files\Files\ + ARTIFACTS_STAGING_DIR: '${{ github.workspace }}\artifacts' + APPX_PACKAGE_DIR: '${{ github.workspace }}\artifacts\AppxPackages\' + APP_PROJECT_PATH: 'src\Files.App\Files.App.csproj' + PACKAGE_MANIFEST_PATH: 'src\Files.App\Package.appxmanifest' + LAUNCHER_PROJECT_PATH: 'src\Files.App.Launcher\Files.App.Launcher.vcxproj' + TEST_PROJECT_PATH: 'tests\Files.InteractionTests\Files.InteractionTests.csproj' + APP_INSTALLER_SIDELOAD_URL: 'https://cdn.files.community/files/preview/' + + steps: + - name: Checkout the repository + uses: actions/checkout@v4 + - name: Setup MSBuild + uses: microsoft/setup-msbuild@v2 + - name: Setup NuGet + uses: NuGet/setup-nuget@v2 + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + global-json-file: global.json + + - name: Configure the package manifest, logo, and secrets + shell: pwsh + run: | + . './.github/scripts/Configure-AppxManifest.ps1' ` + -Branch "SideloadPreview" ` + -PackageManifestPath "$env:PACKAGE_MANIFEST_PATH" ` + -Publisher "$env:SIDELOAD_PUBLISHER_SECRET" ` + -WorkingDir "$env:WORKING_DIR" ` + -SecretBingMapsKey "$env:SECRET_BINGMAPS_KEY" ` + -SecretSentry "$env:SECRET_SENTRY" ` + -SecretGitHubOAuthClientId "$env:SECRET_GITHUB_OAUTH_CLIENT_ID" + env: + SIDELOAD_PUBLISHER_SECRET: ${{ secrets.SIDELOAD_PUBLISHER_SECRET }} + SECRET_BINGMAPS_KEY: ${{ secrets.BING_MAPS_SECRET }} + SECRET_SENTRY: ${{ secrets.SENTRY_SECRET }} + SECRET_GITHUB_OAUTH_CLIENT_ID: ${{ secrets.GH_OAUTH_CLIENT_ID }} + + - name: Restore Files + shell: pwsh + run: | + msbuild $env:SOLUTION_NAME ` + -t:Restore ` + -p:Platform=$env:PLATFORM ` + -p:Configuration=$env:CONFIGURATION ` + -p:PublishReadyToRun=true ` + -v:quiet + + - name: Restore NuGet Packages for Launcher Project + shell: pwsh + run: | + nuget restore "$env:LAUNCHER_PROJECT_PATH" ` + -SolutionDirectory "$env:WORKING_DIR" ` + -Verbosity detailed + + - name: Build launcher project + shell: pwsh + run: | + msbuild "$env:LAUNCHER_PROJECT_PATH" ` + -t:Build ` + -p:Platform=$env:PLATFORM ` + -p:Configuration=$env:CONFIGURATION ` + -v:quiet + + - name: Sign launcher EXE with Azure Trusted Signing + uses: azure/trusted-signing-action@v0.4.0 + with: + azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} + azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} + azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} + endpoint: https://eus.codesigning.azure.net/ + trusted-signing-account-name: ${{ secrets.SIGNING_ACCOUNT_NAME }} + certificate-profile-name: ${{ secrets.SIGNING_PROFILE_NAME }} + files-folder: ${{ github.workspace }}\src\Files.App\Assets\FilesOpenDialog + files-folder-filter: "*.exe" + files-folder-recurse: false + file-digest: SHA256 + timestamp-rfc3161: http://timestamp.acs.microsoft.com + timestamp-digest: SHA256 + + - name: Build & package Files + shell: pwsh + run: | + $platforms = "$env:APPX_BUNDLE_PLATFORMS" -split '\|' + foreach ($plat in $platforms) { + Write-Host "Building for $plat..." + msbuild "$env:APP_PROJECT_PATH" ` + -t:Build ` + -p:Platform=$plat ` + -p:Configuration=$env:CONFIGURATION ` + -p:AppxPackageDir="$env:APPX_PACKAGE_DIR" ` + -p:AppxBundle=Never ` + -p:GenerateAppxPackageOnBuild=true ` + -p:UapAppxPackageBuildMode=Sideload ` + -v:quiet + } + + - name: Create msixbundle and appinstaller + shell: pwsh + run: | + . './.github/scripts/Create-MsixBundle.ps1' ` + -AppxPackageDir "$env:APPX_PACKAGE_DIR" ` + -BundleName "Files.Package" ` + -PackageManifestPath "$env:PACKAGE_MANIFEST_PATH" ` + -AppInstallerUri "$env:APP_INSTALLER_SIDELOAD_URL" ` + -BuildMode "Sideload" + + - name: Remove empty files from the packages + shell: bash + run: find $ARTIFACTS_STAGING_DIR -empty -delete + + - name: Sign Files with Azure Trusted Signing + uses: azure/trusted-signing-action@v0.4.0 + with: + azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} + azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} + azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} + endpoint: https://eus.codesigning.azure.net/ + trusted-signing-account-name: ${{ secrets.SIGNING_ACCOUNT_NAME }} + certificate-profile-name: ${{ secrets.SIGNING_PROFILE_NAME }} + files-folder: ${{ env.APPX_PACKAGE_DIR }} + files-folder-filter: msixbundle + files-folder-recurse: true + files-folder-depth: 4 + file-digest: SHA256 + timestamp-rfc3161: http://timestamp.acs.microsoft.com + timestamp-digest: SHA256 + + - name: Upload packages to Cloudflare + uses: ryand56/r2-upload-action@latest + with: + r2-account-id: ${{ secrets.R2_ACCOUNT_ID }} + r2-access-key-id: ${{ secrets.R2_ACCESS_KEY_ID }} + r2-secret-access-key: ${{ secrets.R2_SECRET_ACCESS_KEY }} + r2-bucket: ${{ secrets.R2_BUCKET }} + source-dir: ${{ env.APPX_PACKAGE_DIR }} + destination-dir: ./files/preview + + - name: Upload the packages to GitHub Actions + uses: actions/upload-artifact@v4 + with: + name: 'Appx Packages (${{ env.CONFIGURATION }}, ${{ env.PLATFORM }})' + path: ${{ env.ARTIFACTS_STAGING_DIR }} diff --git a/.github/workflows/cd-sideload-stable.yml b/.github/workflows/cd-sideload-stable.yml new file mode 100644 index 000000000000..249b43cc1340 --- /dev/null +++ b/.github/workflows/cd-sideload-stable.yml @@ -0,0 +1,175 @@ +# Copyright (c) Files Community +# Licensed under the MIT License. + +# Abstract: +# Deploys Files (Sideload). +# +# Workflow: +# 1. Configure manifest, logo and secrets +# 2. Restore, build and package Files +# 3. Publish the appinstaller to files.community +# 4. Sign the package +# 5. Publish the package to Azure + +name: Files CD (Sideload Stable) + +on: + workflow_dispatch: + +jobs: + build: + runs-on: windows-2025-vs2026 + environment: Deployments + strategy: + fail-fast: false + matrix: + configuration: [Release] + platform: [x64] + env: + SOLUTION_NAME: 'Files.slnx' + CONFIGURATION: '${{ matrix.configuration }}' + PLATFORM: '${{ matrix.platform }}' + APPX_BUNDLE_PLATFORMS: 'x64|arm64' + WORKING_DIR: '${{ github.workspace }}' # D:\a\Files\Files\ + ARTIFACTS_STAGING_DIR: '${{ github.workspace }}\artifacts' + APPX_PACKAGE_DIR: '${{ github.workspace }}\artifacts\AppxPackages\' + APP_PROJECT_PATH: 'src\Files.App\Files.App.csproj' + PACKAGE_MANIFEST_PATH: 'src\Files.App\Package.appxmanifest' + LAUNCHER_PROJECT_PATH: 'src\Files.App.Launcher\Files.App.Launcher.vcxproj' + TEST_PROJECT_PATH: 'tests\Files.InteractionTests\Files.InteractionTests.csproj' + APP_INSTALLER_SIDELOAD_URL: 'https://cdn.files.community/files/stable/' + + steps: + - name: Checkout the repository + uses: actions/checkout@v4 + - name: Setup MSBuild + uses: microsoft/setup-msbuild@v2 + - name: Setup NuGet + uses: NuGet/setup-nuget@v2 + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + global-json-file: global.json + + - name: Configure the package manifest, logo, and secrets + shell: pwsh + run: | + . './.github/scripts/Configure-AppxManifest.ps1' ` + -Branch "SideloadStable" ` + -PackageManifestPath "$env:PACKAGE_MANIFEST_PATH" ` + -Publisher "$env:SIDELOAD_PUBLISHER_SECRET" ` + -WorkingDir "$env:WORKING_DIR" ` + -SecretBingMapsKey "$env:SECRET_BINGMAPS_KEY" ` + -SecretSentry "$env:SECRET_SENTRY" ` + -SecretGitHubOAuthClientId "$env:SECRET_GITHUB_OAUTH_CLIENT_ID" + env: + SIDELOAD_PUBLISHER_SECRET: ${{ secrets.SIDELOAD_PUBLISHER_SECRET }} + SECRET_BINGMAPS_KEY: ${{ secrets.BING_MAPS_SECRET }} + SECRET_SENTRY: ${{ secrets.SENTRY_SECRET }} + SECRET_GITHUB_OAUTH_CLIENT_ID: ${{ secrets.GH_OAUTH_CLIENT_ID }} + + - name: Restore Files + shell: pwsh + run: | + msbuild $env:SOLUTION_NAME ` + -t:Restore ` + -p:Platform=$env:PLATFORM ` + -p:Configuration=$env:CONFIGURATION ` + -p:PublishReadyToRun=true ` + -v:quiet + + - name: Restore NuGet Packages for Launcher Project + shell: pwsh + run: | + nuget restore "$env:LAUNCHER_PROJECT_PATH" ` + -SolutionDirectory "$env:WORKING_DIR" ` + -Verbosity detailed + + - name: Build launcher project + shell: pwsh + run: | + msbuild "$env:LAUNCHER_PROJECT_PATH" ` + -t:Build ` + -p:Platform=$env:PLATFORM ` + -p:Configuration=$env:CONFIGURATION ` + -v:quiet + + - name: Sign launcher EXE with Azure Trusted Signing + uses: azure/trusted-signing-action@v0.4.0 + with: + azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} + azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} + azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} + endpoint: https://eus.codesigning.azure.net/ + trusted-signing-account-name: ${{ secrets.SIGNING_ACCOUNT_NAME }} + certificate-profile-name: ${{ secrets.SIGNING_PROFILE_NAME }} + files-folder: ${{ github.workspace }}\src\Files.App\Assets\FilesOpenDialog + files-folder-filter: "*.exe" + files-folder-recurse: false + file-digest: SHA256 + timestamp-rfc3161: http://timestamp.acs.microsoft.com + timestamp-digest: SHA256 + + - name: Build & package Files + shell: pwsh + run: | + $platforms = "$env:APPX_BUNDLE_PLATFORMS" -split '\|' + foreach ($plat in $platforms) { + Write-Host "Building for $plat..." + msbuild "$env:APP_PROJECT_PATH" ` + -t:Build ` + -p:Platform=$plat ` + -p:Configuration=$env:CONFIGURATION ` + -p:AppxPackageDir="$env:APPX_PACKAGE_DIR" ` + -p:AppxBundle=Never ` + -p:GenerateAppxPackageOnBuild=true ` + -p:UapAppxPackageBuildMode=Sideload ` + -v:quiet + } + + - name: Create msixbundle and appinstaller + shell: pwsh + run: | + . './.github/scripts/Create-MsixBundle.ps1' ` + -AppxPackageDir "$env:APPX_PACKAGE_DIR" ` + -BundleName "Files.Package" ` + -PackageManifestPath "$env:PACKAGE_MANIFEST_PATH" ` + -AppInstallerUri "$env:APP_INSTALLER_SIDELOAD_URL" ` + -BuildMode "Sideload" + + - name: Remove empty files from the packages + shell: bash + run: find $ARTIFACTS_STAGING_DIR -empty -delete + + - name: Sign Files with Azure Trusted Signing + uses: azure/trusted-signing-action@v0.4.0 + with: + azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} + azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} + azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} + endpoint: https://eus.codesigning.azure.net/ + trusted-signing-account-name: ${{ secrets.SIGNING_ACCOUNT_NAME }} + certificate-profile-name: ${{ secrets.SIGNING_PROFILE_NAME }} + files-folder: ${{ env.APPX_PACKAGE_DIR }} + files-folder-filter: msixbundle + files-folder-recurse: true + files-folder-depth: 4 + file-digest: SHA256 + timestamp-rfc3161: http://timestamp.acs.microsoft.com + timestamp-digest: SHA256 + + - name: Upload packages to Cloudflare + uses: ryand56/r2-upload-action@latest + with: + r2-account-id: ${{ secrets.R2_ACCOUNT_ID }} + r2-access-key-id: ${{ secrets.R2_ACCESS_KEY_ID }} + r2-secret-access-key: ${{ secrets.R2_SECRET_ACCESS_KEY }} + r2-bucket: ${{ secrets.R2_BUCKET }} + source-dir: ${{ env.APPX_PACKAGE_DIR }} + destination-dir: ./files/stable + + - name: Upload the packages to GitHub Actions + uses: actions/upload-artifact@v4 + with: + name: 'Appx Packages (${{ env.CONFIGURATION }}, ${{ env.PLATFORM }})' + path: ${{ env.ARTIFACTS_STAGING_DIR }} diff --git a/.github/workflows/cd-stable.yml b/.github/workflows/cd-stable.yml deleted file mode 100644 index bc27fd356dac..000000000000 --- a/.github/workflows/cd-stable.yml +++ /dev/null @@ -1,152 +0,0 @@ -# Copyright (c) 2024 Files Community -# Licensed under the MIT License. See the LICENSE. - -# Abstract: -# Deploys Files (Sideload). -# -# Workflow: -# 1. Configure manifest, logo and secrets -# 2. Restore, build and package Files -# 3. Publish the appinstaller to files.community -# 4. Sign the package -# 5. Publish the package to Azure - -name: Files CD (Stable) - -on: - workflow_dispatch: - -jobs: - build: - runs-on: windows-latest - environment: Deployments - strategy: - fail-fast: false - matrix: - configuration: [Stable] - platform: [x64] - env: - SOLUTION_NAME: 'Files.sln' - CONFIGURATION: '${{ matrix.configuration }}' - PLATFORM: '${{ matrix.platform }}' - APPX_BUNDLE_PLATFORMS: 'x64|arm64' - WORKING_DIR: '${{ github.workspace }}' # D:\a\Files\Files\ - ARTIFACTS_STAGING_DIR: '${{ github.workspace }}\artifacts' - APPX_PACKAGE_DIR: '${{ github.workspace }}\artifacts\AppxPackages' - PACKAGE_PROJECT_DIR: 'src\Files.App (Package)' - PACKAGE_PROJECT_PATH: 'src\Files.App (Package)\Files.Package.wapproj' - PACKAGE_MANIFEST_PATH: 'src\Files.App (Package)\Package.appxmanifest' - TEST_PROJECT_PATH: 'tests\Files.InteractionTests\Files.InteractionTests.csproj' - APP_INSTALLER_SIDELOAD_URL: 'https://cdn.files.community/files/stable/' - - steps: - - name: Checkout the repository - uses: actions/checkout@v4 - - name: Setup MSBuild - uses: microsoft/setup-msbuild@v2 - - name: Setup NuGet - uses: NuGet/setup-nuget@v2 - - name: Setup .NET 8 - uses: actions/setup-dotnet@v4 - with: - dotnet-version: '8.0.x' - - - name: Configure the package manifest, logo, and secrets - shell: pwsh - run: | - . './scripts/Configure-AppxManifest.ps1' ` - -Branch "$env:CONFIGURATION" ` - -PackageManifestPath "$env:PACKAGE_MANIFEST_PATH" ` - -Publisher "$env:SIDELOAD_PUBLISHER_SECRET" ` - -WorkingDir "$env:WORKING_DIR" ` - -SecretBingMapsKey "$env:SECRET_BINGMAPS_KEY" ` - -SecretSentry "$env:SECRET_SENTRY" ` - -SecretGitHubOAuthClientId "$env:SECRET_GITHUB_OAUTH_CLIENT_ID" - env: - SIDELOAD_PUBLISHER_SECRET: ${{ secrets.SIDELOAD_PUBLISHER_SECRET }} - SECRET_BINGMAPS_KEY: ${{ secrets.BING_MAPS_SECRET }} - SECRET_SENTRY: ${{ secrets.SENTRY_SECRET }} - SECRET_GITHUB_OAUTH_CLIENT_ID: ${{ secrets.GH_OAUTH_CLIENT_ID }} - - - name: Use Windows SDK Preview - shell: cmd - run: | - for /f %%a in ('dir /b /a:d %localappdata%\Microsoft\VisualStudio\17*') do echo UsePreviews=True>%localappdata%\Microsoft\VisualStudio\%%a\sdk.txt - - - name: Restore NuGet - shell: pwsh - run: 'nuget restore $env:SOLUTION_NAME' - - - name: Restore Files - shell: pwsh - run: | - msbuild $env:SOLUTION_NAME ` - -t:Restore ` - -p:Platform=$env:PLATFORM ` - -p:Configuration=$env:CONFIGURATION ` - -p:PublishReadyToRun=true - - - name: Build & package Files - shell: pwsh - run: | - msbuild "$env:PACKAGE_PROJECT_PATH" ` - -t:Build ` - -t:_GenerateAppxPackage ` - -p:Platform=$env:PLATFORM ` - -p:Configuration=$env:CONFIGURATION ` - -p:AppxBundlePlatforms=$env:APPX_BUNDLE_PLATFORMS ` - -p:AppxPackageDir="$env:APPX_PACKAGE_DIR" ` - -p:AppxBundle=Always ` - -p:UapAppxPackageBuildMode=Sideload ` - -p:GenerateAppInstallerFile=True ` - -p:AppInstallerUri=$env:APP_INSTALLER_SIDELOAD_URL - - - name: Remove empty files from the packages - shell: bash - run: find $ARTIFACTS_STAGING_DIR -empty -delete - - - name: Update appinstaller schema - run: | - $newSchema = "http://schemas.microsoft.com/appx/appinstaller/2018" - $localFilePath = "${{ env.APPX_PACKAGE_DIR }}/Files.Package.appinstaller" - $fileContent = Get-Content $localFilePath - $fileContent = $fileContent.Replace("http://schemas.microsoft.com/appx/appinstaller/2017/2", $newSchema) - $fileContent | Set-Content $localFilePath - - - name: Sign Files with Azure Trusted Signing - uses: azure/trusted-signing-action@v0.3.20 - with: - azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} - azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} - azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} - endpoint: https://eus.codesigning.azure.net/ - code-signing-account-name: ${{ secrets.SIGNING_ACCOUNT_NAME }} - certificate-profile-name: ${{ secrets.SIGNING_PROFILE_NAME }} - files-folder: ${{ env.APPX_PACKAGE_DIR }} - files-folder-filter: msixbundle - files-folder-recurse: true - files-folder-depth: 4 - file-digest: SHA256 - timestamp-rfc3161: http://timestamp.acs.microsoft.com - timestamp-digest: SHA256 - - - name: Login to Azure - uses: azure/login@v2 - with: - creds: ${{ secrets.AZURE_CREDENTIALS }} - - - name: Upload to Azure blob storage - uses: azure/powershell@v2 - with: - inlineScript: | - az storage blob upload-batch --account-name "filescommunity" --destination "files" --destination-path "stable" --source ${{ env.APPX_PACKAGE_DIR }} --overwrite true - azPSVersion: "latest" - - - name: Logout from Azure - run: 'az logout' - - - name: Upload the packages to GitHub Actions - uses: actions/upload-artifact@v4 - with: - name: 'Appx Packages (${{ env.CONFIGURATION }}, ${{ env.PLATFORM }})' - path: ${{ env.ARTIFACTS_STAGING_DIR }} diff --git a/.github/workflows/cd-store-preview.yml b/.github/workflows/cd-store-preview.yml new file mode 100644 index 000000000000..e89a8760ea48 --- /dev/null +++ b/.github/workflows/cd-store-preview.yml @@ -0,0 +1,143 @@ +# Copyright (c) Files Community +# Licensed under the MIT License. + +# Abstract: +# Deploys Files Preview (Store). +# +# Workflow: +# 1. Configure manifest, logo and secrets +# 2. Restore, build and package Files +# 3. Generate a msixupload file +# 4. Publish the msixupload to GitHub Actions + +name: Files CD (Store Preview) + +on: + workflow_dispatch: + +jobs: + build: + runs-on: windows-2025-vs2026 + environment: Deployments + strategy: + fail-fast: false + matrix: + configuration: [Release] + platform: [x64] + env: + SOLUTION_NAME: 'Files.slnx' + CONFIGURATION: '${{ matrix.configuration }}' + PLATFORM: '${{ matrix.platform }}' + APPX_BUNDLE_PLATFORMS: 'x64|arm64' + WORKING_DIR: '${{ github.workspace }}' # D:\a\Files\Files\ + ARTIFACTS_STAGING_DIR: '${{ github.workspace }}\artifacts' + APPX_PACKAGE_DIR: '${{ github.workspace }}\artifacts\AppxPackages\' + APP_PROJECT_PATH: '${{ github.workspace }}\src\Files.App\Files.App.csproj' + PACKAGE_MANIFEST_PATH: '${{ github.workspace }}\src\Files.App\Package.appxmanifest' + LAUNCHER_PROJECT_PATH: 'src\Files.App.Launcher\Files.App.Launcher.vcxproj' + + steps: + - name: Checkout the repository + uses: actions/checkout@v4 + - name: Setup MSBuild + uses: microsoft/setup-msbuild@v2 + - name: Setup NuGet + uses: NuGet/setup-nuget@v2 + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + global-json-file: global.json + + - name: Configure the package manifest, logo, and secrets + shell: pwsh + run: | + . './.github/scripts/Configure-AppxManifest.ps1' ` + -Branch "StorePreview" ` + -PackageManifestPath "$env:PACKAGE_MANIFEST_PATH" ` + -Publisher "$env:STORE_PUBLISHER_SECRET" ` + -WorkingDir "$env:WORKING_DIR" ` + -SecretBingMapsKey "$env:SECRET_BINGMAPS_KEY" ` + -SecretSentry "$env:SECRET_SENTRY" ` + -SecretGitHubOAuthClientId "$env:SECRET_GITHUB_OAUTH_CLIENT_ID" + env: + STORE_PUBLISHER_SECRET: ${{ secrets.STORE_PUBLISHER_SECRET }} + SECRET_BINGMAPS_KEY: ${{ secrets.BING_MAPS_SECRET }} + SECRET_SENTRY: ${{ secrets.SENTRY_SECRET }} + SECRET_GITHUB_OAUTH_CLIENT_ID: ${{ secrets.GH_OAUTH_CLIENT_ID }} + + - name: Restore Files + shell: pwsh + run: | + msbuild $env:SOLUTION_NAME ` + -t:Restore ` + -p:Platform=$env:PLATFORM ` + -p:Configuration=$env:CONFIGURATION ` + -p:PublishReadyToRun=true ` + -v:quiet + + - name: Restore NuGet Packages for Launcher Project + shell: pwsh + run: | + nuget restore "$env:LAUNCHER_PROJECT_PATH" ` + -SolutionDirectory "$env:WORKING_DIR" ` + -Verbosity detailed + + - name: Build launcher project + shell: pwsh + run: | + msbuild "$env:LAUNCHER_PROJECT_PATH" ` + -t:Build ` + -p:Platform=$env:PLATFORM ` + -p:Configuration=$env:CONFIGURATION ` + -v:quiet + + - name: Sign launcher EXE with Azure Trusted Signing + uses: azure/trusted-signing-action@v0.4.0 + with: + azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} + azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} + azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} + endpoint: https://eus.codesigning.azure.net/ + trusted-signing-account-name: ${{ secrets.SIGNING_ACCOUNT_NAME }} + certificate-profile-name: ${{ secrets.SIGNING_PROFILE_NAME }} + files-folder: ${{ github.workspace }}\src\Files.App\Assets\FilesOpenDialog + files-folder-filter: "*.exe" + files-folder-recurse: false + file-digest: SHA256 + timestamp-rfc3161: http://timestamp.acs.microsoft.com + timestamp-digest: SHA256 + + - name: Build & package Files + shell: pwsh + run: | + $platforms = "$env:APPX_BUNDLE_PLATFORMS" -split '\|' + foreach ($plat in $platforms) { + Write-Host "Building for $plat..." + msbuild "$env:APP_PROJECT_PATH" ` + -t:Build ` + -p:Platform=$plat ` + -p:Configuration=$env:CONFIGURATION ` + -p:AppxPackageDir="$env:APPX_PACKAGE_DIR" ` + -p:AppxBundle=Never ` + -p:GenerateAppxPackageOnBuild=true ` + -p:UapAppxPackageBuildMode=StoreUpload ` + -v:quiet + } + + - name: Create msixbundle and msixupload + shell: pwsh + run: | + . './.github/scripts/Create-MsixBundle.ps1' ` + -AppxPackageDir "$env:APPX_PACKAGE_DIR" ` + -BundleName "Files.Package" ` + -BuildMode "StoreUpload" + + - name: Remove empty files from the packages + shell: bash + run: find $ARTIFACTS_STAGING_DIR -empty -delete + + - name: Upload the packages to GitHub Actions + uses: actions/upload-artifact@v4 + with: + name: 'Appx Packages (${{ env.CONFIGURATION }}, ${{ env.PLATFORM }})' + path: ${{ env.ARTIFACTS_STAGING_DIR }} diff --git a/.github/workflows/cd-store-stable.yml b/.github/workflows/cd-store-stable.yml new file mode 100644 index 000000000000..95eb3be2be26 --- /dev/null +++ b/.github/workflows/cd-store-stable.yml @@ -0,0 +1,143 @@ +# Copyright (c) Files Community +# Licensed under the MIT License. + +# Abstract: +# Deploys Files (Store). +# +# Workflow: +# 1. Configure manifest, logo and secrets +# 2. Restore, build and package Files +# 3. Generate a msixupload file +# 4. Publish the msixupload to GitHub Actions + +name: Files CD (Store Stable) + +on: + workflow_dispatch: + +jobs: + build: + runs-on: windows-2025-vs2026 + environment: Deployments + strategy: + fail-fast: false + matrix: + configuration: [Release] + platform: [x64] + env: + SOLUTION_NAME: 'Files.slnx' + CONFIGURATION: '${{ matrix.configuration }}' + PLATFORM: '${{ matrix.platform }}' + APPX_BUNDLE_PLATFORMS: 'x64|arm64' + WORKING_DIR: '${{ github.workspace }}' # D:\a\Files\Files\ + ARTIFACTS_STAGING_DIR: '${{ github.workspace }}\artifacts' + APPX_PACKAGE_DIR: '${{ github.workspace }}\artifacts\AppxPackages\' + APP_PROJECT_PATH: '${{ github.workspace }}\src\Files.App\Files.App.csproj' + PACKAGE_MANIFEST_PATH: '${{ github.workspace }}\src\Files.App\Package.appxmanifest' + LAUNCHER_PROJECT_PATH: 'src\Files.App.Launcher\Files.App.Launcher.vcxproj' + + steps: + - name: Checkout the repository + uses: actions/checkout@v4 + - name: Setup MSBuild + uses: microsoft/setup-msbuild@v2 + - name: Setup NuGet + uses: NuGet/setup-nuget@v2 + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + global-json-file: global.json + + - name: Configure the package manifest, logo, and secrets + shell: pwsh + run: | + . './.github/scripts/Configure-AppxManifest.ps1' ` + -Branch "StoreStable" ` + -PackageManifestPath "$env:PACKAGE_MANIFEST_PATH" ` + -Publisher "$env:STORE_PUBLISHER_SECRET" ` + -WorkingDir "$env:WORKING_DIR" ` + -SecretBingMapsKey "$env:SECRET_BINGMAPS_KEY" ` + -SecretSentry "$env:SECRET_SENTRY" ` + -SecretGitHubOAuthClientId "$env:SECRET_GITHUB_OAUTH_CLIENT_ID" + env: + STORE_PUBLISHER_SECRET: ${{ secrets.STORE_PUBLISHER_SECRET }} + SECRET_BINGMAPS_KEY: ${{ secrets.BING_MAPS_SECRET }} + SECRET_SENTRY: ${{ secrets.SENTRY_SECRET }} + SECRET_GITHUB_OAUTH_CLIENT_ID: ${{ secrets.GH_OAUTH_CLIENT_ID }} + + - name: Restore Files + shell: pwsh + run: | + msbuild $env:SOLUTION_NAME ` + -t:Restore ` + -p:Platform=$env:PLATFORM ` + -p:Configuration=$env:CONFIGURATION ` + -p:PublishReadyToRun=true ` + -v:quiet + + - name: Restore NuGet Packages for Launcher Project + shell: pwsh + run: | + nuget restore "$env:LAUNCHER_PROJECT_PATH" ` + -SolutionDirectory "$env:WORKING_DIR" ` + -Verbosity detailed + + - name: Build launcher project + shell: pwsh + run: | + msbuild "$env:LAUNCHER_PROJECT_PATH" ` + -t:Build ` + -p:Platform=$env:PLATFORM ` + -p:Configuration=$env:CONFIGURATION ` + -v:quiet + + - name: Sign launcher EXE with Azure Trusted Signing + uses: azure/trusted-signing-action@v0.4.0 + with: + azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} + azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} + azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} + endpoint: https://eus.codesigning.azure.net/ + trusted-signing-account-name: ${{ secrets.SIGNING_ACCOUNT_NAME }} + certificate-profile-name: ${{ secrets.SIGNING_PROFILE_NAME }} + files-folder: ${{ github.workspace }}\src\Files.App\Assets\FilesOpenDialog + files-folder-filter: "*.exe" + files-folder-recurse: false + file-digest: SHA256 + timestamp-rfc3161: http://timestamp.acs.microsoft.com + timestamp-digest: SHA256 + + - name: Build & package Files + shell: pwsh + run: | + $platforms = "$env:APPX_BUNDLE_PLATFORMS" -split '\|' + foreach ($plat in $platforms) { + Write-Host "Building for $plat..." + msbuild "$env:APP_PROJECT_PATH" ` + -t:Build ` + -p:Platform=$plat ` + -p:Configuration=$env:CONFIGURATION ` + -p:AppxPackageDir="$env:APPX_PACKAGE_DIR" ` + -p:AppxBundle=Never ` + -p:GenerateAppxPackageOnBuild=true ` + -p:UapAppxPackageBuildMode=StoreUpload ` + -v:quiet + } + + - name: Create msixbundle and msixupload + shell: pwsh + run: | + . './.github/scripts/Create-MsixBundle.ps1' ` + -AppxPackageDir "$env:APPX_PACKAGE_DIR" ` + -BundleName "Files.Package" ` + -BuildMode "StoreUpload" + + - name: Remove empty files from the packages + shell: bash + run: find $ARTIFACTS_STAGING_DIR -empty -delete + + - name: Upload the packages to GitHub Actions + uses: actions/upload-artifact@v4 + with: + name: 'Appx Packages (${{ env.CONFIGURATION }}, ${{ env.PLATFORM }})' + path: ${{ env.ARTIFACTS_STAGING_DIR }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fbf366d9f1f7..dc5a92a632f8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,5 @@ -# Copyright (c) 2024 Files Community -# Licensed under the MIT License. See the LICENSE. +# Copyright (c) Files Community +# Licensed under the MIT License. # Abstract: # This CI is executed when a new commit is created on the main branch or @@ -29,16 +29,15 @@ run-name: ${{ github.event_name == 'pull_request' && 'Files PR Validation' || 'F env: WORKING_DIR: ${{ github.workspace }} # Default: 'D:\a\Files\Files' - SOLUTION_PATH: '${{ github.workspace }}\Files.sln' - PACKAGE_PROJECT_DIR: '${{ github.workspace }}\src\Files.App (Package)' - PACKAGE_PROJECT_PATH: '${{ github.workspace }}\src\Files.App (Package)\Files.Package.wapproj' + SOLUTION_PATH: '${{ github.workspace }}\Files.slnx' + APP_PROJECT_PATH: '${{ github.workspace }}\src\Files.App\Files.App.csproj' AUTOMATED_TESTS_ARCHITECTURE: 'x64' AUTOMATED_TESTS_CONFIGURATION: 'Release' AUTOMATED_TESTS_PROJECT_DIR: '${{ github.workspace }}\tests\Files.InteractionTests' AUTOMATED_TESTS_PROJECT_PATH: '${{ github.workspace }}\tests\Files.InteractionTests\Files.InteractionTests.csproj' AUTOMATED_TESTS_ASSEMBLY_DIR: '${{ github.workspace }}\artifacts\TestsAssembly' ARTIFACTS_STAGING_DIR: '${{ github.workspace }}\artifacts' - APPX_PACKAGE_DIR: '${{ github.workspace }}\artifacts\AppxPackages' + APPX_PACKAGE_DIR: '${{ github.workspace }}\artifacts\AppxPackages\' APPX_SELFSIGNED_CERT_PATH: '${{ github.workspace }}\.github\workflows\FilesApp_SelfSigned.pfx' WINAPPDRIVER_EXE86_PATH: 'C:\Program Files (x86)\Windows Application Driver\WinAppDriver.exe' WINAPPDRIVER_EXE64_PATH: 'C:\Program Files\Windows Application Driver\WinAppDriver.exe' @@ -49,10 +48,7 @@ jobs: if: github.repository_owner == 'files-community' - runs-on: ubuntu-latest - defaults: - run: - shell: pwsh + runs-on: windows-2025-vs2026 steps: @@ -60,8 +56,10 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 2 - - - name: Install XamlStyler console + - name: Setup .NET + uses: actions/setup-dotnet@v4 + + - name: Install XamlStyler.Console run: 'dotnet tool install --global XamlStyler.Console' - name: Check XAML formatting @@ -86,7 +84,7 @@ jobs: if: github.repository_owner == 'files-community' - runs-on: windows-latest + runs-on: windows-2025-vs2026 strategy: fail-fast: false matrix: @@ -104,14 +102,10 @@ jobs: uses: microsoft/setup-msbuild@v2 - name: Setup NuGet uses: NuGet/setup-nuget@v2 - - name: Setup .NET 8 + - name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: '8.0.x' - - - name: Restore NuGet - shell: pwsh - run: 'nuget restore $env:SOLUTION_PATH' + global-json-file: global.json - name: Restore Files shell: pwsh @@ -120,50 +114,52 @@ jobs: -t:Restore ` -p:Platform=$env:ARCHITECTURE ` -p:Configuration=$env:CONFIGURATION ` - -p:PublishReadyToRun=true + -p:PublishReadyToRun=true ` + -v:quiet - if: env.CONFIGURATION != env.AUTOMATED_TESTS_CONFIGURATION || env.ARCHITECTURE != env.AUTOMATED_TESTS_ARCHITECTURE name: Build Files run: | msbuild ` - $env:PACKAGE_PROJECT_PATH ` + $env:APP_PROJECT_PATH ` -t:Build ` - -clp:ErrorsOnly ` -p:Configuration=$env:CONFIGURATION ` -p:Platform=$env:ARCHITECTURE ` - -p:AppxBundle=Never + -p:AppxBundle=Never ` + -p:GenerateAppxPackageOnBuild=false ` + -v:quiet - if: env.CONFIGURATION == env.AUTOMATED_TESTS_CONFIGURATION && env.ARCHITECTURE == env.AUTOMATED_TESTS_ARCHITECTURE name: Create self signed cert as a pfx file - run: ./scripts/Generate-SelfCertPfx.ps1 -Destination "$env:APPX_SELFSIGNED_CERT_PATH" + run: ./.github/scripts/Generate-SelfCertPfx.ps1 -Destination "$env:APPX_SELFSIGNED_CERT_PATH" - if: env.CONFIGURATION == env.AUTOMATED_TESTS_CONFIGURATION && env.ARCHITECTURE == env.AUTOMATED_TESTS_ARCHITECTURE name: Build & package Files run: | msbuild ` - $env:PACKAGE_PROJECT_PATH ` + $env:APP_PROJECT_PATH ` -t:Build ` - -t:_GenerateAppxPackage ` - -clp:ErrorsOnly ` -p:Configuration=$env:CONFIGURATION ` -p:Platform=$env:ARCHITECTURE ` -p:AppxBundlePlatforms=$env:AUTOMATED_TESTS_ARCHITECTURE ` -p:AppxBundle=Always ` + -p:GenerateAppxPackageOnBuild=true ` -p:UapAppxPackageBuildMode=SideloadOnly ` -p:AppxPackageDir=$env:APPX_PACKAGE_DIR ` -p:AppxPackageSigningEnabled=true ` -p:PackageCertificateKeyFile=$env:APPX_SELFSIGNED_CERT_PATH ` -p:PackageCertificatePassword="" ` - -p:PackageCertificateThumbprint="" + -p:PackageCertificateThumbprint="" ` + -v:quiet - if: env.ARCHITECTURE == env.AUTOMATED_TESTS_ARCHITECTURE && env.CONFIGURATION == env.AUTOMATED_TESTS_CONFIGURATION name: Build interaction tests run: | msbuild $env:AUTOMATED_TESTS_PROJECT_PATH ` -t:Build ` - -clp:ErrorsOnly ` -p:Configuration=$env:CONFIGURATION ` - -p:Platform=$env:AUTOMATED_TESTS_ARCHITECTURE + -p:Platform=$env:AUTOMATED_TESTS_ARCHITECTURE ` + -v:quiet - if: env.ARCHITECTURE == env.AUTOMATED_TESTS_ARCHITECTURE && env.CONFIGURATION == env.AUTOMATED_TESTS_CONFIGURATION name: Copy tests bin to the artifacts dir @@ -172,6 +168,8 @@ jobs: Copy-Item ` -Path "$env:AUTOMATED_TESTS_PROJECT_DIR\bin" ` -Destination "$env:AUTOMATED_TESTS_ASSEMBLY_DIR" -Recurse + # Copy the self-signed cert so the test job can trust it + Copy-Item -Path "$env:APPX_SELFSIGNED_CERT_PATH" -Destination "$env:ARTIFACTS_STAGING_DIR" - if: env.ARCHITECTURE == env.AUTOMATED_TESTS_ARCHITECTURE && env.CONFIGURATION == env.AUTOMATED_TESTS_CONFIGURATION name: Upload the packages to the Artifacts @@ -185,7 +183,7 @@ jobs: if: github.repository_owner == 'files-community' && always() needs: [build] - runs-on: windows-latest + runs-on: windows-2025-vs2026 strategy: fail-fast: false matrix: @@ -205,6 +203,10 @@ jobs: - name: Checkout the repository uses: actions/checkout@v4 + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + global-json-file: global.json - name: Download the packages from the Artifacts uses: actions/download-artifact@v4 @@ -215,11 +217,30 @@ jobs: - name: Install Files shell: powershell run: | - Set-Location "$env:APPX_PACKAGE_DIR" - $AppxPackageBundleDir = Get-ChildItem -Filter Files.Package_*_Test -Name - Set-Location $AppxPackageBundleDir - ./Install.ps1 -Force - Get-AppxPackage + # Trust the self-signed certificate + Import-PfxCertificate ` + -FilePath (Join-Path $env:ARTIFACTS_STAGING_DIR "FilesApp_SelfSigned.pfx") ` + -CertStoreLocation Cert:\LocalMachine\Root ` + -Password (New-Object System.Security.SecureString) + + # Install Windows App Runtime (derive major.minor from Directory.Packages.props) + [xml]$pkgProps = Get-Content "$env:WORKING_DIR\Directory.Packages.props" + $sdkVer = ($pkgProps.Project.ItemGroup.PackageVersion | Where-Object Include -eq 'Microsoft.WindowsAppSDK').Version + $majorMinor = ($sdkVer -split '\.')[0..1] -join '.' + $installer = Join-Path $env:TEMP "windowsappruntimeinstall-x64.exe" + Invoke-WebRequest -Uri "https://aka.ms/windowsappsdk/$majorMinor/latest/windowsappruntimeinstall-x64.exe" -OutFile $installer + Start-Process $installer -ArgumentList "--quiet","--force" -Wait -NoNewWindow + + # Install the app package (with any sideload dependencies) + $pkg = Get-ChildItem $env:APPX_PACKAGE_DIR -Recurse -Include *.msixbundle, *.msix | + Where-Object { $_.DirectoryName -notmatch '\\Dependencies\\' } | Select-Object -First 1 + $deps = @(Get-ChildItem $env:APPX_PACKAGE_DIR -Recurse -Filter *.appx | + Where-Object { $_.DirectoryName -match '\\Dependencies\\' } | ForEach-Object { $_.FullName }) + if ($deps.Count -gt 0) { + Add-AppxPackage -Path $pkg.FullName -DependencyPath $deps + } else { + Add-AppxPackage -Path $pkg.FullName + } - name: Set full HD resolution run: Set-DisplayResolution -Width 1920 -Height 1080 -Force @@ -228,11 +249,25 @@ jobs: shell: pwsh run: Start-Process -FilePath "$env:WINAPPDRIVER_EXE86_PATH" + # Retry integration tests if first attempt fails - name: Run interaction tests - run: | - dotnet test ` - $env:AUTOMATED_TESTS_ASSEMBLY_DIR\**\Files.InteractionTests.dll ` - --logger "trx;LogFileName=$env:AUTOMATED_TESTS_ASSEMBLY_DIR\testResults.trx" + uses: nick-fields/retry@v3 + with: + timeout_minutes: 15 + max_attempts: 2 + shell: pwsh + command: | + dotnet test ` + --test-modules **\Files.InteractionTests.dll ` + --root-directory $env:AUTOMATED_TESTS_ASSEMBLY_DIR ` + --results-directory $env:AUTOMATED_TESTS_ASSEMBLY_DIR ` + --report-trx ` + --report-trx-filename testResults.trx + + - if: github.event_name == 'pull_request' + uses: geekyeggo/delete-artifact@v5 + with: + name: '*' # - name: Generate markdown from the tests result # shell: pwsh diff --git a/.github/workflows/deploy-stable-legacy.yml b/.github/workflows/deploy-stable-legacy.yml deleted file mode 100644 index b84b8ccfe3c9..000000000000 --- a/.github/workflows/deploy-stable-legacy.yml +++ /dev/null @@ -1,179 +0,0 @@ -name: Deploy Stable Pipeline - -on: - workflow_dispatch: - -jobs: - build: - runs-on: windows-latest - environment: Deployments - strategy: - fail-fast: false - matrix: - configuration: [Stable] - platform: [x64] - env: - SOLUTION_NAME: 'Files.sln' - PACKAGE_PROJECT_DIR: 'src\Files.App (Package)' - PACKAGE_PROJECT_PATH: 'src\Files.App (Package)\Files.Package.wapproj' - TEST_PROJECT_PATH: 'tests\Files.InteractionTests\Files.InteractionTests.csproj' - CONFIGURATION: ${{ matrix.configuration }} - PLATFORM: ${{ matrix.platform }} - APPX_BUNDLE_PLATFORMS: 'x64|arm64' - WORKING_DIR: ${{ github.workspace }} # Default: D:\a\Files\Files\ - ARTIFACTS_STAGING_DIR: ${{ github.workspace }}\artifacts - APPX_PACKAGE_DIR: ${{ github.workspace }}\artifacts\AppxPackages - - steps: - - name: Checkout the repository - uses: actions/checkout@v4 - - - name: Setup MSBuild - uses: microsoft/setup-msbuild@v1 - - - name: Setup NuGet - uses: NuGet/setup-nuget@v1.1.1 - - - name: Setup .NET 8 - uses: actions/setup-dotnet@v3 - with: - dotnet-version: '8.0.x' - - # TODO: Move the command to PowerShell script instead - - name: Update Package.appxmanifest - shell: pwsh - run: | - [xml]$xmlDoc = Get-Content "$env:PACKAGE_PROJECT_DIR\Package.appxmanifest" - $xmlDoc.Package.Identity.Name="Files" - $xmlDoc.Package.Identity.Publisher="$env:SIDELOAD_PUBLISHER_SECRET" - $xmlDoc.Package.Properties.DisplayName="Files" - $xmlDoc.Package.Applications.Application.VisualElements.DisplayName="Files" - $xmlDoc.Save("$env:PACKAGE_PROJECT_DIR\Package.appxmanifest") - env: - SIDELOAD_PUBLISHER_SECRET: ${{ secrets.SIDELOAD_PUBLISHER_SECRET }} - - # TODO: Move the command to PowerShell script instead - - name: Use the ${{ env.CONFIGURATION }} logo sets - shell: pwsh - run: | - Get-ChildItem "$env:WORKING_DIR\src" -Include *.csproj, *.appxmanifest, *.wapproj, *.xaml -recurse | ForEach -Process ` - { ` - (Get-Content $_ -Raw | ForEach -Process { $_ -replace "Assets\\AppTiles\\Dev", "Assets\AppTiles\Release" }) | ` - Set-Content $_ -NoNewline ` - } - - - name: Inject the Bing Maps API token - shell: pwsh - run: | - Get-ChildItem "$env:WORKING_DIR\src" -Include *.cs -recurse | ForEach-Object -Process ` - { ` - (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "bingmapskey.secret", "$env:BING_MAPS_SECRET" }) | ` - Set-Content $_ -NoNewline ` - } - env: - BING_MAPS_SECRET: ${{ secrets.BING_MAPS_SECRET }} - - - name: Inject the Sentry token - shell: pwsh - run: | - Get-ChildItem "$env:WORKING_DIR\src" -Include *.cs -recurse | ForEach-Object -Process ` - { ` - (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "sentry.secret", "$env:SENTRY_SECRET" }) | ` - Set-Content $_ -NoNewline ` - } - env: - SENTRY_SECRET: ${{ secrets.SENTRY_SECRET }} - - - name: Inject the GitHub OAuth client ID - run: | - Get-ChildItem "$env:WORKING_DIR\src" -Include *.cs -recurse | ForEach-Object -Process ` - { ` - (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "githubclientid.secret", "$env:GH_OAUTH_CLIENT_ID" }) | ` - Set-Content $_ -NoNewline ` - } - env: - GH_OAUTH_CLIENT_ID: ${{ secrets.GH_OAUTH_CLIENT_ID }} - - - name: Use Windows SDK Preview - shell: cmd - run: | - for /f %%a in ('dir /b /a:d %localappdata%\Microsoft\VisualStudio\17*') do echo UsePreviews=True>%localappdata%\Microsoft\VisualStudio\%%a\sdk.txt - - - name: Restore NuGet - shell: pwsh - run: 'nuget restore $env:SOLUTION_NAME' - - - name: Restore ${{ env.SOLUTION_NAME }} - shell: pwsh - run: | - msbuild $env:SOLUTION_NAME ` - -t:Restore ` - -p:Platform=$env:PLATFORM ` - -p:Configuration=$env:CONFIGURATION ` - -p:PublishReadyToRun=true - - - name: Build ${{ env.SOLUTION_NAME }} - shell: pwsh - run: | - msbuild "$env:PACKAGE_PROJECT_PATH" ` - -t:Build ` - -t:_GenerateAppxPackage ` - -p:Platform=$env:PLATFORM ` - -p:Configuration=$env:CONFIGURATION ` - -p:AppxBundlePlatforms=$env:APPX_BUNDLE_PLATFORMS ` - -p:AppxPackageDir="$env:APPX_PACKAGE_DIR" ` - -p:AppxBundle=Always ` - -p:UapAppxPackageBuildMode=Sideload ` - -p:GenerateAppInstallerFile=True ` - -p:AppInstallerUri=https://cdn.files.community/files/stable/ - - - name: Remove empty files from the packages - shell: bash - run: find $ARTIFACTS_STAGING_DIR -empty -delete - - - name: Update appinstaller schema - run: | - $newSchema = "http://schemas.microsoft.com/appx/appinstaller/2018" - $localFilePath = "${{ env.APPX_PACKAGE_DIR }}/Files.Package.appinstaller" - $fileContent = Get-Content $localFilePath - $fileContent = $fileContent.Replace("http://schemas.microsoft.com/appx/appinstaller/2017/2", $newSchema) - $fileContent | Set-Content $localFilePath - - - name: Sign files with Azure Trusted Signing - uses: azure/trusted-signing-action@v0.3.16 - with: - azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} - azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} - azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} - endpoint: https://eus.codesigning.azure.net/ - code-signing-account-name: ${{ secrets.SIGNING_ACCOUNT_NAME }} - certificate-profile-name: ${{ secrets.SIGNING_PROFILE_NAME }} - files-folder: ${{ env.APPX_PACKAGE_DIR }} - files-folder-filter: msixbundle - files-folder-recurse: true - files-folder-depth: 4 - file-digest: SHA256 - timestamp-rfc3161: http://timestamp.acs.microsoft.com - timestamp-digest: SHA256 - - - uses: azure/login@v1 - with: - creds: ${{ secrets.AZURE_CREDENTIALS }} - - - name: Upload to blob storage - uses: azure/powershell@v1 - with: - inlineScript: | - az storage blob upload-batch --account-name "filescommunity" --destination "files" --destination-path "stable" --source ${{ env.APPX_PACKAGE_DIR }} --overwrite true - azPSVersion: "latest" - - # Azure logout - - name: logout - run: | - az logout - - - name: Upload the packages to GitHub Actions - uses: actions/upload-artifact@v3 - with: - name: 'Appx Packages (${{ env.CONFIGURATION }}, ${{ env.PLATFORM }})' - path: ${{ env.ARTIFACTS_STAGING_DIR }} diff --git a/.github/workflows/format-xaml.yml b/.github/workflows/format-xaml.yml index f84fd5348a5a..43eee9729135 100644 --- a/.github/workflows/format-xaml.yml +++ b/.github/workflows/format-xaml.yml @@ -6,11 +6,8 @@ on: jobs: format-xaml: if: github.event.issue.pull_request && github.event.comment.body == '/format' - runs-on: ubuntu-latest + runs-on: windows-2025-vs2026 environment: Pull Requests - defaults: - run: - shell: pwsh permissions: contents: write @@ -46,6 +43,9 @@ jobs: - uses: actions/checkout@v4 if: env.CAN_RUN == 1 + - name: Setup .NET + uses: actions/setup-dotnet@v4 + - name: Set git identity if: env.CAN_RUN == 1 run: | @@ -74,13 +74,16 @@ jobs: "CAN_RUN=0" | Out-File -FilePath $env:GITHUB_ENV -Append } - - name: Install Xaml Styler + - name: Install XamlStyler.Console if: env.CAN_RUN == 1 run: dotnet tool install --global XamlStyler.Console - name: Format XAML files if: env.CAN_RUN == 1 - run: xstyler -l None -r -d src/Files.App + run: | + xstyler -l None -r -d src/Files.App + xstyler -l None -r -d src/Files.App.Controls + xstyler -l None -r -d tests/Files.App.UITests - name: Commit formatted files if: env.CAN_RUN == 1 diff --git a/.gitignore b/.gitignore index 60421ddadba9..fab72c112dd9 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ Files.App.SaveDialog*.dll Files.App.Launcher.exe # User-specific files +*.rsuser *.suo *.user *.userosscache @@ -17,6 +18,9 @@ Files.App.Launcher.exe # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs +# Mono auto generated files +mono_crash.* + # Build results [Dd]ebug/ [Dd]ebugPublic/ @@ -24,10 +28,14 @@ Files.App.Launcher.exe [Rr]eleases/ x64/ x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ +[Ll]ogs/ # Visual Studio 2015/2017 cache/options directory .vs/ @@ -41,9 +49,10 @@ Generated\ Files/ [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* -# NUNIT +# NUnit *.VisualState.xml TestResult.xml +nunit-*.xml # Build Results of an ATL Project [Dd]ebugPS/ @@ -57,6 +66,10 @@ BenchmarkDotNet.Artifacts/ project.lock.json project.fragment.lock.json artifacts/ +**/Properties/launchSettings.json + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt # StyleCop StyleCopReport.xml @@ -65,6 +78,7 @@ StyleCopReport.xml *_i.c *_p.c *_i.h +*_h.h *.ilk *.meta *.obj @@ -81,7 +95,9 @@ StyleCopReport.xml *.tlh *.tmp *.tmp_proj +*_wpftmp.csproj *.log +*.tlog *.vspscc *.vssscc .builds @@ -136,6 +152,11 @@ _TeamCity* .axoCover/* !.axoCover/settings.json +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + # Visual Studio code coverage results *.coverage *.coveragexml @@ -173,6 +194,7 @@ publish/ *.azurePubxml # Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted +*.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to @@ -180,7 +202,8 @@ publish/ # in these scripts will be unencrypted PublishScripts/ -# NuGet Packages +# NuGet Symbol Packages +*.snupkg # The packages folder can be ignored because of Package Restore **/[Pp]ackages/* # except build/, which is used as an MSBuild target. @@ -205,12 +228,14 @@ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt *.appx +*.appxbundle +*.appxupload # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache -!*.[Cc]ache/ +!?*.[Cc]ache/ # Others ClientBin/ @@ -221,10 +246,11 @@ ClientBin/ *.jfm src/**/*.pfx tests/**/*.pfx +*.pfx *.publishsettings orleans.codegen.cs -# Including strong name files can present a security risk +# Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk @@ -255,6 +281,9 @@ ServiceFabricBackup/ *.bim.layout *.bim_*.settings *.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl # Microsoft Fakes FakesAssemblies/ @@ -275,6 +304,17 @@ node_modules/ # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts @@ -290,12 +330,9 @@ paket-files/ # FAKE - F# Make .fake/ -# JetBrains Rider -.idea/ -*.sln.iml - -# CodeRush +# CodeRush personal settings .cr/ +.cr/personal # Python Tools for Visual Studio (PTVS) __pycache__/ @@ -320,7 +357,7 @@ __pycache__/ # OpenCover UI analysis results OpenCover/ -# Azure Stream Analytics local run output +# Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log @@ -329,6 +366,46 @@ ASALocalRun/ # NVidia Nsight GPU debugger configuration file *.nvuser -# MFractors (Xamarin productivity tool) working folder +# MFractors (Xamarin productivity tool) working folder .mfractor/ -Files.Extensions/nul + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml +.idea/ +src/Files.App/Assets/FilesOpenDialog/Files.App.Launcher.exe.sha256 diff --git a/Directory.Build.props b/Directory.Build.props index 7f2cd7afb94e..6696a667afb4 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,23 +1,13 @@ - + + + net10.0 + 10.0.26100.0 + 10.0.19041.0 + 10.0.26100.67-preview + $(TargetFrameworkVersion)-windows$(TargetWindowsVersion) + preview - - enable - Debug;Stable;Preview;Store - Files Community - Copyright (c) 2024 Files Community. - Files Community - Copyright (c) 2024 Files Community - Files - en-US - x86;x64;ARM64 - win-x86;win-x64;win-arm64 - latest - en-US - - TRACE;DEBUG;NETFX_CORE - TRACE;RELEASE;NETFX_CORE - true - - + true + diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 000000000000..98597455581a --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,65 @@ + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Files.sln b/Files.sln deleted file mode 100644 index 9a93ecc64e8c..000000000000 --- a/Files.sln +++ /dev/null @@ -1,596 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.1.32421.90 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A74DCE98-A744-4D71-A2B1-7EE4FED0936B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{481DE2EA-E6CE-4A9C-A220-3B543B95AAA1}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "platforms", "platforms", "{A188C26B-E731-4E0B-9D17-D21CEBD9B43F}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "core", "core", "{9F36C2AD-005D-4EA5-A1F1-6BC42773FC85}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Files.Shared", "src\Files.Shared\Files.Shared.csproj", "{94F77692-D47C-48D8-A1A7-645192EF38A4}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Files.App.Storage", "src\Files.App.Storage\Files.App.Storage.csproj", "{B8051E11-5BF2-49F7-A7C8-E3820DBB8209}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Files.Core.Storage", "src\Files.Core.Storage\Files.Core.Storage.csproj", "{53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Files.App.BackgroundTasks", "src\Files.App.BackgroundTasks\Files.App.BackgroundTasks.csproj", "{BB1DA0B0-4E5B-4336-961E-DF389482C094}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Files.InteractionTests", "tests\Files.InteractionTests\Files.InteractionTests.csproj", "{4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Files.App.OpenDialog", "src\Files.App.OpenDialog\Files.App.OpenDialog.vcxproj", "{A2FF3F3B-8EBC-4108-B99D-1476B7876656}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Files.App.Launcher", "src\Files.App.Launcher\Files.App.Launcher.vcxproj", "{25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Files.App", "src\Files.App\Files.App.csproj", "{6F431D82-A5FF-4833-B5E4-702E1E523126}" - ProjectSection(ProjectDependencies) = postProject - {1EE996D6-885E-4403-A461-26C7A4E14D26} = {1EE996D6-885E-4403-A461-26C7A4E14D26} - {A2FF3F3B-8EBC-4108-B99D-1476B7876656} = {A2FF3F3B-8EBC-4108-B99D-1476B7876656} - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6} = {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6} - {7756A1A4-17B5-4E6B-9B12-F19AA868A225} = {7756A1A4-17B5-4E6B-9B12-F19AA868A225} - {B3FE3F3B-CECC-4918-B72B-5488C3774125} = {B3FE3F3B-CECC-4918-B72B-5488C3774125} - EndProjectSection -EndProject -Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "Files.Package", "src\Files.App (Package)\Files.Package.wapproj", "{8F60FD8E-1921-47D6-97B0-D26D7B3A4999}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Files.Core.SourceGenerator", "src\Files.Core.SourceGenerator\Files.Core.SourceGenerator.csproj", "{6FA07816-DE0A-4D49-84E8-38E953A33C87}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Files.App.Server", "src\Files.App.Server\Files.App.Server.csproj", "{1EE996D6-885E-4403-A461-26C7A4E14D26}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Files.App.SaveDialog", "src\Files.App.SaveDialog\Files.App.SaveDialog.vcxproj", "{EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Files.App.SaveDialog.Win32", "src\Files.App.SaveDialog\Files.App.SaveDialog.Win32.vcxproj", "{7756A1A4-17B5-4E6B-9B12-F19AA868A225}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Files.App.OpenDialog.Win32", "src\Files.App.OpenDialog\Files.App.OpenDialog.Win32.vcxproj", "{B3FE3F3B-CECC-4918-B72B-5488C3774125}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Files.App.UITests", "tests\Files.App.UITests\Files.App.UITests.csproj", "{6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Files.App.Controls", "src\Files.App.Controls\Files.App.Controls.csproj", "{83FF8729-CC76-43E2-976F-47F0A187FC7E}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|arm64 = Debug|arm64 - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Preview|arm64 = Preview|arm64 - Preview|x64 = Preview|x64 - Preview|x86 = Preview|x86 - Release|arm64 = Release|arm64 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - Stable|arm64 = Stable|arm64 - Stable|x64 = Stable|x64 - Stable|x86 = Stable|x86 - Store|arm64 = Store|arm64 - Store|x64 = Store|x64 - Store|x86 = Store|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Debug|arm64.ActiveCfg = Debug|arm64 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Debug|arm64.Build.0 = Debug|arm64 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Debug|x64.ActiveCfg = Debug|x64 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Debug|x64.Build.0 = Debug|x64 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Debug|x86.ActiveCfg = Debug|x86 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Debug|x86.Build.0 = Debug|x86 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Preview|arm64.ActiveCfg = Preview|arm64 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Preview|arm64.Build.0 = Preview|arm64 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Preview|x64.ActiveCfg = Preview|x64 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Preview|x64.Build.0 = Preview|x64 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Preview|x86.ActiveCfg = Preview|x86 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Preview|x86.Build.0 = Preview|x86 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Release|arm64.ActiveCfg = Release|arm64 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Release|arm64.Build.0 = Release|arm64 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Release|x64.ActiveCfg = Release|x64 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Release|x64.Build.0 = Release|x64 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Release|x86.ActiveCfg = Release|x86 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Release|x86.Build.0 = Release|x86 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Stable|arm64.ActiveCfg = Stable|arm64 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Stable|arm64.Build.0 = Stable|arm64 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Stable|x64.ActiveCfg = Stable|x64 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Stable|x64.Build.0 = Stable|x64 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Stable|x86.ActiveCfg = Stable|x86 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Stable|x86.Build.0 = Stable|x86 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Store|arm64.ActiveCfg = Store|arm64 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Store|arm64.Build.0 = Store|arm64 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Store|x64.ActiveCfg = Store|x64 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Store|x64.Build.0 = Store|x64 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Store|x86.ActiveCfg = Store|x86 - {94F77692-D47C-48D8-A1A7-645192EF38A4}.Store|x86.Build.0 = Store|x86 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Debug|arm64.ActiveCfg = Debug|arm64 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Debug|arm64.Build.0 = Debug|arm64 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Debug|x64.ActiveCfg = Debug|x64 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Debug|x64.Build.0 = Debug|x64 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Debug|x86.ActiveCfg = Debug|x86 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Debug|x86.Build.0 = Debug|x86 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Preview|arm64.ActiveCfg = Preview|arm64 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Preview|arm64.Build.0 = Preview|arm64 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Preview|x64.ActiveCfg = Preview|x64 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Preview|x64.Build.0 = Preview|x64 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Preview|x86.ActiveCfg = Preview|x86 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Preview|x86.Build.0 = Preview|x86 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Release|arm64.ActiveCfg = Release|arm64 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Release|arm64.Build.0 = Release|arm64 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Release|x64.ActiveCfg = Release|x64 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Release|x64.Build.0 = Release|x64 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Release|x86.ActiveCfg = Release|x86 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Release|x86.Build.0 = Release|x86 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Stable|arm64.ActiveCfg = Stable|arm64 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Stable|arm64.Build.0 = Stable|arm64 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Stable|x64.ActiveCfg = Stable|x64 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Stable|x64.Build.0 = Stable|x64 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Stable|x86.ActiveCfg = Stable|x86 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Stable|x86.Build.0 = Stable|x86 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Store|arm64.ActiveCfg = Store|arm64 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Store|arm64.Build.0 = Store|arm64 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Store|x64.ActiveCfg = Store|x64 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Store|x64.Build.0 = Store|x64 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Store|x86.ActiveCfg = Store|x86 - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Store|x86.Build.0 = Store|x86 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Debug|arm64.ActiveCfg = Debug|arm64 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Debug|arm64.Build.0 = Debug|arm64 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Debug|x64.ActiveCfg = Debug|x64 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Debug|x64.Build.0 = Debug|x64 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Debug|x86.ActiveCfg = Debug|x86 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Debug|x86.Build.0 = Debug|x86 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Preview|arm64.ActiveCfg = Preview|arm64 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Preview|arm64.Build.0 = Preview|arm64 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Preview|x64.ActiveCfg = Preview|x64 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Preview|x64.Build.0 = Preview|x64 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Preview|x86.ActiveCfg = Preview|x86 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Preview|x86.Build.0 = Preview|x86 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Release|arm64.ActiveCfg = Release|arm64 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Release|arm64.Build.0 = Release|arm64 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Release|x64.ActiveCfg = Release|x64 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Release|x64.Build.0 = Release|x64 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Release|x86.ActiveCfg = Release|x86 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Release|x86.Build.0 = Release|x86 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Stable|arm64.ActiveCfg = Stable|arm64 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Stable|arm64.Build.0 = Stable|arm64 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Stable|x64.ActiveCfg = Stable|x64 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Stable|x64.Build.0 = Stable|x64 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Stable|x86.ActiveCfg = Stable|x86 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Stable|x86.Build.0 = Stable|x86 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Store|arm64.ActiveCfg = Store|arm64 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Store|arm64.Build.0 = Store|arm64 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Store|x64.ActiveCfg = Store|x64 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Store|x64.Build.0 = Store|x64 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Store|x86.ActiveCfg = Store|x86 - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Store|x86.Build.0 = Store|x86 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Debug|arm64.ActiveCfg = Debug|ARM64 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Debug|arm64.Build.0 = Debug|ARM64 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Debug|x64.ActiveCfg = Debug|x64 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Debug|x64.Build.0 = Debug|x64 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Debug|x86.ActiveCfg = Debug|x86 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Debug|x86.Build.0 = Debug|x86 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Preview|arm64.ActiveCfg = Preview|ARM64 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Preview|arm64.Build.0 = Preview|ARM64 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Preview|x64.ActiveCfg = Preview|x64 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Preview|x64.Build.0 = Preview|x64 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Preview|x86.ActiveCfg = Preview|x86 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Preview|x86.Build.0 = Preview|x86 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Release|arm64.ActiveCfg = Release|ARM64 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Release|arm64.Build.0 = Release|ARM64 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Release|x64.ActiveCfg = Release|x64 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Release|x64.Build.0 = Release|x64 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Release|x86.ActiveCfg = Release|x86 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Release|x86.Build.0 = Release|x86 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Stable|arm64.ActiveCfg = Stable|ARM64 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Stable|arm64.Build.0 = Stable|ARM64 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Stable|x64.ActiveCfg = Stable|x64 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Stable|x64.Build.0 = Stable|x64 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Stable|x86.ActiveCfg = Stable|x86 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Stable|x86.Build.0 = Stable|x86 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Store|arm64.ActiveCfg = Store|ARM64 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Store|arm64.Build.0 = Store|ARM64 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Store|x64.ActiveCfg = Store|x64 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Store|x64.Build.0 = Store|x64 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Store|x86.ActiveCfg = Store|x86 - {BB1DA0B0-4E5B-4336-961E-DF389482C094}.Store|x86.Build.0 = Store|x86 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Debug|arm64.ActiveCfg = Debug|arm64 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Debug|arm64.Build.0 = Debug|arm64 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Debug|x64.ActiveCfg = Debug|x64 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Debug|x64.Build.0 = Debug|x64 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Debug|x86.ActiveCfg = Debug|x86 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Debug|x86.Build.0 = Debug|x86 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Preview|arm64.ActiveCfg = Preview|arm64 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Preview|arm64.Build.0 = Preview|arm64 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Preview|x64.ActiveCfg = Preview|x64 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Preview|x64.Build.0 = Preview|x64 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Preview|x86.ActiveCfg = Preview|x86 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Preview|x86.Build.0 = Preview|x86 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Release|arm64.ActiveCfg = Release|arm64 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Release|arm64.Build.0 = Release|arm64 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Release|x64.ActiveCfg = Release|x64 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Release|x64.Build.0 = Release|x64 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Release|x86.ActiveCfg = Release|x86 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Release|x86.Build.0 = Release|x86 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Stable|arm64.ActiveCfg = Stable|arm64 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Stable|arm64.Build.0 = Stable|arm64 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Stable|x64.ActiveCfg = Stable|x64 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Stable|x64.Build.0 = Stable|x64 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Stable|x86.ActiveCfg = Stable|x86 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Stable|x86.Build.0 = Stable|x86 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Store|arm64.ActiveCfg = Store|arm64 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Store|arm64.Build.0 = Store|arm64 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Store|x64.ActiveCfg = Store|x64 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Store|x64.Build.0 = Store|x64 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Store|x86.ActiveCfg = Store|x86 - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0}.Store|x86.Build.0 = Store|x86 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Debug|arm64.ActiveCfg = Debug|arm64 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Debug|arm64.Build.0 = Debug|arm64 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Debug|x64.ActiveCfg = Debug|x64 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Debug|x64.Build.0 = Debug|x64 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Debug|x86.ActiveCfg = Debug|Win32 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Debug|x86.Build.0 = Debug|Win32 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Preview|arm64.ActiveCfg = Preview|arm64 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Preview|arm64.Build.0 = Preview|arm64 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Preview|x64.ActiveCfg = Preview|x64 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Preview|x64.Build.0 = Preview|x64 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Preview|x86.ActiveCfg = Preview|Win32 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Preview|x86.Build.0 = Preview|Win32 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Release|arm64.ActiveCfg = Release|arm64 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Release|arm64.Build.0 = Release|arm64 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Release|x64.ActiveCfg = Release|x64 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Release|x64.Build.0 = Release|x64 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Release|x86.ActiveCfg = Release|Win32 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Release|x86.Build.0 = Release|Win32 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Stable|arm64.ActiveCfg = Stable|arm64 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Stable|arm64.Build.0 = Stable|arm64 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Stable|x64.ActiveCfg = Stable|x64 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Stable|x64.Build.0 = Stable|x64 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Stable|x86.ActiveCfg = Stable|Win32 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Stable|x86.Build.0 = Stable|Win32 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Store|arm64.ActiveCfg = Store|arm64 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Store|arm64.Build.0 = Store|arm64 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Store|x64.ActiveCfg = Store|x64 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Store|x64.Build.0 = Store|x64 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Store|x86.ActiveCfg = Store|Win32 - {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Store|x86.Build.0 = Store|Win32 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Debug|arm64.ActiveCfg = Debug|arm64 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Debug|arm64.Build.0 = Debug|arm64 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Debug|x64.ActiveCfg = Debug|x64 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Debug|x64.Build.0 = Debug|x64 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Debug|x86.ActiveCfg = Debug|Win32 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Debug|x86.Build.0 = Debug|Win32 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Preview|arm64.ActiveCfg = Preview|arm64 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Preview|arm64.Build.0 = Preview|arm64 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Preview|x64.ActiveCfg = Preview|x64 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Preview|x64.Build.0 = Preview|x64 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Preview|x86.ActiveCfg = Preview|Win32 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Preview|x86.Build.0 = Preview|Win32 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Release|arm64.ActiveCfg = Release|arm64 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Release|arm64.Build.0 = Release|arm64 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Release|x64.ActiveCfg = Release|x64 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Release|x64.Build.0 = Release|x64 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Release|x86.ActiveCfg = Release|Win32 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Release|x86.Build.0 = Release|Win32 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Stable|arm64.ActiveCfg = Stable|arm64 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Stable|arm64.Build.0 = Stable|arm64 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Stable|x64.ActiveCfg = Stable|x64 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Stable|x64.Build.0 = Stable|x64 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Stable|x86.ActiveCfg = Stable|Win32 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Stable|x86.Build.0 = Stable|Win32 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Store|arm64.ActiveCfg = Store|arm64 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Store|arm64.Build.0 = Store|arm64 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Store|x64.ActiveCfg = Store|x64 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Store|x64.Build.0 = Store|x64 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Store|x86.ActiveCfg = Store|Win32 - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07}.Store|x86.Build.0 = Store|Win32 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Debug|arm64.ActiveCfg = Debug|arm64 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Debug|arm64.Build.0 = Debug|arm64 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Debug|arm64.Deploy.0 = Debug|arm64 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Debug|x64.ActiveCfg = Debug|x64 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Debug|x64.Build.0 = Debug|x64 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Debug|x64.Deploy.0 = Debug|x64 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Debug|x86.ActiveCfg = Debug|x86 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Debug|x86.Build.0 = Debug|x86 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Debug|x86.Deploy.0 = Debug|x86 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Preview|arm64.ActiveCfg = Preview|arm64 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Preview|arm64.Build.0 = Preview|arm64 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Preview|x64.ActiveCfg = Preview|x64 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Preview|x64.Build.0 = Preview|x64 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Preview|x86.ActiveCfg = Preview|x86 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Preview|x86.Build.0 = Preview|x86 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Release|arm64.ActiveCfg = Release|arm64 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Release|arm64.Build.0 = Release|arm64 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Release|arm64.Deploy.0 = Release|arm64 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Release|x64.ActiveCfg = Release|x64 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Release|x64.Build.0 = Release|x64 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Release|x64.Deploy.0 = Release|x64 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Release|x86.ActiveCfg = Release|x86 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Release|x86.Build.0 = Release|x86 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Release|x86.Deploy.0 = Release|x86 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Stable|arm64.ActiveCfg = Stable|arm64 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Stable|arm64.Build.0 = Stable|arm64 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Stable|x64.ActiveCfg = Stable|x64 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Stable|x64.Build.0 = Stable|x64 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Stable|x86.ActiveCfg = Stable|x86 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Stable|x86.Build.0 = Stable|x86 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Store|arm64.ActiveCfg = Store|arm64 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Store|arm64.Build.0 = Store|arm64 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Store|x64.ActiveCfg = Store|x64 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Store|x64.Build.0 = Store|x64 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Store|x86.ActiveCfg = Store|x86 - {6F431D82-A5FF-4833-B5E4-702E1E523126}.Store|x86.Build.0 = Store|x86 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Debug|arm64.ActiveCfg = Debug|ARM64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Debug|arm64.Build.0 = Debug|ARM64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Debug|arm64.Deploy.0 = Debug|ARM64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Debug|x64.ActiveCfg = Debug|x64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Debug|x64.Build.0 = Debug|x64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Debug|x64.Deploy.0 = Debug|x64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Debug|x86.ActiveCfg = Debug|x86 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Debug|x86.Build.0 = Debug|x86 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Debug|x86.Deploy.0 = Debug|x86 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Preview|arm64.ActiveCfg = Preview|ARM64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Preview|arm64.Build.0 = Preview|ARM64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Preview|arm64.Deploy.0 = Preview|ARM64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Preview|x64.ActiveCfg = Preview|x64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Preview|x64.Build.0 = Preview|x64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Preview|x64.Deploy.0 = Preview|x64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Preview|x86.ActiveCfg = Preview|x86 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Preview|x86.Build.0 = Preview|x86 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Preview|x86.Deploy.0 = Preview|x86 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Release|arm64.ActiveCfg = Release|ARM64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Release|arm64.Build.0 = Release|ARM64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Release|arm64.Deploy.0 = Release|ARM64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Release|x64.ActiveCfg = Release|x64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Release|x64.Build.0 = Release|x64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Release|x64.Deploy.0 = Release|x64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Release|x86.ActiveCfg = Release|x86 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Release|x86.Build.0 = Release|x86 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Release|x86.Deploy.0 = Release|x86 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Stable|arm64.ActiveCfg = Stable|ARM64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Stable|arm64.Build.0 = Stable|ARM64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Stable|arm64.Deploy.0 = Stable|ARM64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Stable|x64.ActiveCfg = Stable|x64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Stable|x64.Build.0 = Stable|x64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Stable|x64.Deploy.0 = Stable|x64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Stable|x86.ActiveCfg = Stable|x86 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Stable|x86.Build.0 = Stable|x86 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Stable|x86.Deploy.0 = Stable|x86 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Store|arm64.ActiveCfg = Store|ARM64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Store|arm64.Build.0 = Store|ARM64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Store|arm64.Deploy.0 = Store|ARM64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Store|x64.ActiveCfg = Store|x64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Store|x64.Build.0 = Store|x64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Store|x64.Deploy.0 = Store|x64 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Store|x86.ActiveCfg = Store|x86 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Store|x86.Build.0 = Store|x86 - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999}.Store|x86.Deploy.0 = Store|x86 - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Debug|arm64.ActiveCfg = Debug|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Debug|arm64.Build.0 = Debug|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Debug|x64.ActiveCfg = Debug|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Debug|x64.Build.0 = Debug|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Debug|x86.ActiveCfg = Debug|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Debug|x86.Build.0 = Debug|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Preview|arm64.ActiveCfg = Release|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Preview|arm64.Build.0 = Release|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Preview|x64.ActiveCfg = Release|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Preview|x64.Build.0 = Release|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Preview|x86.ActiveCfg = Release|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Preview|x86.Build.0 = Release|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Release|arm64.ActiveCfg = Release|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Release|arm64.Build.0 = Release|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Release|x64.ActiveCfg = Release|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Release|x64.Build.0 = Release|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Release|x86.ActiveCfg = Release|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Release|x86.Build.0 = Release|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Stable|arm64.ActiveCfg = Release|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Stable|arm64.Build.0 = Release|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Stable|x64.ActiveCfg = Release|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Stable|x64.Build.0 = Release|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Stable|x86.ActiveCfg = Release|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Stable|x86.Build.0 = Release|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Store|arm64.ActiveCfg = Release|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Store|arm64.Build.0 = Release|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Store|x64.ActiveCfg = Release|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Store|x64.Build.0 = Release|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Store|x86.ActiveCfg = Release|Any CPU - {6FA07816-DE0A-4D49-84E8-38E953A33C87}.Store|x86.Build.0 = Release|Any CPU - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Debug|arm64.ActiveCfg = Debug|arm64 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Debug|arm64.Build.0 = Debug|arm64 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Debug|arm64.Deploy.0 = Debug|arm64 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Debug|x64.ActiveCfg = Debug|x64 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Debug|x64.Build.0 = Debug|x64 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Debug|x64.Deploy.0 = Debug|x64 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Debug|x86.ActiveCfg = Debug|x86 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Debug|x86.Build.0 = Debug|x86 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Debug|x86.Deploy.0 = Debug|x86 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Preview|arm64.ActiveCfg = Preview|arm64 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Preview|arm64.Build.0 = Preview|arm64 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Preview|x64.ActiveCfg = Preview|x64 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Preview|x64.Build.0 = Preview|x64 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Preview|x86.ActiveCfg = Preview|x86 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Preview|x86.Build.0 = Preview|x86 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Release|arm64.ActiveCfg = Release|arm64 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Release|arm64.Build.0 = Release|arm64 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Release|arm64.Deploy.0 = Release|arm64 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Release|x64.ActiveCfg = Release|x64 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Release|x64.Build.0 = Release|x64 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Release|x64.Deploy.0 = Release|x64 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Release|x86.ActiveCfg = Release|x86 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Release|x86.Build.0 = Release|x86 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Release|x86.Deploy.0 = Release|x86 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Stable|arm64.ActiveCfg = Stable|arm64 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Stable|arm64.Build.0 = Stable|arm64 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Stable|x64.ActiveCfg = Stable|x64 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Stable|x64.Build.0 = Stable|x64 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Stable|x86.ActiveCfg = Stable|x86 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Stable|x86.Build.0 = Stable|x86 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Store|arm64.ActiveCfg = Store|arm64 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Store|arm64.Build.0 = Store|arm64 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Store|x64.ActiveCfg = Store|x64 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Store|x64.Build.0 = Store|x64 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Store|x86.ActiveCfg = Store|x86 - {1EE996D6-885E-4403-A461-26C7A4E14D26}.Store|x86.Build.0 = Store|x86 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Debug|arm64.ActiveCfg = Debug|x64 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Debug|arm64.Build.0 = Debug|x64 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Debug|x64.ActiveCfg = Debug|x64 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Debug|x64.Build.0 = Debug|x64 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Debug|x86.ActiveCfg = Debug|Win32 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Debug|x86.Build.0 = Debug|Win32 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Preview|arm64.ActiveCfg = Debug|x64 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Preview|arm64.Build.0 = Debug|x64 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Preview|x64.ActiveCfg = Debug|x64 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Preview|x64.Build.0 = Debug|x64 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Preview|x86.ActiveCfg = Debug|Win32 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Preview|x86.Build.0 = Debug|Win32 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Release|arm64.ActiveCfg = Release|x64 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Release|arm64.Build.0 = Release|x64 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Release|x64.ActiveCfg = Release|x64 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Release|x64.Build.0 = Release|x64 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Release|x86.ActiveCfg = Release|Win32 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Release|x86.Build.0 = Release|Win32 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Stable|arm64.ActiveCfg = Debug|x64 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Stable|arm64.Build.0 = Debug|x64 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Stable|x64.ActiveCfg = Debug|x64 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Stable|x64.Build.0 = Debug|x64 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Stable|x86.ActiveCfg = Debug|Win32 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Stable|x86.Build.0 = Debug|Win32 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Store|arm64.ActiveCfg = Debug|x64 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Store|arm64.Build.0 = Debug|x64 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Store|x64.ActiveCfg = Debug|x64 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Store|x64.Build.0 = Debug|x64 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Store|x86.ActiveCfg = Debug|Win32 - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6}.Store|x86.Build.0 = Debug|Win32 - {7756A1A4-17B5-4E6B-9B12-F19AA868A225}.Debug|arm64.ActiveCfg = Debug|Win32 - {7756A1A4-17B5-4E6B-9B12-F19AA868A225}.Debug|arm64.Build.0 = Debug|Win32 - {7756A1A4-17B5-4E6B-9B12-F19AA868A225}.Debug|x64.ActiveCfg = Debug|Win32 - {7756A1A4-17B5-4E6B-9B12-F19AA868A225}.Debug|x64.Build.0 = Debug|Win32 - {7756A1A4-17B5-4E6B-9B12-F19AA868A225}.Debug|x86.ActiveCfg = Debug|Win32 - {7756A1A4-17B5-4E6B-9B12-F19AA868A225}.Preview|arm64.ActiveCfg = Debug|Win32 - {7756A1A4-17B5-4E6B-9B12-F19AA868A225}.Preview|arm64.Build.0 = Debug|Win32 - {7756A1A4-17B5-4E6B-9B12-F19AA868A225}.Preview|x64.ActiveCfg = Debug|Win32 - {7756A1A4-17B5-4E6B-9B12-F19AA868A225}.Preview|x64.Build.0 = Debug|Win32 - {7756A1A4-17B5-4E6B-9B12-F19AA868A225}.Preview|x86.ActiveCfg = Debug|Win32 - {7756A1A4-17B5-4E6B-9B12-F19AA868A225}.Release|arm64.ActiveCfg = Release|Win32 - {7756A1A4-17B5-4E6B-9B12-F19AA868A225}.Release|arm64.Build.0 = Release|Win32 - {7756A1A4-17B5-4E6B-9B12-F19AA868A225}.Release|x64.ActiveCfg = Release|Win32 - {7756A1A4-17B5-4E6B-9B12-F19AA868A225}.Release|x64.Build.0 = Release|Win32 - {7756A1A4-17B5-4E6B-9B12-F19AA868A225}.Release|x86.ActiveCfg = Release|Win32 - {7756A1A4-17B5-4E6B-9B12-F19AA868A225}.Stable|arm64.ActiveCfg = Debug|Win32 - {7756A1A4-17B5-4E6B-9B12-F19AA868A225}.Stable|arm64.Build.0 = Debug|Win32 - {7756A1A4-17B5-4E6B-9B12-F19AA868A225}.Stable|x64.ActiveCfg = Debug|Win32 - {7756A1A4-17B5-4E6B-9B12-F19AA868A225}.Stable|x64.Build.0 = Debug|Win32 - {7756A1A4-17B5-4E6B-9B12-F19AA868A225}.Stable|x86.ActiveCfg = Debug|Win32 - {7756A1A4-17B5-4E6B-9B12-F19AA868A225}.Store|arm64.ActiveCfg = Debug|Win32 - {7756A1A4-17B5-4E6B-9B12-F19AA868A225}.Store|arm64.Build.0 = Debug|Win32 - {7756A1A4-17B5-4E6B-9B12-F19AA868A225}.Store|x64.ActiveCfg = Debug|Win32 - {7756A1A4-17B5-4E6B-9B12-F19AA868A225}.Store|x64.Build.0 = Debug|Win32 - {7756A1A4-17B5-4E6B-9B12-F19AA868A225}.Store|x86.ActiveCfg = Debug|Win32 - {B3FE3F3B-CECC-4918-B72B-5488C3774125}.Debug|arm64.ActiveCfg = Debug|Win32 - {B3FE3F3B-CECC-4918-B72B-5488C3774125}.Debug|arm64.Build.0 = Debug|Win32 - {B3FE3F3B-CECC-4918-B72B-5488C3774125}.Debug|x64.ActiveCfg = Debug|Win32 - {B3FE3F3B-CECC-4918-B72B-5488C3774125}.Debug|x64.Build.0 = Debug|Win32 - {B3FE3F3B-CECC-4918-B72B-5488C3774125}.Debug|x86.ActiveCfg = Debug|Win32 - {B3FE3F3B-CECC-4918-B72B-5488C3774125}.Preview|arm64.ActiveCfg = Debug|Win32 - {B3FE3F3B-CECC-4918-B72B-5488C3774125}.Preview|arm64.Build.0 = Debug|Win32 - {B3FE3F3B-CECC-4918-B72B-5488C3774125}.Preview|x64.ActiveCfg = Debug|Win32 - {B3FE3F3B-CECC-4918-B72B-5488C3774125}.Preview|x64.Build.0 = Debug|Win32 - {B3FE3F3B-CECC-4918-B72B-5488C3774125}.Preview|x86.ActiveCfg = Debug|Win32 - {B3FE3F3B-CECC-4918-B72B-5488C3774125}.Release|arm64.ActiveCfg = Release|Win32 - {B3FE3F3B-CECC-4918-B72B-5488C3774125}.Release|arm64.Build.0 = Release|Win32 - {B3FE3F3B-CECC-4918-B72B-5488C3774125}.Release|x64.ActiveCfg = Release|Win32 - {B3FE3F3B-CECC-4918-B72B-5488C3774125}.Release|x64.Build.0 = Release|Win32 - {B3FE3F3B-CECC-4918-B72B-5488C3774125}.Release|x86.ActiveCfg = Release|Win32 - {B3FE3F3B-CECC-4918-B72B-5488C3774125}.Stable|arm64.ActiveCfg = Debug|Win32 - {B3FE3F3B-CECC-4918-B72B-5488C3774125}.Stable|arm64.Build.0 = Debug|Win32 - {B3FE3F3B-CECC-4918-B72B-5488C3774125}.Stable|x64.ActiveCfg = Debug|Win32 - {B3FE3F3B-CECC-4918-B72B-5488C3774125}.Stable|x64.Build.0 = Debug|Win32 - {B3FE3F3B-CECC-4918-B72B-5488C3774125}.Stable|x86.ActiveCfg = Debug|Win32 - {B3FE3F3B-CECC-4918-B72B-5488C3774125}.Store|arm64.ActiveCfg = Debug|Win32 - {B3FE3F3B-CECC-4918-B72B-5488C3774125}.Store|arm64.Build.0 = Debug|Win32 - {B3FE3F3B-CECC-4918-B72B-5488C3774125}.Store|x64.ActiveCfg = Debug|Win32 - {B3FE3F3B-CECC-4918-B72B-5488C3774125}.Store|x64.Build.0 = Debug|Win32 - {B3FE3F3B-CECC-4918-B72B-5488C3774125}.Store|x86.ActiveCfg = Debug|Win32 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Debug|arm64.ActiveCfg = Debug|ARM64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Debug|arm64.Build.0 = Debug|ARM64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Debug|arm64.Deploy.0 = Debug|ARM64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Debug|x64.ActiveCfg = Debug|x64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Debug|x64.Build.0 = Debug|x64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Debug|x64.Deploy.0 = Debug|x64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Debug|x86.ActiveCfg = Debug|x86 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Debug|x86.Build.0 = Debug|x86 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Debug|x86.Deploy.0 = Debug|x86 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Preview|arm64.ActiveCfg = Debug|ARM64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Preview|arm64.Build.0 = Debug|ARM64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Preview|arm64.Deploy.0 = Debug|ARM64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Preview|x64.ActiveCfg = Debug|x64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Preview|x64.Build.0 = Debug|x64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Preview|x64.Deploy.0 = Debug|x64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Preview|x86.ActiveCfg = Debug|x86 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Preview|x86.Build.0 = Debug|x86 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Preview|x86.Deploy.0 = Debug|x86 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Release|arm64.ActiveCfg = Release|ARM64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Release|arm64.Build.0 = Release|ARM64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Release|arm64.Deploy.0 = Release|ARM64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Release|x64.ActiveCfg = Release|x64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Release|x64.Build.0 = Release|x64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Release|x64.Deploy.0 = Release|x64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Release|x86.ActiveCfg = Release|x86 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Release|x86.Build.0 = Release|x86 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Release|x86.Deploy.0 = Release|x86 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Stable|arm64.ActiveCfg = Debug|ARM64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Stable|arm64.Build.0 = Debug|ARM64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Stable|arm64.Deploy.0 = Debug|ARM64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Stable|x64.ActiveCfg = Debug|x64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Stable|x64.Build.0 = Debug|x64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Stable|x64.Deploy.0 = Debug|x64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Stable|x86.ActiveCfg = Debug|x86 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Stable|x86.Build.0 = Debug|x86 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Stable|x86.Deploy.0 = Debug|x86 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Store|arm64.ActiveCfg = Debug|ARM64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Store|arm64.Build.0 = Debug|ARM64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Store|arm64.Deploy.0 = Debug|ARM64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Store|x64.ActiveCfg = Debug|x64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Store|x64.Build.0 = Debug|x64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Store|x64.Deploy.0 = Debug|x64 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Store|x86.ActiveCfg = Debug|x86 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Store|x86.Build.0 = Debug|x86 - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B}.Store|x86.Deploy.0 = Debug|x86 - {83FF8729-CC76-43E2-976F-47F0A187FC7E}.Debug|arm64.ActiveCfg = Debug|x64 - {83FF8729-CC76-43E2-976F-47F0A187FC7E}.Debug|x64.ActiveCfg = Debug|x64 - {83FF8729-CC76-43E2-976F-47F0A187FC7E}.Debug|x64.Build.0 = Debug|x64 - {83FF8729-CC76-43E2-976F-47F0A187FC7E}.Debug|x86.ActiveCfg = Debug|x64 - {83FF8729-CC76-43E2-976F-47F0A187FC7E}.Preview|arm64.ActiveCfg = Debug|x64 - {83FF8729-CC76-43E2-976F-47F0A187FC7E}.Preview|x64.ActiveCfg = Debug|x64 - {83FF8729-CC76-43E2-976F-47F0A187FC7E}.Preview|x86.ActiveCfg = Debug|x64 - {83FF8729-CC76-43E2-976F-47F0A187FC7E}.Release|arm64.ActiveCfg = Release|x64 - {83FF8729-CC76-43E2-976F-47F0A187FC7E}.Release|x64.ActiveCfg = Release|x64 - {83FF8729-CC76-43E2-976F-47F0A187FC7E}.Release|x86.ActiveCfg = Release|x64 - {83FF8729-CC76-43E2-976F-47F0A187FC7E}.Stable|arm64.ActiveCfg = Debug|x64 - {83FF8729-CC76-43E2-976F-47F0A187FC7E}.Stable|x64.ActiveCfg = Debug|x64 - {83FF8729-CC76-43E2-976F-47F0A187FC7E}.Stable|x86.ActiveCfg = Debug|x64 - {83FF8729-CC76-43E2-976F-47F0A187FC7E}.Store|arm64.ActiveCfg = Debug|x64 - {83FF8729-CC76-43E2-976F-47F0A187FC7E}.Store|x64.ActiveCfg = Debug|x64 - {83FF8729-CC76-43E2-976F-47F0A187FC7E}.Store|x86.ActiveCfg = Debug|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {A188C26B-E731-4E0B-9D17-D21CEBD9B43F} = {A74DCE98-A744-4D71-A2B1-7EE4FED0936B} - {9F36C2AD-005D-4EA5-A1F1-6BC42773FC85} = {A74DCE98-A744-4D71-A2B1-7EE4FED0936B} - {94F77692-D47C-48D8-A1A7-645192EF38A4} = {9F36C2AD-005D-4EA5-A1F1-6BC42773FC85} - {B8051E11-5BF2-49F7-A7C8-E3820DBB8209} = {A188C26B-E731-4E0B-9D17-D21CEBD9B43F} - {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3} = {9F36C2AD-005D-4EA5-A1F1-6BC42773FC85} - {BB1DA0B0-4E5B-4336-961E-DF389482C094} = {A188C26B-E731-4E0B-9D17-D21CEBD9B43F} - {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0} = {481DE2EA-E6CE-4A9C-A220-3B543B95AAA1} - {A2FF3F3B-8EBC-4108-B99D-1476B7876656} = {A188C26B-E731-4E0B-9D17-D21CEBD9B43F} - {25FD5045-6D4C-4DD0-B3AC-613AB59CBB07} = {A188C26B-E731-4E0B-9D17-D21CEBD9B43F} - {6F431D82-A5FF-4833-B5E4-702E1E523126} = {A188C26B-E731-4E0B-9D17-D21CEBD9B43F} - {8F60FD8E-1921-47D6-97B0-D26D7B3A4999} = {A188C26B-E731-4E0B-9D17-D21CEBD9B43F} - {6FA07816-DE0A-4D49-84E8-38E953A33C87} = {9F36C2AD-005D-4EA5-A1F1-6BC42773FC85} - {1EE996D6-885E-4403-A461-26C7A4E14D26} = {A188C26B-E731-4E0B-9D17-D21CEBD9B43F} - {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6} = {A188C26B-E731-4E0B-9D17-D21CEBD9B43F} - {7756A1A4-17B5-4E6B-9B12-F19AA868A225} = {A188C26B-E731-4E0B-9D17-D21CEBD9B43F} - {B3FE3F3B-CECC-4918-B72B-5488C3774125} = {A188C26B-E731-4E0B-9D17-D21CEBD9B43F} - {6F5B1C76-6FA1-49C0-9AF5-672BEDF6900B} = {481DE2EA-E6CE-4A9C-A220-3B543B95AAA1} - {83FF8729-CC76-43E2-976F-47F0A187FC7E} = {A188C26B-E731-4E0B-9D17-D21CEBD9B43F} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {0E62043C-A7A1-4982-9EC9-4CDB2939B776} - EndGlobalSection -EndGlobal diff --git a/Files.slnx b/Files.slnx new file mode 100644 index 000000000000..49657a490d21 --- /dev/null +++ b/Files.slnx @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LICENSE b/LICENSE index 7069f962a688..f534703f4f52 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 Files Community +Copyright (c) 2018 - present Files Community Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Settings.XamlStyler b/Settings.XamlStyler index 117aea82adf0..d831092fff19 100644 --- a/Settings.XamlStyler +++ b/Settings.XamlStyler @@ -1,3 +1,4 @@ { - "IndentWithTabs": true + "IndentWithTabs": true, + "NoNewLineMarkupExtensions": "x:Bind, Binding, controls:ThemedIconMarkup", } \ No newline at end of file diff --git a/assets/ClassicInstallerBadge-dark.png b/assets/ClassicInstallerBadge-dark.png deleted file mode 100644 index 645112e689a1..000000000000 Binary files a/assets/ClassicInstallerBadge-dark.png and /dev/null differ diff --git a/assets/ClassicInstallerBadge-light.png b/assets/ClassicInstallerBadge-light.png deleted file mode 100644 index 1875cf4cf2c3..000000000000 Binary files a/assets/ClassicInstallerBadge-light.png and /dev/null differ diff --git a/assets/FilesScreenshot.png b/assets/FilesScreenshot.png deleted file mode 100644 index fb20d5af425a..000000000000 Binary files a/assets/FilesScreenshot.png and /dev/null differ diff --git a/assets/ReadmeHero.png b/assets/ReadmeHero.png deleted file mode 100644 index 00362280b874..000000000000 Binary files a/assets/ReadmeHero.png and /dev/null differ diff --git a/assets/StoreBadge-dark.png b/assets/StoreBadge-dark.png deleted file mode 100644 index 5409ad81e71c..000000000000 Binary files a/assets/StoreBadge-dark.png and /dev/null differ diff --git a/assets/StoreBadge-light.png b/assets/StoreBadge-light.png deleted file mode 100644 index e03d0e5362d3..000000000000 Binary files a/assets/StoreBadge-light.png and /dev/null differ diff --git a/builds/azure-pipelines-release.yml b/builds/azure-pipelines-release.yml deleted file mode 100644 index c5892ad91de2..000000000000 --- a/builds/azure-pipelines-release.yml +++ /dev/null @@ -1,172 +0,0 @@ -trigger: none -pr: none - -pool: - vmImage: 'windows-latest' - -parameters: - - name: releaseBranch - displayName: Release Branch - type: string - default: 'stable' - values: - - 'stable' - - 'preview' - - name: packageIdentityName - displayName: Package Identity Name - type: string - default: 'Files' - values: - - 'Files' - - 'FilesPreview' - - name: packageDisplayName - displayName: Package Display Name - type: string - default: 'Files' - values: - - 'Files' - - 'Files - Preview' - - name: iconVariant - displayName: Icon Variant - type: string - default: 'Release' - values: - - 'Release' - - 'Preview' - -variables: -- group: BuildPipelineVariables -- name: solution - value: '**/*.sln' -- name: buildPlatform - value: 'x64|ARM64' -- name: storeBuildConfiguration - value: 'Store' -- name: stableBuildConfiguration - value: 'Stable' -- name: appxPackageDir - value: '$(build.artifactStagingDirectory)\AppxPackages\\' - -jobs: -### Store release ### -- job: StoreRelease - timeoutInMinutes: 120 - - steps: - - task: PowerShell@2 - inputs: - targetType: 'inline' - script: | - [xml]$xmlDoc = Get-Content '$(Build.SourcesDirectory)\src\Files.App (Package)\Package.appxmanifest' - $xmlDoc.Package.Identity.Name="49306atecsolution.FilesUWP" - $xmlDoc.Package.Identity.Publisher="CN=53EC4384-7F5B-4CF6-8C23-513FFE9D1AB7" - $xmlDoc.Package.Properties.DisplayName="Files" - $xmlDoc.Package.Applications.Application.VisualElements.DisplayName="Files" - # Removes packageManagement from Store release - $nsmgr = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable) - $nsmgr.AddNamespace("pkg", "http://schemas.microsoft.com/appx/manifest/foundation/windows10") - $nsmgr.AddNamespace("rescap", "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities") - $pm = $xmlDoc.SelectSingleNode("/pkg:Package/pkg:Capabilities/rescap:Capability[@Name='packageManagement']", $nsmgr) - $xmlDoc.Package.Capabilities.RemoveChild($pm) - $xmlDoc.Save('$(Build.SourcesDirectory)\src\Files.App (Package)\Package.appxmanifest') - failOnStderr: true - - # This replaces references to the dev icon with the specified icon variant - - task: PowerShell@2 - displayName: 'Use Correct Logo' - inputs: - targetType: 'inline' - script: | - gci $(Build.SourcesDirectory)\src -Include *.csproj, *.appxmanifest, *.wapproj, *.xaml -recurse | ForEach -Process { - (Get-Content $_ -Raw | ForEach -Process {$_ -replace "Assets\\AppTiles\\Dev", "Assets\AppTiles\Release"}) | Set-Content $_ -NoNewline - } - failOnStderr: true - -# Adds the Bing maps token to the project - - task: DownloadSecureFile@1 - name: mapsDevKey - displayName: 'Download Bing Maps Dev Key' - inputs: - secureFile: 'BingMapsKey.txt' - - - task: CopyFiles@2 - inputs: - SourceFolder: '$(Agent.TempDirectory)' - Contents: '$(mapsDevKey.secureFilePath)' - TargetFolder: '$(Build.SourcesDirectory)\src\Files.App\Resources' - overWrite: true - -# Injects the Sentry token to the project - - task: PowerShell@2 - displayName: 'Inject Sentry token' - inputs: - targetType: 'inline' - script: | - gci $(Build.SourcesDirectory)\src -Include *.cs -recurse | ForEach -Process { - (Get-Content $_ -Raw | ForEach -Process {$_ -replace "sentry.secret", "$(sentry.secret)"}) | Set-Content $_ -NoNewline - } - failOnStderr: true - -# Injects the GitHub token to the project - - task: PowerShell@2 - displayName: 'Inject GitHub token' - inputs: - targetType: 'inline' - script: | - gci $(Build.SourcesDirectory)\src -Include *.cs -recurse | ForEach -Process { - (Get-Content $_ -Raw | ForEach -Process {$_ -replace "githubclientid.secret", "$(githubclientid.secret)"}) | Set-Content $_ -NoNewline - } - failOnStderr: true - - - task: UseDotNet@2 - inputs: - packageType: sdk - version: 8.0.x - includePreviewVersions: false - - - task: CmdLine@2 - inputs: - script: | - for /f %%a in ('dir /b /a:d %localappdata%\Microsoft\VisualStudio\17*') do echo UsePreviews=True>%localappdata%\Microsoft\VisualStudio\%%a\sdk.txt - - - task: MSBuild@1 - inputs: - platform: 'x64' - solution: '$(solution)' - configuration: '$(storeBuildConfiguration)' - msbuildArguments: '/t:restore /p:Configuration="$(storeBuildConfiguration)";Platform="$(buildPlatform)";PublishReadyToRun=true' - maximumCpuCount: true - - - task: MSBuild@1 - inputs: - solution: '**/*.wapproj' - platform: 'x64' - configuration: '$(storeBuildConfiguration)' - msbuildArguments: '/t:build;_GenerateAppxPackage /p:AppxBundlePlatforms="$(buildPlatform)" /p:AppxPackageDir="$(appxPackageDir)" /p:AppxBundle=Always /p:UapAppxPackageBuildMode=StoreUpload' - maximumCpuCount: true - - - task: CopyFiles@2 - displayName: 'Copy Files to: $(build.artifactstagingdirectory)' - inputs: - SourceFolder: '$(system.defaultworkingdirectory)' - Contents: '**\bin\$(storeBuildConfiguration)\**' - TargetFolder: '$(build.artifactstagingdirectory)' - - - task: PublishBuildArtifacts@1 - displayName: 'Publish Artifact: Store' - inputs: - PathtoPublish: '$(build.artifactstagingdirectory)' - ArtifactName: 'Store' - publishLocation: 'Container' - - - task: store-flight@0 - inputs: - serviceEndpoint: 'Files' - appId: '9NGHP3DX8HDX' - flightName: 'Files - Preview' - packagePath: '$(appxPackageDir)\**\*.msixupload' - force: false - skipPolling: false - deletePackages: true - numberOfPackagesToKeep: '5' - isMandatoryUpdate: true diff --git a/crowdin.yml b/crowdin.yml index cc0e9a2da319..381fd6b58b37 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -1,4 +1,5 @@ -commit_message: 'fix: new translations in %original_file_name% (%original_file_name%) from Crowdin' +commit_message: Translate resources in `%original_file_name%` via Crowdin +pull_request_title: 'Code Quality: Updated translations' append_commit_message: files: - source: /src/Files.App/Strings/en-US/*.resw diff --git a/docs/istorage-enum.md b/docs/istorage-enum.md deleted file mode 100644 index f9d620f082bf..000000000000 --- a/docs/istorage-enum.md +++ /dev/null @@ -1,50 +0,0 @@ -## Code examples - -```cs -using System; -using System.Collections.Generic; -using System.Threading; - -namespace SecureFolderFS.Sdk.Storage.StorageEnumeration -{ - /// - /// Enumerates storage objects of a given directory. - /// - /// This interface can be implemented to provide complex enumeration of directories as well as being a substitute for built-in enumeration. - /// - public interface IStorageEnumerator : IDisposable - { - /// - /// Gets the folder where enumeration takes place. - /// - IFolder SourceFolder { get; } - - /// - /// Enumerates the for files. - /// - /// A that cancels this action. - /// Returns an async operation represented by of type of all files discovered by the enumerator. - IAsyncEnumerable> EnumerateFilesAsync(CancellationToken cancellationToken = default); - - /// - /// Enumerates the for folders. - /// - /// A that cancels this action. - /// Returns an async operation represented by of type of all folders discovered by the enumerator. - IAsyncEnumerable> EnumerateFoldersAsync(CancellationToken cancellationToken = default); - - /// - /// Enumerates the for items. - /// - /// A that cancels this action. - /// Returns an async operation represented by of type of all items discovered by the enumerator. - IAsyncEnumerable> EnumerateStorageAsync(CancellationToken cancellationToken = default); - } -} - -class EnumerationResult where T : IStorable -{ - T Storable { get; } - IStoragePropertiesCollection? Properties { get; } -} -``` \ No newline at end of file diff --git a/docs/listeditem.md b/docs/listeditem.md deleted file mode 100644 index 1c6a20554d39..000000000000 --- a/docs/listeditem.md +++ /dev/null @@ -1,76 +0,0 @@ -# StorableViewModel - -## Code examples -```csharp - public abstract class StorableViewModel : ObservableObject - { - public IStorable Storable { get; } - - public StorageItemViewModel(IStorable storable) - { - this.storable = storable; - } - } -``` - -```csharp - public class StandardItemViewModel : StorableViewModel - { - private long _size; - - public long Size - { - get => _size; - set => SetProperty(ref _size, value); - } - - // ... DateModified, DateCreated as well - - public IStoragePropertiesCollection Properties { get; } - - public StandardItemViewModel(IStorable storable, IStoragePropertiesCollection properties) : base(storable) - { - this.Properties = properties; - } - - public void - } -``` - -Adds an `ItemPropertiesKind` enum: - -```csharp -public enum ItemPropertiesKind -{ - Standard, - Extended -} -``` - -Amends the IStoragePropertiesCollection interface to include an enum parameter on `GetStoragePropertiesAsync` method: - -```csharp -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace SecureFolderFS.Sdk.Storage.StorageProperties -{ - - public interface IStoragePropertiesCollection - { - DateTime DateCreated { get; } - - - DateTime DateModified { get; } - - - ulong? Size { get; } - - - IAsyncEnumerable GetStoragePropertiesAsync(ItemPropertiesKind propertiesKind = ItemPropertiesKind.Standard, CancellationToken cancellationToken = default); - } -} -``` - diff --git a/docs/rich-commands.md b/docs/rich-commands.md deleted file mode 100644 index 35f230849088..000000000000 --- a/docs/rich-commands.md +++ /dev/null @@ -1,157 +0,0 @@ -# Concept of Rich Commands - -> [!INFO] -> Write down here - -# Commands - -This is the list of all commands defined in `CommandCodes` enum except `None`. - -| Category | Name | Label | Description | HotKey | -| ------------------ | ----------------------------------- | ----------------------------------------- | -------------------------------------------------------- | ------------------- | -| Global | OpenHelp | Help | Open online help page in browser | F1 | -| | ToggleFullScreen | FullScreen | Toggle full screen | F11 | -| | EnterCompactOverlay | Enter compact overlay | Enter compact overlay | Ctrl+Alt+Up | -| | ExitCompactOverlay | Exit compact overlay | Exit compact overlay | Ctrl+Alt+Down | -| | ToggleCompactOverlay | Toggle compact overlay | Toggle compact overlay | F12 | -| | Search | Search | Go to search box | Ctrl+F, F3 | -| | SearchUnindexedItems | Search unindexed items | Search for unindexed items | | -| | Redo | Redo | Redo the last file operation | Ctrl+Y | -| | Undo | Undo | Undo the last file operation | Ctrl+Z | -| | EditPath | Edit path | Focus path bar | Ctrl+L, Alt+D | -| Show | ToggleShowHiddenItems | Show hidden items | Toggle whether to show hidden items | Ctrl+H | -| | ToggleShowFileExtensions | Show file extensions | Toggle whether to show file extensions | | -| | TogglePreviewPane | Toggle the preview pane | Toggle whether to show preview pane | Ctrl+P | -| | ToggleSidebar | Toggle the sidebar | Toggle whether to show sidebar | Ctrl+B | -| File System | CopyItem | Copy | Copy item(s) to clipboard | Ctrl+C | -| | CopyPath | Copy path | Copy path of item to clipboard | Ctrl+Shift+C | -| | CutItem | Cut | Cut item(s) to clipboard | Ctrl+X | -| | PasteItemToSelection | Paste | Paste item(s) from clipboard to selected folder | Ctrl+Shift+V | -| | DeleteItem | Delete | Delete item(s) | Delete, Ctrl+D | -| | DeletemeItemPermanently | Delete permanently | Delete item(s) permanently | Shift+Delete | -| | CreateFolder | Folder | Create new folder | | -| | CreateFolderWithSelection | Create folder with selection | Create a folder with the currently selected item(s) | | -| | AddItem | New | Create new item | Ctrl+Shift+N | -| | CreateShortcut | Create shortcut | Create new shortcut(s) to selected item(s) | | -| | CreateShortcutFromDialog | Shortcut | Create new shortcut to any item | | -| | EmptyRecycleBin | Empty Recycle Bin | Empty recycle bin | | -| | FormatDrive | Format... | Open "Format Drive" menu for selected item | | -| | RestoreRecycleBin | Restore | Restore selected item(s) from recycle bin | | -| | RestoreAllRecycleBin | Restore All Items | Restore all items from recycle bin | | -| | OpenItem | Open | Open item(s) | Enter | -| | OpenItemWithApplicationPicker | Open with | Open item(s) with selected application | | -| | OpenParentFolder | Open parent folder | Open parent folder of searched item | | -| | OpenFileLocation | Open file location | Open the item's location | | -| | RefreshItems | Refresh | Refresh page contents | Ctrl+R, F5 | -| | Rename | Rename | Rename selected item | F2 | -| Selection | SelectAll | Select All | Select all items | Ctrl+A | -| | InvertSelection | Invert Selection | Invert item selection | | -| | ClearSelection | Clear Selection | Clear item selection | | -| | ToggleSelect | Toggle Selection | Toggle item selection | Ctrl+Space | -| Share | ShareItem | Share | Share selected file(s) with others | | -| Start | PinToStart | Pin to the Start Menu | Pin item(s) to the Start Menu | | -| | UnpinFromStart | Unpin from the Start Menu | Unpin item(s) from the Start Menu | | -| Sidebar | PinFolderToSidebar | Pin to Sidebar | Pin folder(s) to Sidebar | | -| | UnpinFolderFromSidebar | Unpin from Sidebar | Unpin folder(s) from Sidebar | | -| Backgrounds | SetAsWallpaperBackground | Set as desktop background | Set selected picture as desktop background | | -| | SetAsSlideshowBackground | Set as desktop slideshow | Set selected pictures as desktop slideshow | | -| | SetAsLockscreenBackground | Set as lockscreen background | Set selected picture as lockscreen background | | -| Install | InstallFont | Install | Install selected font(s) | | -| | InstallInfDriver | Install | Install driver(s) using selected inf file(s) | | -| | InstallCertificate | Install | Install selected certificate(s) | | -| Run | RunAsAdmin | Run as administrator | Run selected application as administrator | | -| | RunAsAnotherUser | Run as another user | Run selected application as another user | | -| | RunWithPowershell | Run with PowerShell | Run selected PowerShell script | | -| Preview Popup | LaunchPreviewPopup | Launch preview popup | Launch preview in popup window | Space | -| Archives | CompressIntoArchive | Create archive | Create archive with selected item(s) | | -| | CompressIntoSevenZip | Create _ArchiveName_.7z | Create 7z archive instantly with selected item(s) | | -| | CompressIntoZip | Create _ArchiveName_.zip | Create zip archive instantly with selected item(s) | | -| | DecompressArchive | Extract files | Extract items from selected archive(s) to any folder | Ctrl+E | -| | DecompressArchiveHere | Extract here | Extract items from selected archive(s) to current folder | | -| | DecompressArchiveToChildFolder | Extract to _NewFolderName_ | Extract items from selected archive(s) to new folder | | -| Image Manipulation | RotateLeft | Rotate left | Rotate selected image(s) to the left | | -| | RotateRight | Rotate right | Rotate selected image(s) to the right | | -| Open | OpenInVS | Visual Studio | Open the current directory in Visual Studio | | -| | OpenInVSCode | VS Code | Open the current directory in Visual Studio Code | | -| | OpenProperties | Open properties | Open properties window | Alt+Enter | -| | OpenSettings | Settings | Open settings page | Ctrl+, | -| | OpenTerminal | Open in terminal | Open folder in terminal | Ctrl+\` | -| | OpenTerminalAsAdmin | Open in terminal as administrator | Open folder in terminal as administrator | Ctrl+Shift+\` | -| | OpenCommandPalette | Command palette | Open command palette | Ctrl+Shift+P | -| Layout | LayoutDecreaseSize | Decrease size | Decrease icon size in grid view | Ctrl+- | -| | LayoutIncreaseSize | Increase size | Increase icon size in grid view | Ctrl++ | -| | LayoutDetails | Details | Switch to details view | Ctrl+Shift+1 | -| | LayoutTiles | Tiles | Switch to tiles view | Ctrl+Shift+2 | -| | LayoutGridSmall | Small Icons | Switch to grid view with small icons | Ctrl+Shift+3 | -| | LayoutGridMedium | Medium Icons | Switch to grid view with medium icons | Ctrl+Shift+4 | -| | LayoutGridLarge | Large Icons | Switch to grid view with large icons | Ctrl+Shift+5 | -| | LayoutColumns | Columns | Switch to columns view | Ctrl+Shift+6 | -| | LayoutAdaptive | Adaptive | Switch views adaptively | Ctrl+Shift+7 | -| Sort by | SortByName | Name | Sort items by name | | -| | SortByDateModified | Date modified | Sort items by date modified | | -| | SortByDateCreated | Date created | Sort items by date created | | -| | SortBySize | Size | Sort items by size | | -| | SortByType | Type | Sort items by type | | -| | SortBySyncStatus | Sync status | Sort items by sync status | | -| | SortByTag | Tags | Sort items by tags | | -| | SortByPath | Path | Sort items by path | | -| | SortByOriginalFolder | Original folder | Sort items by original folder | | -| | SortByDateDeleted | Date deleted | Sort items by date deleted | | -| | SortAscending | Ascending | Sort items in ascending order | | -| | SortDescending | Descending | Sort items in descending order | | -| | ToggleSortDirection | Toggle sort direction | Toggle item sort direction | | -| | ToggleSortDirectoriesAlongsideFiles | List and sort directories alongside files | List and sort directories alongside files | | -| Group by | GroupByNone | None | List items without grouping | | -| | GroupByName | Name | Group items by name | | -| | GroupByDateModified | Date modified | Group items by date modified | | -| | GroupByDateCreated | Date created | Group items by date created | | -| | GroupBySize | Size | Group items by size | | -| | GroupByType | Type | Group items by type | | -| | GroupBySyncStatus | Sync status | Group items by sync status | | -| | GroupByTag | Tags | Group items by tags | | -| | GroupByOriginalFolder | Original folder | Group items by original folder | | -| | GroupByDateDeleted | Date deleted | Group items by date deleted | | -| | GroupByFolderPath | Folder path | Group items by folder path | | -| | GroupByDateModifiedYear | Year | Group items by year of date modified | | -| | GroupByDateModifiedMonth | Month | Group items by month of date modified | | -| | GroupByDateModifiedDay | Day | Group items by day of date modified | | -| | GroupByDateCreatedYear | Year | Group items by year of date created | | -| | GroupByDateCreatedMonth | Month | Group items by month of date created | | -| | GroupByDateCreatedDay | Day | Group items by day of date created | | -| | GroupByDateDeletedYear | Year | Group items by year of date deleted | | -| | GroupByDateDeletedMonth | Month | Group items by month of date deleted | | -| | GroupByDateDeletedDay | Day | Group items by day of date deleted | | -| | GroupAscending | Ascending | Sort groups in ascending order | | -| | GroupDescending | Descending | Sort groups in descending order | | -| | ToggleGroupDirection | Toggle sort direction | Toggle group sort direction | | -| | GroupByYear | Year | Group items by year | | -| | GroupByMonth | Month | Group items by month | | -| | ToggleGroupByDateUnit | Toggle grouping unit | Toggle unit for grouping by date | | -| Navigation | NewTab | New tab | Open new tab | Ctrl+T | -| | NavigateBack | Back | Navigate backward in navigation history | Alt+Left, Backspace | -| | NavigateForward | Forward | Navigate forward in navigation history | Alt+Right | -| | NavigateUp | Up | Navigate up one directory | Alt+Up | -| Other | DuplicateCurrentTab | Duplicate tab | Duplicate current tab | | -| | DuplicateSelectedTab | Duplicate tab | Duplicate selected tab | Ctrl+Shift+K | -| | CloseTabsToTheLeftCurrent | Close tabs to the left | Close tabs to the left of current tab | | -| | CloseTabsToTheLeftSelected | Close tabs to the left | Close tabs to the left of selected tab | | -| | CloseTabsToTheRightCurrent | Close tabs to the right | Close tabs to the right of current tab | | -| | CloseTabsToTheRightSelected | Close tabs to the right | Close tabs to the right of selected tab | | -| | CloseOtherTabsCurrent | Close other tabs | Close tabs other than current tab | | -| | CloseOtherTabsSelected | Close other tabs | Close tabs other than selected tab | | -| | OpenDirectoryInNewPane | Open in new pane | Open directory in new pane | | -| | OpenDirectoryInNewTab | Open in new tab | Open directory in new tab | | -| | OpenInNewWindowItem | Open in new window | Open directory in new window | | -| | ReopenClosedTab | Reopen closed tab | Reopen last closed tab | Ctrl+Shift+T | -| | PreviousTab | Moves to the previous tab | Move to the previous tab | Ctrl+Shift+Tab | -| | NextTab | Moves to the next tab | Move to the next tab | Ctrl+Tab | -| | CloseSelectedTab | Closes current tab | Close current tab | Ctrl+W, Ctrl+F4 | -| | OpenNewPane | New pane | Open new pane | Alt+Shift++ | -| | ClosePane | Close pane | Close right pane | Ctrl+Shift+W | -| Play | PlayAll | Play all | Play the selected media files | | -| Git | GitFetch | Fetch | Run git fetch | | -| | GitInit | Initialize repo | Initialize a Git repository | | -| | GitPull | Pull | Run git pull | | -| | GitPush | Push | Run git push | | -| | GitSync | Sync | Run git pull and then git push | | -| Tags | OpenAllTaggedItems | Open all | Open all tagged items | | diff --git a/docs/spec-template.md b/docs/spec-template.md deleted file mode 100644 index ab9e21f9ca19..000000000000 --- a/docs/spec-template.md +++ /dev/null @@ -1,76 +0,0 @@ - - - - -# Background - - - - - - - - - - - -# Description - - - -# Examples - - - - - - - -# Remarks - - - - -# API Notes - - - - -# API Details - - -# Appendix - \ No newline at end of file diff --git a/docs/storage-layer.md b/docs/storage-layer.md deleted file mode 100644 index ac2ee59c601e..000000000000 --- a/docs/storage-layer.md +++ /dev/null @@ -1,74 +0,0 @@ -# A New Era for the Storage Layer in Files App - -## Background - - - - - - -Presently, it is common for users to browse a large folder of storage items, but only interact with a small subset of them, if any. Relatively recent expansions to the type of filesystem APIs allowed within an `AppContainer` (low-IL) context have improved the app experience for users by many orders of magnitude. Notably, telemetry data implies that app session durations are not only longer but also more inline with user expectations. Over the last three years, contributors have used this insight to justify significant investment into how Files enumerates storage items from the filesystem. - -One of the most consequential parts of this effort was the distinction between standard and extended item properties. Standard properties are those that Files can access quickly using the `FindFirstFileExFromApp()` method, and extended properties are those that Files cannot access quickly due to the required overhead of constructing an `IStorageItem` instance. Luckily, transitioning away from the bulk enumeration methods on `StorageFolder` resulted in significant performance improvements; a sizable reduction in memory usage; and the learning that users, at any given moment, are *almost never* concerned with every single storage item in the working directory-let alone every single property of each item in the viewport. - -Thus, the decision was made that extended properties like the thumbnail, display type, and others should have their value availability deferred until the respective item container is scrolled into the viewport. While not all layout modes at the time included an `ItemsControl` such as `ListView` or `GridView`, this *lazy loading* scheme was enabled by particular UI virtualization events on the controls, which fire when the user scrolls new items into the viewport. - -The Files developers are of the opinion that further improvements can be made to the storage layer. - -## Motivation -The current storage layer that includes these prior improvements is very loosely organized, but one of its major characteristics is a largely unmaintainable monolith that lacks sound architecture patterns. Many intended concerns of this so-called `ItemViewModel` are poorly defined for the demands of a flexible, consistent and extensible storage layer. - -Community ambitions to deliver a high-quality file manager experience wherever users may be also catalyze this effort, for the necessary ability to display items from multiple non-native sources requires careful planning. - -Lastly, the current *lazy loading* pattern was introduced at a time before all layout modes used `ItemsControl`s, and thus could not take advantage of virtualization at the data source level. - -## Description - - - -## Examples - - - - - - - -## Remarks - - - - -## API Notes - - - - -## API Details - - -## Appendix - \ No newline at end of file diff --git a/global.json b/global.json new file mode 100644 index 000000000000..9e04c2aca53c --- /dev/null +++ b/global.json @@ -0,0 +1,9 @@ +{ + "sdk": { + "version": "10.0.102", + "rollForward": "latestMajor" + }, + "test": { + "runner": "Microsoft.Testing.Platform" + } +} \ No newline at end of file diff --git a/nuget.config b/nuget.config index 08506035a9a6..7a61847c8f24 100644 --- a/nuget.config +++ b/nuget.config @@ -1,7 +1,21 @@  - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + diff --git a/scripts/Configure-AppxManifest.ps1 b/scripts/Configure-AppxManifest.ps1 deleted file mode 100644 index 7f7bf132b2b1..000000000000 --- a/scripts/Configure-AppxManifest.ps1 +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright (c) 2024 Files Community -# Licensed under the MIT License. See the LICENSE. - -param( - [string]$Branch = "", - [string]$PackageManifestPath = "", - [string]$Publisher = "", - [string]$WorkingDir = "", - [string]$SecretBingMapsKey = "", - [string]$SecretSentry = "", - [string]$SecretGitHubOAuthClientId = "" -) - -[xml]$xmlDoc = Get-Content $PackageManifestPath -$xmlDoc.Package.Identity.Publisher = $Publisher - -if ($Branch -eq "Preview") -{ - # Set identities - $xmlDoc.Package.Identity.Name="FilesPreview" - $xmlDoc.Package.Properties.DisplayName="Files - Preview" - $xmlDoc.Package.Applications.Application.VisualElements.DisplayName="Files - Preview" - $xmlDoc.Save($PackageManifestPath) - - Get-ChildItem $WorkingDir -Include *.csproj, *.appxmanifest, *.wapproj, *.xaml -recurse | ForEach-Object -Process ` - { ` - (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "Assets\\AppTiles\\Dev", "Assets\AppTiles\Preview" }) | ` - Set-Content $_ -NoNewline ` - } -} -elseif ($Branch -eq "Stable") -{ - # Set identities - $xmlDoc.Package.Identity.Name="Files" - $xmlDoc.Package.Properties.DisplayName="Files" - $xmlDoc.Package.Applications.Application.VisualElements.DisplayName="Files" - $xmlDoc.Save($PackageManifestPath) - - Get-ChildItem $WorkingDir -Include *.csproj, *.appxmanifest, *.wapproj, *.xaml -recurse | ForEach-Object -Process ` - { ` - (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "Assets\\AppTiles\\Dev", "Assets\AppTiles\Release" }) | ` - Set-Content $_ -NoNewline ` - } -} -elseif ($Branch -eq "Store") -{ - # Set identities - $xmlDoc.Package.Identity.Name="49306atecsolution.FilesUWP" - $xmlDoc.Package.Properties.DisplayName="Files" - $xmlDoc.Package.Applications.Application.VisualElements.DisplayName="Files" - - # Remove an capability that is used for the sideload - $nsmgr = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable) - $nsmgr.AddNamespace("pkg", "http://schemas.microsoft.com/appx/manifest/foundation/windows10") - $nsmgr.AddNamespace("rescap", "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities") - $pm = $xmlDoc.SelectSingleNode("/pkg:Package/pkg:Capabilities/rescap:Capability[@Name='packageManagement']", $nsmgr) - $xmlDoc.Package.Capabilities.RemoveChild($pm) - $xmlDoc.Save($PackageManifestPath) - - Get-ChildItem $WorkingDir -Include *.csproj, *.appxmanifest, *.wapproj, *.xaml -recurse | ForEach-Object -Process ` - { ` - (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "Assets\\AppTiles\\Dev", "Assets\AppTiles\Release" }) | ` - Set-Content $_ -NoNewline ` - } -} - -Get-ChildItem $WorkingDir -Include *.cs -recurse | ForEach-Object -Process ` -{ ` - (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "bingmapskey.secret", "$SecretBingMapsKey" }) | ` - Set-Content $_ -NoNewline ` -} - -Get-ChildItem $WorkingDir -Include *.cs -recurse | ForEach-Object -Process ` -{ - (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "sentry.secret", "$SecretSentry" }) | ` - Set-Content $_ -NoNewline ` -} - -Get-ChildItem $WorkingDir -Include *.cs -recurse | ForEach-Object -Process ` -{ ` - (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "githubclientid.secret", "$SecretGitHubOAuthClientId" }) | ` - Set-Content $_ -NoNewline ` -} diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/BadgeLogo.scale-100.png b/src/Files.App (Package)/Assets/AppTiles/Dev/BadgeLogo.scale-100.png deleted file mode 100644 index 351944cbf1b6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/BadgeLogo.scale-100.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/BadgeLogo.scale-125.png b/src/Files.App (Package)/Assets/AppTiles/Dev/BadgeLogo.scale-125.png deleted file mode 100644 index f4b05dcaea14..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/BadgeLogo.scale-125.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/BadgeLogo.scale-150.png b/src/Files.App (Package)/Assets/AppTiles/Dev/BadgeLogo.scale-150.png deleted file mode 100644 index e1733477d441..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/BadgeLogo.scale-150.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/BadgeLogo.scale-200.png b/src/Files.App (Package)/Assets/AppTiles/Dev/BadgeLogo.scale-200.png deleted file mode 100644 index 6dda95fb62ef..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/BadgeLogo.scale-200.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/BadgeLogo.scale-400.png b/src/Files.App (Package)/Assets/AppTiles/Dev/BadgeLogo.scale-400.png deleted file mode 100644 index d2d094cf6464..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/BadgeLogo.scale-400.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Large310x310Logo.scale-100.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Large310x310Logo.scale-100.png deleted file mode 100644 index f8a50cce7baa..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Large310x310Logo.scale-100.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Large310x310Logo.scale-125.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Large310x310Logo.scale-125.png deleted file mode 100644 index 8f1299e0d788..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Large310x310Logo.scale-125.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Large310x310Logo.scale-150.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Large310x310Logo.scale-150.png deleted file mode 100644 index 94b7baea95d2..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Large310x310Logo.scale-150.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Large310x310Logo.scale-200.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Large310x310Logo.scale-200.png deleted file mode 100644 index 0b99c11c7960..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Large310x310Logo.scale-200.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Large310x310Logo.scale-400.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Large310x310Logo.scale-400.png deleted file mode 100644 index 850d7005bc07..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Large310x310Logo.scale-400.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Logo.ico b/src/Files.App (Package)/Assets/AppTiles/Dev/Logo.ico deleted file mode 100644 index 8a06d1fe72f4..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Logo.ico and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Small71x71Logo.scale-100.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Small71x71Logo.scale-100.png deleted file mode 100644 index 092db21d01d7..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Small71x71Logo.scale-100.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Small71x71Logo.scale-125.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Small71x71Logo.scale-125.png deleted file mode 100644 index 845222a45341..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Small71x71Logo.scale-125.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Small71x71Logo.scale-150.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Small71x71Logo.scale-150.png deleted file mode 100644 index f67e168b764d..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Small71x71Logo.scale-150.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Small71x71Logo.scale-200.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Small71x71Logo.scale-200.png deleted file mode 100644 index 381a8165ae2c..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Small71x71Logo.scale-200.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Small71x71Logo.scale-400.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Small71x71Logo.scale-400.png deleted file mode 100644 index eee15bac41b2..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Small71x71Logo.scale-400.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/SplashScreen.scale-100.png b/src/Files.App (Package)/Assets/AppTiles/Dev/SplashScreen.scale-100.png deleted file mode 100644 index 48f7a0de877e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/SplashScreen.scale-100.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/SplashScreen.scale-125.png b/src/Files.App (Package)/Assets/AppTiles/Dev/SplashScreen.scale-125.png deleted file mode 100644 index b136e02d3c3f..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/SplashScreen.scale-125.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/SplashScreen.scale-150.png b/src/Files.App (Package)/Assets/AppTiles/Dev/SplashScreen.scale-150.png deleted file mode 100644 index 4ac06a52eb7b..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/SplashScreen.scale-150.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/SplashScreen.scale-200.png b/src/Files.App (Package)/Assets/AppTiles/Dev/SplashScreen.scale-200.png deleted file mode 100644 index c3a4746a36ff..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/SplashScreen.scale-200.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/SplashScreen.scale-400.png b/src/Files.App (Package)/Assets/AppTiles/Dev/SplashScreen.scale-400.png deleted file mode 100644 index f5020d791b15..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/SplashScreen.scale-400.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square150x150Logo.scale-100.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square150x150Logo.scale-100.png deleted file mode 100644 index c8d58a4d8a4c..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square150x150Logo.scale-100.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square150x150Logo.scale-125.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square150x150Logo.scale-125.png deleted file mode 100644 index 2962f54962cc..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square150x150Logo.scale-125.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square150x150Logo.scale-150.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square150x150Logo.scale-150.png deleted file mode 100644 index 8f9dd261a645..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square150x150Logo.scale-150.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square150x150Logo.scale-200.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square150x150Logo.scale-200.png deleted file mode 100644 index f160cd780f4c..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square150x150Logo.scale-200.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square150x150Logo.scale-400.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square150x150Logo.scale-400.png deleted file mode 100644 index 2b4c434e0ec6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square150x150Logo.scale-400.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.scale-100.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.scale-100.png deleted file mode 100644 index 761b31d34df7..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.scale-100.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.scale-125.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.scale-125.png deleted file mode 100644 index 790204d92501..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.scale-125.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.scale-150.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.scale-150.png deleted file mode 100644 index c6a35c6dffc2..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.scale-150.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.scale-200.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.scale-200.png deleted file mode 100644 index 4dad4068236e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.scale-200.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.scale-400.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.scale-400.png deleted file mode 100644 index 0108055c719f..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.scale-400.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-16.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-16.png deleted file mode 100644 index 4daa93646383..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-16.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-16_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-16_altform-lightunplated_theme-light.png deleted file mode 100644 index 4daa93646383..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-16_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-16_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-16_altform-unplated.png deleted file mode 100644 index 4daa93646383..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-16_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-20.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-20.png deleted file mode 100644 index 080414f506d8..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-20.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-20_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-20_altform-lightunplated_theme-light.png deleted file mode 100644 index 080414f506d8..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-20_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-20_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-20_altform-unplated.png deleted file mode 100644 index 080414f506d8..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-20_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-24.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-24.png deleted file mode 100644 index d9a455363d67..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-24.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-24_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-24_altform-lightunplated_theme-light.png deleted file mode 100644 index d9a455363d67..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-24_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-24_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-24_altform-unplated.png deleted file mode 100644 index d9a455363d67..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-24_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-256.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-256.png deleted file mode 100644 index d13d13b124c8..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-256.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-256_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-256_altform-lightunplated_theme-light.png deleted file mode 100644 index d13d13b124c8..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-256_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-256_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-256_altform-unplated.png deleted file mode 100644 index d13d13b124c8..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-256_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-30.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-30.png deleted file mode 100644 index c91d42f83f13..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-30.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-30_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-30_altform-lightunplated_theme-light.png deleted file mode 100644 index c91d42f83f13..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-30_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-30_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-30_altform-unplated.png deleted file mode 100644 index c91d42f83f13..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-30_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-32.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-32.png deleted file mode 100644 index e99d8d483fc8..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-32.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-32_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-32_altform-lightunplated_theme-light.png deleted file mode 100644 index e99d8d483fc8..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-32_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-32_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-32_altform-unplated.png deleted file mode 100644 index e99d8d483fc8..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-32_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-36.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-36.png deleted file mode 100644 index c74cca3e1ece..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-36.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-36_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-36_altform-lightunplated_theme-light.png deleted file mode 100644 index c74cca3e1ece..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-36_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-36_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-36_altform-unplated.png deleted file mode 100644 index c74cca3e1ece..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-36_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-40.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-40.png deleted file mode 100644 index 52a986595820..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-40.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-40_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-40_altform-lightunplated_theme-light.png deleted file mode 100644 index 52a986595820..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-40_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-40_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-40_altform-unplated.png deleted file mode 100644 index 52a986595820..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-40_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-48.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-48.png deleted file mode 100644 index 11a9708fa307..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-48.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-48_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-48_altform-lightunplated_theme-light.png deleted file mode 100644 index 11a9708fa307..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-48_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-48_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-48_altform-unplated.png deleted file mode 100644 index 11a9708fa307..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-48_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-60.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-60.png deleted file mode 100644 index 4006de341321..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-60.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-60_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-60_altform-lightunplated_theme-light.png deleted file mode 100644 index 4006de341321..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-60_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-60_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-60_altform-unplated.png deleted file mode 100644 index 4006de341321..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-60_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-64.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-64.png deleted file mode 100644 index b1b32e67758d..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-64.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-64_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-64_altform-lightunplated_theme-light.png deleted file mode 100644 index b1b32e67758d..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-64_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-64_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-64_altform-unplated.png deleted file mode 100644 index b1b32e67758d..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-64_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-72.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-72.png deleted file mode 100644 index 95156bfbc7d2..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-72.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-72_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-72_altform-lightunplated_theme-light.png deleted file mode 100644 index 95156bfbc7d2..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-72_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-72_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-72_altform-unplated.png deleted file mode 100644 index 95156bfbc7d2..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-72_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-80.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-80.png deleted file mode 100644 index 234968c58dae..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-80.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-80_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-80_altform-lightunplated_theme-light.png deleted file mode 100644 index 234968c58dae..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-80_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-80_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-80_altform-unplated.png deleted file mode 100644 index 234968c58dae..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-80_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-96.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-96.png deleted file mode 100644 index 33c5167b8c89..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-96.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-96_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-96_altform-lightunplated_theme-light.png deleted file mode 100644 index 33c5167b8c89..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-96_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-96_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-96_altform-unplated.png deleted file mode 100644 index 33c5167b8c89..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Square44x44Logo.targetsize-96_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/StoreLogo.scale-100.png b/src/Files.App (Package)/Assets/AppTiles/Dev/StoreLogo.scale-100.png deleted file mode 100644 index 299d023e1a69..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/StoreLogo.scale-100.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/StoreLogo.scale-125.png b/src/Files.App (Package)/Assets/AppTiles/Dev/StoreLogo.scale-125.png deleted file mode 100644 index 5a399debf0c5..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/StoreLogo.scale-125.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/StoreLogo.scale-150.png b/src/Files.App (Package)/Assets/AppTiles/Dev/StoreLogo.scale-150.png deleted file mode 100644 index c3345e26d657..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/StoreLogo.scale-150.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/StoreLogo.scale-200.png b/src/Files.App (Package)/Assets/AppTiles/Dev/StoreLogo.scale-200.png deleted file mode 100644 index 953c83725052..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/StoreLogo.scale-200.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/StoreLogo.scale-400.png b/src/Files.App (Package)/Assets/AppTiles/Dev/StoreLogo.scale-400.png deleted file mode 100644 index 765b2e191cd3..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/StoreLogo.scale-400.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Wide310x150Logo.scale-100.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Wide310x150Logo.scale-100.png deleted file mode 100644 index 299cdc4d2576..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Wide310x150Logo.scale-100.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Wide310x150Logo.scale-125.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Wide310x150Logo.scale-125.png deleted file mode 100644 index f78eaab8eb25..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Wide310x150Logo.scale-125.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Wide310x150Logo.scale-150.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Wide310x150Logo.scale-150.png deleted file mode 100644 index 749e3405ec9f..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Wide310x150Logo.scale-150.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Wide310x150Logo.scale-200.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Wide310x150Logo.scale-200.png deleted file mode 100644 index 294e237acfda..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Wide310x150Logo.scale-200.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/Wide310x150Logo.scale-400.png b/src/Files.App (Package)/Assets/AppTiles/Dev/Wide310x150Logo.scale-400.png deleted file mode 100644 index 1946b30bc7d9..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/Wide310x150Logo.scale-400.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-100_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-100_contrast-black.png deleted file mode 100644 index 38d7cb281d44..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-100_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-125_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-125_contrast-black.png deleted file mode 100644 index 757fc1e369da..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-125_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-150_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-150_contrast-black.png deleted file mode 100644 index 61688eed72fd..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-150_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-200_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-200_contrast-black.png deleted file mode 100644 index 89f6a3af93d3..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-200_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-400_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-400_contrast-black.png deleted file mode 100644 index cd7f4284a0cc..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-400_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-100_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-100_contrast-black.png deleted file mode 100644 index 1c424d552493..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-100_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-125_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-125_contrast-black.png deleted file mode 100644 index 4a90f644867c..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-125_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-150_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-150_contrast-black.png deleted file mode 100644 index 40e3ab1d7a79..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-150_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-200_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-200_contrast-black.png deleted file mode 100644 index b2f1f75e03d2..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-200_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-400_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-400_contrast-black.png deleted file mode 100644 index be7244dc105d..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-400_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-100_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-100_contrast-black.png deleted file mode 100644 index 2fb47ef76d99..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-100_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-125_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-125_contrast-black.png deleted file mode 100644 index 399b95e21b4a..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-125_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-150_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-150_contrast-black.png deleted file mode 100644 index 6c170eee4ef7..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-150_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-200_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-200_contrast-black.png deleted file mode 100644 index f511f0ebf26b..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-200_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-400_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-400_contrast-black.png deleted file mode 100644 index e0c2a27a21f8..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-400_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-100_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-100_contrast-black.png deleted file mode 100644 index d79a8cad05be..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-100_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-125_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-125_contrast-black.png deleted file mode 100644 index fdef48605b89..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-125_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-150_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-150_contrast-black.png deleted file mode 100644 index 8c8efdc333da..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-150_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-200_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-200_contrast-black.png deleted file mode 100644 index 69b3f5dffd13..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-200_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-400_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-400_contrast-black.png deleted file mode 100644 index 6b189407ef61..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-400_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-100_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-100_contrast-black.png deleted file mode 100644 index 7fa472f50cab..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-100_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-125_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-125_contrast-black.png deleted file mode 100644 index 7427bbeb49fc..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-125_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-150_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-150_contrast-black.png deleted file mode 100644 index 8ee8dfbacb5d..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-150_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-200_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-200_contrast-black.png deleted file mode 100644 index 3ae1963eba57..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-200_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-400_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-400_contrast-black.png deleted file mode 100644 index 80ad7602890b..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-400_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-100_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-100_contrast-black.png deleted file mode 100644 index b045d3cadf20..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-100_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-125_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-125_contrast-black.png deleted file mode 100644 index 257cc79f88b1..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-125_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-150_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-150_contrast-black.png deleted file mode 100644 index f8b4a150484e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-150_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-200_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-200_contrast-black.png deleted file mode 100644 index b3bbc5db1ecb..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-200_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-400_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-400_contrast-black.png deleted file mode 100644 index 7fee13cf19c9..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-400_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-black.png deleted file mode 100644 index 1a0c3e0ded83..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-16_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-16_altform-unplated_contrast-black.png deleted file mode 100644 index 1a0c3e0ded83..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-16_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-16_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-16_contrast-black.png deleted file mode 100644 index 1a0c3e0ded83..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-16_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-black.png deleted file mode 100644 index 83d62ee6342e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-20_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-20_altform-unplated_contrast-black.png deleted file mode 100644 index 83d62ee6342e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-20_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-20_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-20_contrast-black.png deleted file mode 100644 index 83d62ee6342e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-20_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-black.png deleted file mode 100644 index 38d7cb281d44..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-24_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-24_altform-unplated_contrast-black.png deleted file mode 100644 index 38d7cb281d44..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-24_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-24_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-24_contrast-black.png deleted file mode 100644 index 38d7cb281d44..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-24_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-black.png deleted file mode 100644 index f1d2cffcd423..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-256_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-256_altform-unplated_contrast-black.png deleted file mode 100644 index ee2bd034b24c..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-256_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-256_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-256_contrast-black.png deleted file mode 100644 index 72255dfd28a8..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-256_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-black.png deleted file mode 100644 index 805348ebb2f6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-30_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-30_altform-unplated_contrast-black.png deleted file mode 100644 index 805348ebb2f6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-30_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-30_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-30_contrast-black.png deleted file mode 100644 index 805348ebb2f6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-30_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-black.png deleted file mode 100644 index f5d939824b24..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-32_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-32_altform-unplated_contrast-black.png deleted file mode 100644 index f5d939824b24..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-32_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-32_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-32_contrast-black.png deleted file mode 100644 index f5d939824b24..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-32_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-black.png deleted file mode 100644 index 1da028374f93..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-36_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-36_altform-unplated_contrast-black.png deleted file mode 100644 index 1da028374f93..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-36_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-36_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-36_contrast-black.png deleted file mode 100644 index 1da028374f93..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-36_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-black.png deleted file mode 100644 index 1da9611df118..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-40_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-40_altform-unplated_contrast-black.png deleted file mode 100644 index 1da9611df118..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-40_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-40_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-40_contrast-black.png deleted file mode 100644 index 1da9611df118..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-40_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-black.png deleted file mode 100644 index a3b7faf2aa23..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-48_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-48_altform-unplated_contrast-black.png deleted file mode 100644 index a3b7faf2aa23..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-48_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-48_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-48_contrast-black.png deleted file mode 100644 index a3b7faf2aa23..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-48_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-black.png deleted file mode 100644 index 322bf0f3a7e3..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-60_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-60_altform-unplated_contrast-black.png deleted file mode 100644 index 7a6220a3b1db..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-60_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-60_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-60_contrast-black.png deleted file mode 100644 index 986d0542b1d5..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-60_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-black.png deleted file mode 100644 index 418dfd97ea5a..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-64_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-64_altform-unplated_contrast-black.png deleted file mode 100644 index 654000a9c50d..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-64_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-64_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-64_contrast-black.png deleted file mode 100644 index a438fc518eac..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-64_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-black.png deleted file mode 100644 index 04f03e9f8d88..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-72_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-72_altform-unplated_contrast-black.png deleted file mode 100644 index 2a1abab7e925..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-72_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-72_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-72_contrast-black.png deleted file mode 100644 index a59504826746..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-72_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-black.png deleted file mode 100644 index 0a59b989edf4..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-80_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-80_altform-unplated_contrast-black.png deleted file mode 100644 index f1794d8592e3..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-80_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-80_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-80_contrast-black.png deleted file mode 100644 index adb3c70c2895..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-80_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-black.png deleted file mode 100644 index e941cf018c08..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-96_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-96_altform-unplated_contrast-black.png deleted file mode 100644 index 9259d463d895..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-96_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-96_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-96_contrast-black.png deleted file mode 100644 index 2386b1842ad2..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-96_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-100_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-100_contrast-black.png deleted file mode 100644 index 487dc85b9f59..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-100_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-125_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-125_contrast-black.png deleted file mode 100644 index c40da56778a2..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-125_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-150_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-150_contrast-black.png deleted file mode 100644 index 958bd811a4bf..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-150_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-200_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-200_contrast-black.png deleted file mode 100644 index 8ea5a537bd46..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-200_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-400_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-400_contrast-black.png deleted file mode 100644 index 74ef83d6f2c7..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-400_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-100_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-100_contrast-black.png deleted file mode 100644 index 01348e2ab950..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-100_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-125_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-125_contrast-black.png deleted file mode 100644 index f4683afe6836..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-125_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-150_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-150_contrast-black.png deleted file mode 100644 index 86fa119c09b5..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-150_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-200_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-200_contrast-black.png deleted file mode 100644 index d46bda87ba37..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-200_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-400_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-400_contrast-black.png deleted file mode 100644 index 4db12beaf62a..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-400_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-100_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-100_contrast-white.png deleted file mode 100644 index 80e5e8ed63a6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-100_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-125_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-125_contrast-white.png deleted file mode 100644 index 26e954f41295..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-125_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-150_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-150_contrast-white.png deleted file mode 100644 index c010357864be..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-150_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-200_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-200_contrast-white.png deleted file mode 100644 index 6e8af9597e13..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-200_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-400_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-400_contrast-white.png deleted file mode 100644 index 004c4e5fb891..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-400_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-100_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-100_contrast-white.png deleted file mode 100644 index ac66939cf8aa..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-100_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-125_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-125_contrast-white.png deleted file mode 100644 index 841202cf69fb..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-125_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-150_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-150_contrast-white.png deleted file mode 100644 index 50a11e1d1fa0..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-150_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-200_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-200_contrast-white.png deleted file mode 100644 index f16187b13a3b..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-200_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-400_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-400_contrast-white.png deleted file mode 100644 index ae147eb8de70..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-400_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-100_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-100_contrast-white.png deleted file mode 100644 index 9e48f54e2177..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-100_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-125_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-125_contrast-white.png deleted file mode 100644 index 4ffe2dd37fe1..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-125_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-150_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-150_contrast-white.png deleted file mode 100644 index 2c63f1fa3f91..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-150_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-200_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-200_contrast-white.png deleted file mode 100644 index d69fd8ed7b04..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-200_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-400_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-400_contrast-white.png deleted file mode 100644 index e5b30a09882a..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-400_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-100_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-100_contrast-white.png deleted file mode 100644 index b4e8e01e6d2e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-100_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-125_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-125_contrast-white.png deleted file mode 100644 index 1b913fd69a74..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-125_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-150_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-150_contrast-white.png deleted file mode 100644 index c6dca65f5a77..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-150_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-200_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-200_contrast-white.png deleted file mode 100644 index 84ca6ca7247a..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-200_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-400_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-400_contrast-white.png deleted file mode 100644 index f8c71e25857f..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-400_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-100_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-100_contrast-white.png deleted file mode 100644 index 614f618fa735..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-100_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-125_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-125_contrast-white.png deleted file mode 100644 index 0b038b1232d0..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-125_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-150_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-150_contrast-white.png deleted file mode 100644 index 2c46f4928683..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-150_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-200_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-200_contrast-white.png deleted file mode 100644 index 385ea5e6f84b..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-200_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-400_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-400_contrast-white.png deleted file mode 100644 index 0f9a6e54dd41..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-400_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-100_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-100_contrast-white.png deleted file mode 100644 index 4f31c13c440d..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-100_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-125_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-125_contrast-white.png deleted file mode 100644 index 681f3075a814..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-125_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-150_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-150_contrast-white.png deleted file mode 100644 index 5997065e1d6a..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-150_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-200_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-200_contrast-white.png deleted file mode 100644 index 90fb690f3c44..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-200_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-400_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-400_contrast-white.png deleted file mode 100644 index 67da6da2660c..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-400_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-white.png deleted file mode 100644 index a2ac9c5fdf41..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-16_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-16_altform-unplated_contrast-white.png deleted file mode 100644 index a2ac9c5fdf41..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-16_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-16_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-16_contrast-white.png deleted file mode 100644 index a2ac9c5fdf41..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-16_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-white.png deleted file mode 100644 index beba47a931d6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-20_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-20_altform-unplated_contrast-white.png deleted file mode 100644 index beba47a931d6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-20_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-20_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-20_contrast-white.png deleted file mode 100644 index beba47a931d6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-20_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-white.png deleted file mode 100644 index 80e5e8ed63a6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-24_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-24_altform-unplated_contrast-white.png deleted file mode 100644 index 80e5e8ed63a6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-24_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-24_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-24_contrast-white.png deleted file mode 100644 index 80e5e8ed63a6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-24_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-white.png deleted file mode 100644 index fe460cab3f97..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-256_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-256_altform-unplated_contrast-white.png deleted file mode 100644 index fe460cab3f97..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-256_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-256_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-256_contrast-white.png deleted file mode 100644 index fe460cab3f97..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-256_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-white.png deleted file mode 100644 index b56dd5cde177..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-30_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-30_altform-unplated_contrast-white.png deleted file mode 100644 index b56dd5cde177..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-30_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-30_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-30_contrast-white.png deleted file mode 100644 index b56dd5cde177..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-30_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-white.png deleted file mode 100644 index b63beead49ab..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-32_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-32_altform-unplated_contrast-white.png deleted file mode 100644 index b63beead49ab..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-32_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-32_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-32_contrast-white.png deleted file mode 100644 index b63beead49ab..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-32_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-white.png deleted file mode 100644 index c013ca9bcf57..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-36_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-36_altform-unplated_contrast-white.png deleted file mode 100644 index c013ca9bcf57..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-36_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-36_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-36_contrast-white.png deleted file mode 100644 index c013ca9bcf57..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-36_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-white.png deleted file mode 100644 index 443f9ad28a15..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-40_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-40_altform-unplated_contrast-white.png deleted file mode 100644 index 443f9ad28a15..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-40_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-40_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-40_contrast-white.png deleted file mode 100644 index 443f9ad28a15..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-40_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-white.png deleted file mode 100644 index 3e3fa2b30a83..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-48_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-48_altform-unplated_contrast-white.png deleted file mode 100644 index 3e3fa2b30a83..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-48_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-48_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-48_contrast-white.png deleted file mode 100644 index 3e3fa2b30a83..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-48_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-white.png deleted file mode 100644 index 9224cb232130..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-60_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-60_altform-unplated_contrast-white.png deleted file mode 100644 index 9224cb232130..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-60_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-60_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-60_contrast-white.png deleted file mode 100644 index 9224cb232130..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-60_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-white.png deleted file mode 100644 index ae45020dcc1c..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-64_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-64_altform-unplated_contrast-white.png deleted file mode 100644 index ae45020dcc1c..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-64_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-64_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-64_contrast-white.png deleted file mode 100644 index ae45020dcc1c..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-64_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-white.png deleted file mode 100644 index 492fbbe1ff37..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-72_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-72_altform-unplated_contrast-white.png deleted file mode 100644 index 492fbbe1ff37..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-72_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-72_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-72_contrast-white.png deleted file mode 100644 index 492fbbe1ff37..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-72_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-white.png deleted file mode 100644 index 3724e54306eb..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-80_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-80_altform-unplated_contrast-white.png deleted file mode 100644 index 3724e54306eb..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-80_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-80_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-80_contrast-white.png deleted file mode 100644 index 3724e54306eb..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-80_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-white.png deleted file mode 100644 index 39599103635e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-96_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-96_altform-unplated_contrast-white.png deleted file mode 100644 index 39599103635e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-96_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-96_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-96_contrast-white.png deleted file mode 100644 index 39599103635e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-96_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-100_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-100_contrast-white.png deleted file mode 100644 index 69ca7984c161..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-100_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-125_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-125_contrast-white.png deleted file mode 100644 index 441680c08dbb..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-125_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-150_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-150_contrast-white.png deleted file mode 100644 index 7d4a742ec205..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-150_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-200_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-200_contrast-white.png deleted file mode 100644 index 2cfd75d7cabe..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-200_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-400_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-400_contrast-white.png deleted file mode 100644 index ed1a5afbd1b6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-400_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-100_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-100_contrast-white.png deleted file mode 100644 index 6a6cb23b1caf..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-100_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-125_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-125_contrast-white.png deleted file mode 100644 index ca2c5e2b67e5..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-125_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-150_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-150_contrast-white.png deleted file mode 100644 index 7c7df0556a13..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-150_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-200_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-200_contrast-white.png deleted file mode 100644 index 97a8567beb60..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-200_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-400_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-400_contrast-white.png deleted file mode 100644 index cc1a45208f4f..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-400_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/BadgeLogo.scale-100.png b/src/Files.App (Package)/Assets/AppTiles/Preview/BadgeLogo.scale-100.png deleted file mode 100644 index 351944cbf1b6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/BadgeLogo.scale-100.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/BadgeLogo.scale-125.png b/src/Files.App (Package)/Assets/AppTiles/Preview/BadgeLogo.scale-125.png deleted file mode 100644 index f4b05dcaea14..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/BadgeLogo.scale-125.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/BadgeLogo.scale-150.png b/src/Files.App (Package)/Assets/AppTiles/Preview/BadgeLogo.scale-150.png deleted file mode 100644 index e1733477d441..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/BadgeLogo.scale-150.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/BadgeLogo.scale-200.png b/src/Files.App (Package)/Assets/AppTiles/Preview/BadgeLogo.scale-200.png deleted file mode 100644 index 6dda95fb62ef..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/BadgeLogo.scale-200.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/BadgeLogo.scale-400.png b/src/Files.App (Package)/Assets/AppTiles/Preview/BadgeLogo.scale-400.png deleted file mode 100644 index d2d094cf6464..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/BadgeLogo.scale-400.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Large310x310Logo.scale-100.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Large310x310Logo.scale-100.png deleted file mode 100644 index cd835d74d53a..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Large310x310Logo.scale-100.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Large310x310Logo.scale-125.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Large310x310Logo.scale-125.png deleted file mode 100644 index 77e0f0ee1959..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Large310x310Logo.scale-125.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Large310x310Logo.scale-150.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Large310x310Logo.scale-150.png deleted file mode 100644 index fe828b802f34..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Large310x310Logo.scale-150.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Large310x310Logo.scale-200.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Large310x310Logo.scale-200.png deleted file mode 100644 index 505a16b790a0..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Large310x310Logo.scale-200.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Large310x310Logo.scale-400.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Large310x310Logo.scale-400.png deleted file mode 100644 index 7aa9a0c27bac..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Large310x310Logo.scale-400.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Logo.ico b/src/Files.App (Package)/Assets/AppTiles/Preview/Logo.ico deleted file mode 100644 index 1a8ba0e14e56..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Logo.ico and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Small71x71Logo.scale-100.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Small71x71Logo.scale-100.png deleted file mode 100644 index a038d33ce979..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Small71x71Logo.scale-100.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Small71x71Logo.scale-125.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Small71x71Logo.scale-125.png deleted file mode 100644 index 12d4f9eb2aa0..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Small71x71Logo.scale-125.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Small71x71Logo.scale-150.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Small71x71Logo.scale-150.png deleted file mode 100644 index 3e5997e1812b..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Small71x71Logo.scale-150.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Small71x71Logo.scale-200.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Small71x71Logo.scale-200.png deleted file mode 100644 index b691b6bb19c1..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Small71x71Logo.scale-200.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Small71x71Logo.scale-400.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Small71x71Logo.scale-400.png deleted file mode 100644 index bc8b5cf8f19e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Small71x71Logo.scale-400.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/SplashScreen.scale-100.png b/src/Files.App (Package)/Assets/AppTiles/Preview/SplashScreen.scale-100.png deleted file mode 100644 index d28d26dddf49..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/SplashScreen.scale-100.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/SplashScreen.scale-125.png b/src/Files.App (Package)/Assets/AppTiles/Preview/SplashScreen.scale-125.png deleted file mode 100644 index 9b7fddb4e90e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/SplashScreen.scale-125.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/SplashScreen.scale-150.png b/src/Files.App (Package)/Assets/AppTiles/Preview/SplashScreen.scale-150.png deleted file mode 100644 index a244f01789fb..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/SplashScreen.scale-150.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/SplashScreen.scale-200.png b/src/Files.App (Package)/Assets/AppTiles/Preview/SplashScreen.scale-200.png deleted file mode 100644 index e2e8120f2094..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/SplashScreen.scale-200.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/SplashScreen.scale-400.png b/src/Files.App (Package)/Assets/AppTiles/Preview/SplashScreen.scale-400.png deleted file mode 100644 index e7884bdc9e5a..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/SplashScreen.scale-400.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square150x150Logo.scale-100.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square150x150Logo.scale-100.png deleted file mode 100644 index 3b1cd8fab964..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square150x150Logo.scale-100.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square150x150Logo.scale-125.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square150x150Logo.scale-125.png deleted file mode 100644 index bda6c20fc7e3..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square150x150Logo.scale-125.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square150x150Logo.scale-150.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square150x150Logo.scale-150.png deleted file mode 100644 index 422d8f111620..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square150x150Logo.scale-150.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square150x150Logo.scale-200.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square150x150Logo.scale-200.png deleted file mode 100644 index ac8ef1e35113..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square150x150Logo.scale-200.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square150x150Logo.scale-400.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square150x150Logo.scale-400.png deleted file mode 100644 index ddd851b73415..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square150x150Logo.scale-400.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.scale-100.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.scale-100.png deleted file mode 100644 index 7ccac70ae0d7..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.scale-100.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.scale-125.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.scale-125.png deleted file mode 100644 index 35a045a3d2aa..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.scale-125.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.scale-150.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.scale-150.png deleted file mode 100644 index 2b92b5f9e76e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.scale-150.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.scale-200.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.scale-200.png deleted file mode 100644 index 4b494ce6acdc..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.scale-200.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.scale-400.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.scale-400.png deleted file mode 100644 index c10f9fe1f91d..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.scale-400.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-16.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-16.png deleted file mode 100644 index bcbc69f59f75..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-16.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-16_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-16_altform-lightunplated_theme-light.png deleted file mode 100644 index bcbc69f59f75..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-16_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-16_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-16_altform-unplated.png deleted file mode 100644 index bcbc69f59f75..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-16_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-20.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-20.png deleted file mode 100644 index f2c64a978d8b..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-20.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-20_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-20_altform-lightunplated_theme-light.png deleted file mode 100644 index f2c64a978d8b..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-20_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-20_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-20_altform-unplated.png deleted file mode 100644 index f2c64a978d8b..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-20_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-24.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-24.png deleted file mode 100644 index 959763c83a23..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-24.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-24_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-24_altform-lightunplated_theme-light.png deleted file mode 100644 index 959763c83a23..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-24_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-24_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-24_altform-unplated.png deleted file mode 100644 index 959763c83a23..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-24_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-256.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-256.png deleted file mode 100644 index d64d7f50eec3..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-256.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-256_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-256_altform-lightunplated_theme-light.png deleted file mode 100644 index c31860106325..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-256_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-256_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-256_altform-unplated.png deleted file mode 100644 index abf62fa5c920..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-256_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-30.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-30.png deleted file mode 100644 index 2ed9f9f2d392..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-30.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-30_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-30_altform-lightunplated_theme-light.png deleted file mode 100644 index 2ed9f9f2d392..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-30_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-30_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-30_altform-unplated.png deleted file mode 100644 index 2ed9f9f2d392..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-30_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-32.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-32.png deleted file mode 100644 index 1d479aab37b7..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-32.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-32_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-32_altform-lightunplated_theme-light.png deleted file mode 100644 index 1d479aab37b7..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-32_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-32_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-32_altform-unplated.png deleted file mode 100644 index 1d479aab37b7..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-32_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-36.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-36.png deleted file mode 100644 index 0e7efb0ad230..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-36.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-36_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-36_altform-lightunplated_theme-light.png deleted file mode 100644 index 0e7efb0ad230..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-36_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-36_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-36_altform-unplated.png deleted file mode 100644 index 0e7efb0ad230..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-36_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-40.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-40.png deleted file mode 100644 index 2c4eff53920a..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-40.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-40_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-40_altform-lightunplated_theme-light.png deleted file mode 100644 index 2c4eff53920a..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-40_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-40_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-40_altform-unplated.png deleted file mode 100644 index 2c4eff53920a..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-40_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-48.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-48.png deleted file mode 100644 index a880039a940b..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-48.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-48_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-48_altform-lightunplated_theme-light.png deleted file mode 100644 index a880039a940b..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-48_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-48_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-48_altform-unplated.png deleted file mode 100644 index a880039a940b..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-48_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-60.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-60.png deleted file mode 100644 index deffb99ff2ac..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-60.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-60_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-60_altform-lightunplated_theme-light.png deleted file mode 100644 index deffb99ff2ac..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-60_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-60_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-60_altform-unplated.png deleted file mode 100644 index deffb99ff2ac..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-60_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-64.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-64.png deleted file mode 100644 index d42c48bf179c..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-64.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-64_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-64_altform-lightunplated_theme-light.png deleted file mode 100644 index d42c48bf179c..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-64_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-64_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-64_altform-unplated.png deleted file mode 100644 index d42c48bf179c..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-64_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-72.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-72.png deleted file mode 100644 index 83a57ca80a07..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-72.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-72_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-72_altform-lightunplated_theme-light.png deleted file mode 100644 index 83a57ca80a07..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-72_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-72_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-72_altform-unplated.png deleted file mode 100644 index 83a57ca80a07..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-72_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-80.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-80.png deleted file mode 100644 index a4daa85d92eb..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-80.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-80_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-80_altform-lightunplated_theme-light.png deleted file mode 100644 index a4daa85d92eb..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-80_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-80_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-80_altform-unplated.png deleted file mode 100644 index a4daa85d92eb..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-80_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-96.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-96.png deleted file mode 100644 index 16ab95860a16..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-96.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-96_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-96_altform-lightunplated_theme-light.png deleted file mode 100644 index 16ab95860a16..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-96_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-96_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-96_altform-unplated.png deleted file mode 100644 index 16ab95860a16..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Square44x44Logo.targetsize-96_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/StoreLogo.scale-100.png b/src/Files.App (Package)/Assets/AppTiles/Preview/StoreLogo.scale-100.png deleted file mode 100644 index 8b2eec9bdd49..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/StoreLogo.scale-100.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/StoreLogo.scale-125.png b/src/Files.App (Package)/Assets/AppTiles/Preview/StoreLogo.scale-125.png deleted file mode 100644 index 166f4e4b1991..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/StoreLogo.scale-125.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/StoreLogo.scale-150.png b/src/Files.App (Package)/Assets/AppTiles/Preview/StoreLogo.scale-150.png deleted file mode 100644 index af57927a4274..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/StoreLogo.scale-150.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/StoreLogo.scale-200.png b/src/Files.App (Package)/Assets/AppTiles/Preview/StoreLogo.scale-200.png deleted file mode 100644 index de653409aede..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/StoreLogo.scale-200.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/StoreLogo.scale-400.png b/src/Files.App (Package)/Assets/AppTiles/Preview/StoreLogo.scale-400.png deleted file mode 100644 index 3c00284cbc63..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/StoreLogo.scale-400.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Wide310x150Logo.scale-100.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Wide310x150Logo.scale-100.png deleted file mode 100644 index 9f2171c580cc..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Wide310x150Logo.scale-100.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Wide310x150Logo.scale-125.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Wide310x150Logo.scale-125.png deleted file mode 100644 index 3e671263ea89..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Wide310x150Logo.scale-125.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Wide310x150Logo.scale-150.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Wide310x150Logo.scale-150.png deleted file mode 100644 index 3556173d56bb..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Wide310x150Logo.scale-150.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Wide310x150Logo.scale-200.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Wide310x150Logo.scale-200.png deleted file mode 100644 index 0429c2429aa2..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Wide310x150Logo.scale-200.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/Wide310x150Logo.scale-400.png b/src/Files.App (Package)/Assets/AppTiles/Preview/Wide310x150Logo.scale-400.png deleted file mode 100644 index 8c8b99cc2b9d..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/Wide310x150Logo.scale-400.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-100_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-100_contrast-black.png deleted file mode 100644 index 351944cbf1b6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-100_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-125_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-125_contrast-black.png deleted file mode 100644 index f4b05dcaea14..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-125_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-150_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-150_contrast-black.png deleted file mode 100644 index e1733477d441..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-150_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-200_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-200_contrast-black.png deleted file mode 100644 index 6dda95fb62ef..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-200_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-400_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-400_contrast-black.png deleted file mode 100644 index d2d094cf6464..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-400_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-100_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-100_contrast-black.png deleted file mode 100644 index 34c47d6daaa0..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-100_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-125_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-125_contrast-black.png deleted file mode 100644 index 8b6c2ec5cfc5..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-125_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-150_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-150_contrast-black.png deleted file mode 100644 index f36b2f24fed0..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-150_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-200_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-200_contrast-black.png deleted file mode 100644 index 86328ba219f6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-200_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-400_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-400_contrast-black.png deleted file mode 100644 index 8c201cf74aa9..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-400_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-100_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-100_contrast-black.png deleted file mode 100644 index 233e462fcc96..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-100_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-125_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-125_contrast-black.png deleted file mode 100644 index a1e6395e1326..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-125_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-150_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-150_contrast-black.png deleted file mode 100644 index 5e9d2875c05a..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-150_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-200_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-200_contrast-black.png deleted file mode 100644 index 82680cba87b7..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-200_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-400_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-400_contrast-black.png deleted file mode 100644 index 67cf077c73ef..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-400_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-100_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-100_contrast-black.png deleted file mode 100644 index 50562035c47a..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-100_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-125_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-125_contrast-black.png deleted file mode 100644 index fa66dde62ffe..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-125_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-150_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-150_contrast-black.png deleted file mode 100644 index ebb28813ae04..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-150_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-200_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-200_contrast-black.png deleted file mode 100644 index 2bf0544d0bc0..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-200_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-400_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-400_contrast-black.png deleted file mode 100644 index 5c7a08418858..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-400_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-100_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-100_contrast-black.png deleted file mode 100644 index 581eda468097..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-100_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-125_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-125_contrast-black.png deleted file mode 100644 index f14d2b063d52..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-125_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-150_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-150_contrast-black.png deleted file mode 100644 index 59040676dce1..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-150_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-200_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-200_contrast-black.png deleted file mode 100644 index b734d4a52e6a..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-200_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-400_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-400_contrast-black.png deleted file mode 100644 index ce7a1b2f57fa..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-400_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-100_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-100_contrast-black.png deleted file mode 100644 index 0d0e0546f93e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-100_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-125_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-125_contrast-black.png deleted file mode 100644 index fc42520a9b3d..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-125_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-150_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-150_contrast-black.png deleted file mode 100644 index cd3ea47970ef..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-150_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-200_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-200_contrast-black.png deleted file mode 100644 index 7984d862c9b4..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-200_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-400_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-400_contrast-black.png deleted file mode 100644 index dacbf50a5faa..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-400_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-black.png deleted file mode 100644 index 1a0c3e0ded83..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-16_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-16_altform-unplated_contrast-black.png deleted file mode 100644 index 1a0c3e0ded83..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-16_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-16_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-16_contrast-black.png deleted file mode 100644 index 1a0c3e0ded83..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-16_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-black.png deleted file mode 100644 index a1b98d53a953..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-20_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-20_altform-unplated_contrast-black.png deleted file mode 100644 index a1b98d53a953..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-20_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-20_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-20_contrast-black.png deleted file mode 100644 index a1b98d53a953..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-20_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-black.png deleted file mode 100644 index 351944cbf1b6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-24_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-24_altform-unplated_contrast-black.png deleted file mode 100644 index 351944cbf1b6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-24_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-24_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-24_contrast-black.png deleted file mode 100644 index 351944cbf1b6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-24_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-black.png deleted file mode 100644 index 6eed7e50f015..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-256_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-256_altform-unplated_contrast-black.png deleted file mode 100644 index 6eed7e50f015..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-256_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-256_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-256_contrast-black.png deleted file mode 100644 index 6eed7e50f015..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-256_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-black.png deleted file mode 100644 index ad548ca11b4b..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-30_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-30_altform-unplated_contrast-black.png deleted file mode 100644 index ad548ca11b4b..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-30_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-30_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-30_contrast-black.png deleted file mode 100644 index ad548ca11b4b..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-30_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-black.png deleted file mode 100644 index c9a05e99e3c3..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-32_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-32_altform-unplated_contrast-black.png deleted file mode 100644 index c9a05e99e3c3..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-32_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-32_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-32_contrast-black.png deleted file mode 100644 index c9a05e99e3c3..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-32_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-black.png deleted file mode 100644 index 77b2a0cd7dca..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-36_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-36_altform-unplated_contrast-black.png deleted file mode 100644 index 77b2a0cd7dca..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-36_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-36_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-36_contrast-black.png deleted file mode 100644 index 77b2a0cd7dca..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-36_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-black.png deleted file mode 100644 index bbe58a1f41ca..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-40_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-40_altform-unplated_contrast-black.png deleted file mode 100644 index bbe58a1f41ca..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-40_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-40_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-40_contrast-black.png deleted file mode 100644 index bbe58a1f41ca..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-40_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-black.png deleted file mode 100644 index 1f4a9acab0e8..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-48_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-48_altform-unplated_contrast-black.png deleted file mode 100644 index 1f4a9acab0e8..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-48_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-48_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-48_contrast-black.png deleted file mode 100644 index 1f4a9acab0e8..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-48_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-black.png deleted file mode 100644 index a31306f714ba..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-60_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-60_altform-unplated_contrast-black.png deleted file mode 100644 index a31306f714ba..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-60_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-60_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-60_contrast-black.png deleted file mode 100644 index a31306f714ba..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-60_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-black.png deleted file mode 100644 index d3347a51e6d4..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-64_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-64_altform-unplated_contrast-black.png deleted file mode 100644 index d3347a51e6d4..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-64_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-64_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-64_contrast-black.png deleted file mode 100644 index d3347a51e6d4..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-64_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-black.png deleted file mode 100644 index b53262b2ec7f..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-72_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-72_altform-unplated_contrast-black.png deleted file mode 100644 index b53262b2ec7f..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-72_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-72_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-72_contrast-black.png deleted file mode 100644 index b53262b2ec7f..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-72_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-black.png deleted file mode 100644 index 780805a9a0e9..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-80_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-80_altform-unplated_contrast-black.png deleted file mode 100644 index 780805a9a0e9..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-80_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-80_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-80_contrast-black.png deleted file mode 100644 index 780805a9a0e9..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-80_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-black.png deleted file mode 100644 index e7f17bcba7ec..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-96_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-96_altform-unplated_contrast-black.png deleted file mode 100644 index e7f17bcba7ec..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-96_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-96_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-96_contrast-black.png deleted file mode 100644 index e7f17bcba7ec..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-96_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-100_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-100_contrast-black.png deleted file mode 100644 index 7aa029c1306e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-100_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-125_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-125_contrast-black.png deleted file mode 100644 index 9ae9de601ae8..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-125_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-150_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-150_contrast-black.png deleted file mode 100644 index 243d61b3dd7e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-150_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-200_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-200_contrast-black.png deleted file mode 100644 index f01d149a8d53..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-200_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-400_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-400_contrast-black.png deleted file mode 100644 index 26359abee70d..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-400_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-100_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-100_contrast-black.png deleted file mode 100644 index 30bd850ae7a3..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-100_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-125_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-125_contrast-black.png deleted file mode 100644 index fbb36a966e53..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-125_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-150_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-150_contrast-black.png deleted file mode 100644 index f8df1c72cca2..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-150_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-200_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-200_contrast-black.png deleted file mode 100644 index 1677ea6aced1..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-200_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-400_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-400_contrast-black.png deleted file mode 100644 index 6baa719379fc..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-400_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-100_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-100_contrast-white.png deleted file mode 100644 index 80e5e8ed63a6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-100_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-125_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-125_contrast-white.png deleted file mode 100644 index 26e954f41295..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-125_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-150_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-150_contrast-white.png deleted file mode 100644 index c010357864be..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-150_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-200_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-200_contrast-white.png deleted file mode 100644 index 6e8af9597e13..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-200_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-400_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-400_contrast-white.png deleted file mode 100644 index 004c4e5fb891..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-400_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-100_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-100_contrast-white.png deleted file mode 100644 index ac66939cf8aa..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-100_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-125_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-125_contrast-white.png deleted file mode 100644 index 841202cf69fb..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-125_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-150_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-150_contrast-white.png deleted file mode 100644 index 227780add3ad..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-150_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-200_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-200_contrast-white.png deleted file mode 100644 index f16187b13a3b..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-200_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-400_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-400_contrast-white.png deleted file mode 100644 index ae147eb8de70..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-400_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-100_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-100_contrast-white.png deleted file mode 100644 index 9e48f54e2177..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-100_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-125_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-125_contrast-white.png deleted file mode 100644 index 4ffe2dd37fe1..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-125_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-150_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-150_contrast-white.png deleted file mode 100644 index 2c63f1fa3f91..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-150_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-200_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-200_contrast-white.png deleted file mode 100644 index d69fd8ed7b04..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-200_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-400_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-400_contrast-white.png deleted file mode 100644 index e5b30a09882a..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-400_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-100_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-100_contrast-white.png deleted file mode 100644 index b4e8e01e6d2e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-100_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-125_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-125_contrast-white.png deleted file mode 100644 index 00d05086d3cd..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-125_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-150_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-150_contrast-white.png deleted file mode 100644 index c6dca65f5a77..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-150_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-200_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-200_contrast-white.png deleted file mode 100644 index 84ca6ca7247a..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-200_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-400_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-400_contrast-white.png deleted file mode 100644 index f8c71e25857f..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-400_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-100_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-100_contrast-white.png deleted file mode 100644 index 614f618fa735..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-100_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-125_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-125_contrast-white.png deleted file mode 100644 index 0b038b1232d0..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-125_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-150_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-150_contrast-white.png deleted file mode 100644 index 2c46f4928683..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-150_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-200_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-200_contrast-white.png deleted file mode 100644 index 385ea5e6f84b..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-200_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-400_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-400_contrast-white.png deleted file mode 100644 index 0f9a6e54dd41..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-400_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-100_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-100_contrast-white.png deleted file mode 100644 index 4f31c13c440d..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-100_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-125_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-125_contrast-white.png deleted file mode 100644 index 681f3075a814..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-125_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-150_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-150_contrast-white.png deleted file mode 100644 index 5997065e1d6a..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-150_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-200_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-200_contrast-white.png deleted file mode 100644 index 90fb690f3c44..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-200_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-400_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-400_contrast-white.png deleted file mode 100644 index 67da6da2660c..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-400_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-white.png deleted file mode 100644 index a2ac9c5fdf41..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-16_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-16_altform-unplated_contrast-white.png deleted file mode 100644 index a2ac9c5fdf41..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-16_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-16_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-16_contrast-white.png deleted file mode 100644 index a2ac9c5fdf41..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-16_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-white.png deleted file mode 100644 index beba47a931d6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-20_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-20_altform-unplated_contrast-white.png deleted file mode 100644 index beba47a931d6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-20_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-20_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-20_contrast-white.png deleted file mode 100644 index beba47a931d6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-20_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-white.png deleted file mode 100644 index 80e5e8ed63a6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-24_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-24_altform-unplated_contrast-white.png deleted file mode 100644 index 80e5e8ed63a6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-24_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-24_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-24_contrast-white.png deleted file mode 100644 index 80e5e8ed63a6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-24_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-white.png deleted file mode 100644 index fe460cab3f97..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-256_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-256_altform-unplated_contrast-white.png deleted file mode 100644 index fe460cab3f97..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-256_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-256_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-256_contrast-white.png deleted file mode 100644 index fe460cab3f97..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-256_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-white.png deleted file mode 100644 index b56dd5cde177..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-30_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-30_altform-unplated_contrast-white.png deleted file mode 100644 index b56dd5cde177..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-30_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-30_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-30_contrast-white.png deleted file mode 100644 index b56dd5cde177..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-30_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-white.png deleted file mode 100644 index b63beead49ab..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-32_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-32_altform-unplated_contrast-white.png deleted file mode 100644 index b63beead49ab..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-32_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-32_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-32_contrast-white.png deleted file mode 100644 index b63beead49ab..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-32_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-white.png deleted file mode 100644 index c013ca9bcf57..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-36_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-36_altform-unplated_contrast-white.png deleted file mode 100644 index c013ca9bcf57..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-36_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-36_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-36_contrast-white.png deleted file mode 100644 index c013ca9bcf57..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-36_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-white.png deleted file mode 100644 index 443f9ad28a15..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-40_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-40_altform-unplated_contrast-white.png deleted file mode 100644 index 443f9ad28a15..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-40_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-40_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-40_contrast-white.png deleted file mode 100644 index 443f9ad28a15..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-40_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-white.png deleted file mode 100644 index 3e3fa2b30a83..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-48_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-48_altform-unplated_contrast-white.png deleted file mode 100644 index 3e3fa2b30a83..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-48_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-48_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-48_contrast-white.png deleted file mode 100644 index 3e3fa2b30a83..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-48_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-white.png deleted file mode 100644 index 9224cb232130..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-60_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-60_altform-unplated_contrast-white.png deleted file mode 100644 index 9224cb232130..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-60_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-60_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-60_contrast-white.png deleted file mode 100644 index 9224cb232130..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-60_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-white.png deleted file mode 100644 index ae45020dcc1c..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-64_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-64_altform-unplated_contrast-white.png deleted file mode 100644 index ae45020dcc1c..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-64_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-64_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-64_contrast-white.png deleted file mode 100644 index ae45020dcc1c..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-64_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-white.png deleted file mode 100644 index 492fbbe1ff37..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-72_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-72_altform-unplated_contrast-white.png deleted file mode 100644 index 492fbbe1ff37..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-72_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-72_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-72_contrast-white.png deleted file mode 100644 index 492fbbe1ff37..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-72_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-white.png deleted file mode 100644 index 3724e54306eb..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-80_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-80_altform-unplated_contrast-white.png deleted file mode 100644 index 3724e54306eb..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-80_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-80_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-80_contrast-white.png deleted file mode 100644 index 3724e54306eb..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-80_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-white.png deleted file mode 100644 index 39599103635e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-96_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-96_altform-unplated_contrast-white.png deleted file mode 100644 index 39599103635e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-96_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-96_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-96_contrast-white.png deleted file mode 100644 index 39599103635e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-96_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-100_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-100_contrast-white.png deleted file mode 100644 index 69ca7984c161..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-100_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-125_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-125_contrast-white.png deleted file mode 100644 index 441680c08dbb..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-125_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-150_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-150_contrast-white.png deleted file mode 100644 index 7d4a742ec205..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-150_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-200_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-200_contrast-white.png deleted file mode 100644 index 2cfd75d7cabe..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-200_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-400_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-400_contrast-white.png deleted file mode 100644 index ed1a5afbd1b6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-400_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-100_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-100_contrast-white.png deleted file mode 100644 index 6a6cb23b1caf..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-100_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-125_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-125_contrast-white.png deleted file mode 100644 index ca2c5e2b67e5..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-125_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-150_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-150_contrast-white.png deleted file mode 100644 index 7c7df0556a13..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-150_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-200_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-200_contrast-white.png deleted file mode 100644 index 97a8567beb60..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-200_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-400_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-400_contrast-white.png deleted file mode 100644 index cc1a45208f4f..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-400_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/BadgeLogo.scale-100.png b/src/Files.App (Package)/Assets/AppTiles/Release/BadgeLogo.scale-100.png deleted file mode 100644 index 351944cbf1b6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/BadgeLogo.scale-100.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/BadgeLogo.scale-125.png b/src/Files.App (Package)/Assets/AppTiles/Release/BadgeLogo.scale-125.png deleted file mode 100644 index f4b05dcaea14..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/BadgeLogo.scale-125.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/BadgeLogo.scale-150.png b/src/Files.App (Package)/Assets/AppTiles/Release/BadgeLogo.scale-150.png deleted file mode 100644 index e1733477d441..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/BadgeLogo.scale-150.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/BadgeLogo.scale-200.png b/src/Files.App (Package)/Assets/AppTiles/Release/BadgeLogo.scale-200.png deleted file mode 100644 index 6dda95fb62ef..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/BadgeLogo.scale-200.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/BadgeLogo.scale-400.png b/src/Files.App (Package)/Assets/AppTiles/Release/BadgeLogo.scale-400.png deleted file mode 100644 index d2d094cf6464..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/BadgeLogo.scale-400.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Large310x310Logo.scale-100.png b/src/Files.App (Package)/Assets/AppTiles/Release/Large310x310Logo.scale-100.png deleted file mode 100644 index 5cccdbac133c..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Large310x310Logo.scale-100.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Large310x310Logo.scale-125.png b/src/Files.App (Package)/Assets/AppTiles/Release/Large310x310Logo.scale-125.png deleted file mode 100644 index 7835a3511b2f..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Large310x310Logo.scale-125.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Large310x310Logo.scale-150.png b/src/Files.App (Package)/Assets/AppTiles/Release/Large310x310Logo.scale-150.png deleted file mode 100644 index c12ef4220a46..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Large310x310Logo.scale-150.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Large310x310Logo.scale-200.png b/src/Files.App (Package)/Assets/AppTiles/Release/Large310x310Logo.scale-200.png deleted file mode 100644 index c8a9a5f06188..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Large310x310Logo.scale-200.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Large310x310Logo.scale-400.png b/src/Files.App (Package)/Assets/AppTiles/Release/Large310x310Logo.scale-400.png deleted file mode 100644 index 8ecd436b21f6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Large310x310Logo.scale-400.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Logo.ico b/src/Files.App (Package)/Assets/AppTiles/Release/Logo.ico deleted file mode 100644 index 38c271e3254b..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Logo.ico and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Small71x71Logo.scale-100.png b/src/Files.App (Package)/Assets/AppTiles/Release/Small71x71Logo.scale-100.png deleted file mode 100644 index 0ba56f1fb1ca..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Small71x71Logo.scale-100.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Small71x71Logo.scale-125.png b/src/Files.App (Package)/Assets/AppTiles/Release/Small71x71Logo.scale-125.png deleted file mode 100644 index b1d0ff8fdab9..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Small71x71Logo.scale-125.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Small71x71Logo.scale-150.png b/src/Files.App (Package)/Assets/AppTiles/Release/Small71x71Logo.scale-150.png deleted file mode 100644 index 9755c835d9f3..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Small71x71Logo.scale-150.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Small71x71Logo.scale-200.png b/src/Files.App (Package)/Assets/AppTiles/Release/Small71x71Logo.scale-200.png deleted file mode 100644 index 9df58f3cf7e9..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Small71x71Logo.scale-200.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Small71x71Logo.scale-400.png b/src/Files.App (Package)/Assets/AppTiles/Release/Small71x71Logo.scale-400.png deleted file mode 100644 index 574bf0697efe..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Small71x71Logo.scale-400.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/SplashScreen.scale-100.png b/src/Files.App (Package)/Assets/AppTiles/Release/SplashScreen.scale-100.png deleted file mode 100644 index e5b46ba043c8..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/SplashScreen.scale-100.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/SplashScreen.scale-125.png b/src/Files.App (Package)/Assets/AppTiles/Release/SplashScreen.scale-125.png deleted file mode 100644 index 8cdf9366f287..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/SplashScreen.scale-125.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/SplashScreen.scale-150.png b/src/Files.App (Package)/Assets/AppTiles/Release/SplashScreen.scale-150.png deleted file mode 100644 index 6383291bd206..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/SplashScreen.scale-150.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/SplashScreen.scale-200.png b/src/Files.App (Package)/Assets/AppTiles/Release/SplashScreen.scale-200.png deleted file mode 100644 index 9fda07426405..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/SplashScreen.scale-200.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/SplashScreen.scale-400.png b/src/Files.App (Package)/Assets/AppTiles/Release/SplashScreen.scale-400.png deleted file mode 100644 index 047e412c7b2a..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/SplashScreen.scale-400.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square150x150Logo.scale-100.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square150x150Logo.scale-100.png deleted file mode 100644 index a8137d07501a..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square150x150Logo.scale-100.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square150x150Logo.scale-125.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square150x150Logo.scale-125.png deleted file mode 100644 index 3ee0c5b72b2c..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square150x150Logo.scale-125.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square150x150Logo.scale-150.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square150x150Logo.scale-150.png deleted file mode 100644 index 748bc69afa80..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square150x150Logo.scale-150.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square150x150Logo.scale-200.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square150x150Logo.scale-200.png deleted file mode 100644 index bccb192f40fc..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square150x150Logo.scale-200.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square150x150Logo.scale-400.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square150x150Logo.scale-400.png deleted file mode 100644 index 422924e87da4..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square150x150Logo.scale-400.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.scale-100.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.scale-100.png deleted file mode 100644 index 61519ffaa8de..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.scale-100.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.scale-125.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.scale-125.png deleted file mode 100644 index 84d9899f3813..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.scale-125.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.scale-150.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.scale-150.png deleted file mode 100644 index 637d01bbe7db..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.scale-150.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.scale-200.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.scale-200.png deleted file mode 100644 index b6365b100e6a..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.scale-200.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.scale-400.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.scale-400.png deleted file mode 100644 index beb643ab66cd..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.scale-400.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-16.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-16.png deleted file mode 100644 index c2ee774afc88..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-16.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-16_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-16_altform-lightunplated_theme-light.png deleted file mode 100644 index c2ee774afc88..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-16_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-16_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-16_altform-unplated.png deleted file mode 100644 index c2ee774afc88..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-16_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-20.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-20.png deleted file mode 100644 index 7535fb990ba3..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-20.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-20_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-20_altform-lightunplated_theme-light.png deleted file mode 100644 index 7535fb990ba3..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-20_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-20_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-20_altform-unplated.png deleted file mode 100644 index 7535fb990ba3..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-20_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-24.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-24.png deleted file mode 100644 index 3dae3e2eca65..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-24.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-24_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-24_altform-lightunplated_theme-light.png deleted file mode 100644 index 3dae3e2eca65..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-24_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-24_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-24_altform-unplated.png deleted file mode 100644 index 3dae3e2eca65..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-24_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-256.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-256.png deleted file mode 100644 index 2bbe5c0d38ff..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-256.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-256_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-256_altform-lightunplated_theme-light.png deleted file mode 100644 index 2bbe5c0d38ff..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-256_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-256_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-256_altform-unplated.png deleted file mode 100644 index 2bbe5c0d38ff..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-256_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-30.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-30.png deleted file mode 100644 index e1d0b078b15b..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-30.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-30_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-30_altform-lightunplated_theme-light.png deleted file mode 100644 index e1d0b078b15b..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-30_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-30_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-30_altform-unplated.png deleted file mode 100644 index e1d0b078b15b..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-30_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-32.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-32.png deleted file mode 100644 index ae6db582ad07..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-32.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-32_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-32_altform-lightunplated_theme-light.png deleted file mode 100644 index ae6db582ad07..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-32_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-32_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-32_altform-unplated.png deleted file mode 100644 index ae6db582ad07..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-32_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-36.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-36.png deleted file mode 100644 index 760dbea9e0dc..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-36.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-36_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-36_altform-lightunplated_theme-light.png deleted file mode 100644 index 760dbea9e0dc..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-36_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-36_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-36_altform-unplated.png deleted file mode 100644 index 760dbea9e0dc..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-36_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-40.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-40.png deleted file mode 100644 index aed43a8d9ed4..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-40.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-40_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-40_altform-lightunplated_theme-light.png deleted file mode 100644 index aed43a8d9ed4..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-40_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-40_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-40_altform-unplated.png deleted file mode 100644 index aed43a8d9ed4..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-40_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-48.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-48.png deleted file mode 100644 index 82a1fda63836..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-48.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-48_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-48_altform-lightunplated_theme-light.png deleted file mode 100644 index 82a1fda63836..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-48_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-48_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-48_altform-unplated.png deleted file mode 100644 index 82a1fda63836..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-48_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-60.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-60.png deleted file mode 100644 index ab990ce4e895..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-60.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-60_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-60_altform-lightunplated_theme-light.png deleted file mode 100644 index ab990ce4e895..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-60_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-60_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-60_altform-unplated.png deleted file mode 100644 index ab990ce4e895..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-60_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-64.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-64.png deleted file mode 100644 index d8d83997a05d..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-64.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-64_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-64_altform-lightunplated_theme-light.png deleted file mode 100644 index d8d83997a05d..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-64_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-64_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-64_altform-unplated.png deleted file mode 100644 index d8d83997a05d..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-64_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-72.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-72.png deleted file mode 100644 index b50266e2c2e3..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-72.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-72_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-72_altform-lightunplated_theme-light.png deleted file mode 100644 index b50266e2c2e3..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-72_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-72_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-72_altform-unplated.png deleted file mode 100644 index b50266e2c2e3..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-72_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-80.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-80.png deleted file mode 100644 index 6a0fa4c22474..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-80.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-80_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-80_altform-lightunplated_theme-light.png deleted file mode 100644 index 6a0fa4c22474..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-80_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-80_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-80_altform-unplated.png deleted file mode 100644 index 6a0fa4c22474..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-80_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-96.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-96.png deleted file mode 100644 index 05279dff20a1..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-96.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-96_altform-lightunplated_theme-light.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-96_altform-lightunplated_theme-light.png deleted file mode 100644 index 05279dff20a1..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-96_altform-lightunplated_theme-light.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-96_altform-unplated.png b/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-96_altform-unplated.png deleted file mode 100644 index 05279dff20a1..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Square44x44Logo.targetsize-96_altform-unplated.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/StoreLogo.scale-100.png b/src/Files.App (Package)/Assets/AppTiles/Release/StoreLogo.scale-100.png deleted file mode 100644 index 7a156062352e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/StoreLogo.scale-100.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/StoreLogo.scale-125.png b/src/Files.App (Package)/Assets/AppTiles/Release/StoreLogo.scale-125.png deleted file mode 100644 index d21cc47cf44e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/StoreLogo.scale-125.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/StoreLogo.scale-150.png b/src/Files.App (Package)/Assets/AppTiles/Release/StoreLogo.scale-150.png deleted file mode 100644 index 473826db005f..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/StoreLogo.scale-150.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/StoreLogo.scale-200.png b/src/Files.App (Package)/Assets/AppTiles/Release/StoreLogo.scale-200.png deleted file mode 100644 index 9c1c5c651189..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/StoreLogo.scale-200.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/StoreLogo.scale-400.png b/src/Files.App (Package)/Assets/AppTiles/Release/StoreLogo.scale-400.png deleted file mode 100644 index 717f45e9c6db..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/StoreLogo.scale-400.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Wide310x150Logo.scale-100.png b/src/Files.App (Package)/Assets/AppTiles/Release/Wide310x150Logo.scale-100.png deleted file mode 100644 index 82b373a1e50c..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Wide310x150Logo.scale-100.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Wide310x150Logo.scale-125.png b/src/Files.App (Package)/Assets/AppTiles/Release/Wide310x150Logo.scale-125.png deleted file mode 100644 index 900da7b0a4fe..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Wide310x150Logo.scale-125.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Wide310x150Logo.scale-150.png b/src/Files.App (Package)/Assets/AppTiles/Release/Wide310x150Logo.scale-150.png deleted file mode 100644 index 1c8dd094866e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Wide310x150Logo.scale-150.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Wide310x150Logo.scale-200.png b/src/Files.App (Package)/Assets/AppTiles/Release/Wide310x150Logo.scale-200.png deleted file mode 100644 index fc1fcff8c446..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Wide310x150Logo.scale-200.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/Wide310x150Logo.scale-400.png b/src/Files.App (Package)/Assets/AppTiles/Release/Wide310x150Logo.scale-400.png deleted file mode 100644 index ee3a2ea4a1e9..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/Wide310x150Logo.scale-400.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-100_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-100_contrast-black.png deleted file mode 100644 index 351944cbf1b6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-100_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-125_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-125_contrast-black.png deleted file mode 100644 index f4b05dcaea14..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-125_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-150_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-150_contrast-black.png deleted file mode 100644 index e1733477d441..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-150_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-200_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-200_contrast-black.png deleted file mode 100644 index 6dda95fb62ef..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-200_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-400_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-400_contrast-black.png deleted file mode 100644 index d2d094cf6464..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-400_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-100_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-100_contrast-black.png deleted file mode 100644 index 34c47d6daaa0..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-100_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-125_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-125_contrast-black.png deleted file mode 100644 index 8b6c2ec5cfc5..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-125_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-150_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-150_contrast-black.png deleted file mode 100644 index f36b2f24fed0..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-150_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-200_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-200_contrast-black.png deleted file mode 100644 index 86328ba219f6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-200_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-400_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-400_contrast-black.png deleted file mode 100644 index 8c201cf74aa9..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-400_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-100_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-100_contrast-black.png deleted file mode 100644 index 233e462fcc96..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-100_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-125_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-125_contrast-black.png deleted file mode 100644 index a1e6395e1326..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-125_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-150_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-150_contrast-black.png deleted file mode 100644 index 5e9d2875c05a..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-150_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-200_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-200_contrast-black.png deleted file mode 100644 index 82680cba87b7..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-200_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-400_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-400_contrast-black.png deleted file mode 100644 index 67cf077c73ef..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-400_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-100_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-100_contrast-black.png deleted file mode 100644 index 50562035c47a..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-100_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-125_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-125_contrast-black.png deleted file mode 100644 index bcdb19185ed6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-125_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-150_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-150_contrast-black.png deleted file mode 100644 index ebb28813ae04..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-150_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-200_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-200_contrast-black.png deleted file mode 100644 index 2bf0544d0bc0..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-200_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-400_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-400_contrast-black.png deleted file mode 100644 index 5c7a08418858..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-400_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-100_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-100_contrast-black.png deleted file mode 100644 index 581eda468097..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-100_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-125_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-125_contrast-black.png deleted file mode 100644 index f14d2b063d52..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-125_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-150_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-150_contrast-black.png deleted file mode 100644 index 59040676dce1..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-150_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-200_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-200_contrast-black.png deleted file mode 100644 index b734d4a52e6a..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-200_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-400_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-400_contrast-black.png deleted file mode 100644 index ce7a1b2f57fa..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-400_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-100_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-100_contrast-black.png deleted file mode 100644 index 0d0e0546f93e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-100_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-125_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-125_contrast-black.png deleted file mode 100644 index fc42520a9b3d..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-125_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-150_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-150_contrast-black.png deleted file mode 100644 index cd3ea47970ef..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-150_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-200_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-200_contrast-black.png deleted file mode 100644 index 7984d862c9b4..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-200_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-400_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-400_contrast-black.png deleted file mode 100644 index dacbf50a5faa..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-400_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-black.png deleted file mode 100644 index 1a0c3e0ded83..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-16_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-16_altform-unplated_contrast-black.png deleted file mode 100644 index 1a0c3e0ded83..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-16_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-16_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-16_contrast-black.png deleted file mode 100644 index 1a0c3e0ded83..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-16_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-black.png deleted file mode 100644 index a1b98d53a953..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-20_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-20_altform-unplated_contrast-black.png deleted file mode 100644 index a1b98d53a953..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-20_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-20_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-20_contrast-black.png deleted file mode 100644 index a1b98d53a953..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-20_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-black.png deleted file mode 100644 index 351944cbf1b6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-24_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-24_altform-unplated_contrast-black.png deleted file mode 100644 index 351944cbf1b6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-24_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-24_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-24_contrast-black.png deleted file mode 100644 index 351944cbf1b6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-24_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-black.png deleted file mode 100644 index 6eed7e50f015..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-256_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-256_altform-unplated_contrast-black.png deleted file mode 100644 index 6eed7e50f015..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-256_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-256_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-256_contrast-black.png deleted file mode 100644 index 6eed7e50f015..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-256_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-black.png deleted file mode 100644 index ad548ca11b4b..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-30_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-30_altform-unplated_contrast-black.png deleted file mode 100644 index ad548ca11b4b..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-30_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-30_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-30_contrast-black.png deleted file mode 100644 index ad548ca11b4b..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-30_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-black.png deleted file mode 100644 index c9a05e99e3c3..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-32_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-32_altform-unplated_contrast-black.png deleted file mode 100644 index c9a05e99e3c3..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-32_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-32_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-32_contrast-black.png deleted file mode 100644 index c9a05e99e3c3..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-32_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-black.png deleted file mode 100644 index 77b2a0cd7dca..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-36_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-36_altform-unplated_contrast-black.png deleted file mode 100644 index 77b2a0cd7dca..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-36_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-36_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-36_contrast-black.png deleted file mode 100644 index 77b2a0cd7dca..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-36_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-black.png deleted file mode 100644 index bbe58a1f41ca..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-40_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-40_altform-unplated_contrast-black.png deleted file mode 100644 index bbe58a1f41ca..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-40_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-40_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-40_contrast-black.png deleted file mode 100644 index bbe58a1f41ca..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-40_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-black.png deleted file mode 100644 index 1f4a9acab0e8..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-48_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-48_altform-unplated_contrast-black.png deleted file mode 100644 index 1f4a9acab0e8..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-48_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-48_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-48_contrast-black.png deleted file mode 100644 index 1f4a9acab0e8..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-48_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-black.png deleted file mode 100644 index a31306f714ba..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-60_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-60_altform-unplated_contrast-black.png deleted file mode 100644 index a31306f714ba..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-60_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-60_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-60_contrast-black.png deleted file mode 100644 index a31306f714ba..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-60_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-black.png deleted file mode 100644 index d3347a51e6d4..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-64_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-64_altform-unplated_contrast-black.png deleted file mode 100644 index d3347a51e6d4..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-64_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-64_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-64_contrast-black.png deleted file mode 100644 index d3347a51e6d4..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-64_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-black.png deleted file mode 100644 index b53262b2ec7f..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-72_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-72_altform-unplated_contrast-black.png deleted file mode 100644 index b53262b2ec7f..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-72_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-72_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-72_contrast-black.png deleted file mode 100644 index b53262b2ec7f..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-72_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-black.png deleted file mode 100644 index 780805a9a0e9..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-80_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-80_altform-unplated_contrast-black.png deleted file mode 100644 index 780805a9a0e9..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-80_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-80_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-80_contrast-black.png deleted file mode 100644 index 780805a9a0e9..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-80_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-black.png deleted file mode 100644 index e7f17bcba7ec..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-96_altform-unplated_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-96_altform-unplated_contrast-black.png deleted file mode 100644 index e7f17bcba7ec..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-96_altform-unplated_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-96_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-96_contrast-black.png deleted file mode 100644 index e7f17bcba7ec..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-96_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-100_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-100_contrast-black.png deleted file mode 100644 index 7aa029c1306e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-100_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-125_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-125_contrast-black.png deleted file mode 100644 index 9ae9de601ae8..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-125_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-150_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-150_contrast-black.png deleted file mode 100644 index 243d61b3dd7e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-150_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-200_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-200_contrast-black.png deleted file mode 100644 index f01d149a8d53..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-200_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-400_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-400_contrast-black.png deleted file mode 100644 index 26359abee70d..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-400_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-100_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-100_contrast-black.png deleted file mode 100644 index 30bd850ae7a3..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-100_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-125_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-125_contrast-black.png deleted file mode 100644 index fbb36a966e53..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-125_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-150_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-150_contrast-black.png deleted file mode 100644 index f8df1c72cca2..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-150_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-200_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-200_contrast-black.png deleted file mode 100644 index 1677ea6aced1..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-200_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-400_contrast-black.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-400_contrast-black.png deleted file mode 100644 index 6baa719379fc..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-400_contrast-black.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-100_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-100_contrast-white.png deleted file mode 100644 index 80e5e8ed63a6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-100_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-125_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-125_contrast-white.png deleted file mode 100644 index 26e954f41295..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-125_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-150_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-150_contrast-white.png deleted file mode 100644 index c010357864be..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-150_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-200_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-200_contrast-white.png deleted file mode 100644 index 6e8af9597e13..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-200_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-400_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-400_contrast-white.png deleted file mode 100644 index 004c4e5fb891..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-400_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-100_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-100_contrast-white.png deleted file mode 100644 index ac66939cf8aa..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-100_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-125_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-125_contrast-white.png deleted file mode 100644 index 841202cf69fb..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-125_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-150_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-150_contrast-white.png deleted file mode 100644 index c7320c691678..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-150_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-200_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-200_contrast-white.png deleted file mode 100644 index f16187b13a3b..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-200_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-400_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-400_contrast-white.png deleted file mode 100644 index ae147eb8de70..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-400_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-100_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-100_contrast-white.png deleted file mode 100644 index 9e48f54e2177..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-100_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-125_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-125_contrast-white.png deleted file mode 100644 index 4ffe2dd37fe1..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-125_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-150_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-150_contrast-white.png deleted file mode 100644 index 2c63f1fa3f91..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-150_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-200_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-200_contrast-white.png deleted file mode 100644 index d69fd8ed7b04..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-200_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-400_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-400_contrast-white.png deleted file mode 100644 index e5b30a09882a..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-400_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-100_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-100_contrast-white.png deleted file mode 100644 index b4e8e01e6d2e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-100_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-125_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-125_contrast-white.png deleted file mode 100644 index 2887cfb68f29..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-125_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-150_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-150_contrast-white.png deleted file mode 100644 index c6dca65f5a77..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-150_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-200_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-200_contrast-white.png deleted file mode 100644 index 84ca6ca7247a..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-200_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-400_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-400_contrast-white.png deleted file mode 100644 index f8c71e25857f..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-400_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-100_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-100_contrast-white.png deleted file mode 100644 index 614f618fa735..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-100_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-125_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-125_contrast-white.png deleted file mode 100644 index 0b038b1232d0..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-125_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-150_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-150_contrast-white.png deleted file mode 100644 index 2c46f4928683..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-150_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-200_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-200_contrast-white.png deleted file mode 100644 index 385ea5e6f84b..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-200_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-400_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-400_contrast-white.png deleted file mode 100644 index 0f9a6e54dd41..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-400_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-100_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-100_contrast-white.png deleted file mode 100644 index 4f31c13c440d..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-100_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-125_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-125_contrast-white.png deleted file mode 100644 index 681f3075a814..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-125_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-150_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-150_contrast-white.png deleted file mode 100644 index 5997065e1d6a..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-150_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-200_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-200_contrast-white.png deleted file mode 100644 index 90fb690f3c44..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-200_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-400_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-400_contrast-white.png deleted file mode 100644 index 67da6da2660c..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-400_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-white.png deleted file mode 100644 index a2ac9c5fdf41..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-16_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-16_altform-unplated_contrast-white.png deleted file mode 100644 index a2ac9c5fdf41..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-16_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-16_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-16_contrast-white.png deleted file mode 100644 index a2ac9c5fdf41..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-16_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-white.png deleted file mode 100644 index beba47a931d6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-20_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-20_altform-unplated_contrast-white.png deleted file mode 100644 index beba47a931d6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-20_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-20_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-20_contrast-white.png deleted file mode 100644 index beba47a931d6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-20_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-white.png deleted file mode 100644 index 80e5e8ed63a6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-24_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-24_altform-unplated_contrast-white.png deleted file mode 100644 index 80e5e8ed63a6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-24_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-24_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-24_contrast-white.png deleted file mode 100644 index 80e5e8ed63a6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-24_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-white.png deleted file mode 100644 index fe460cab3f97..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-256_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-256_altform-unplated_contrast-white.png deleted file mode 100644 index fe460cab3f97..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-256_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-256_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-256_contrast-white.png deleted file mode 100644 index fe460cab3f97..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-256_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-white.png deleted file mode 100644 index b56dd5cde177..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-30_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-30_altform-unplated_contrast-white.png deleted file mode 100644 index b56dd5cde177..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-30_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-30_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-30_contrast-white.png deleted file mode 100644 index b56dd5cde177..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-30_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-white.png deleted file mode 100644 index b63beead49ab..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-32_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-32_altform-unplated_contrast-white.png deleted file mode 100644 index b63beead49ab..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-32_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-32_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-32_contrast-white.png deleted file mode 100644 index b63beead49ab..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-32_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-white.png deleted file mode 100644 index c013ca9bcf57..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-36_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-36_altform-unplated_contrast-white.png deleted file mode 100644 index c013ca9bcf57..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-36_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-36_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-36_contrast-white.png deleted file mode 100644 index c013ca9bcf57..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-36_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-white.png deleted file mode 100644 index 443f9ad28a15..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-40_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-40_altform-unplated_contrast-white.png deleted file mode 100644 index 443f9ad28a15..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-40_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-40_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-40_contrast-white.png deleted file mode 100644 index 443f9ad28a15..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-40_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-white.png deleted file mode 100644 index 3e3fa2b30a83..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-48_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-48_altform-unplated_contrast-white.png deleted file mode 100644 index 3e3fa2b30a83..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-48_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-48_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-48_contrast-white.png deleted file mode 100644 index 3e3fa2b30a83..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-48_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-white.png deleted file mode 100644 index 9224cb232130..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-60_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-60_altform-unplated_contrast-white.png deleted file mode 100644 index 9224cb232130..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-60_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-60_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-60_contrast-white.png deleted file mode 100644 index 9224cb232130..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-60_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-white.png deleted file mode 100644 index ae45020dcc1c..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-64_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-64_altform-unplated_contrast-white.png deleted file mode 100644 index ae45020dcc1c..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-64_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-64_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-64_contrast-white.png deleted file mode 100644 index ae45020dcc1c..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-64_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-white.png deleted file mode 100644 index 492fbbe1ff37..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-72_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-72_altform-unplated_contrast-white.png deleted file mode 100644 index 492fbbe1ff37..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-72_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-72_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-72_contrast-white.png deleted file mode 100644 index 492fbbe1ff37..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-72_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-white.png deleted file mode 100644 index 3724e54306eb..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-80_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-80_altform-unplated_contrast-white.png deleted file mode 100644 index 3724e54306eb..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-80_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-80_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-80_contrast-white.png deleted file mode 100644 index 3724e54306eb..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-80_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-white.png deleted file mode 100644 index 39599103635e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-96_altform-unplated_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-96_altform-unplated_contrast-white.png deleted file mode 100644 index 39599103635e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-96_altform-unplated_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-96_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-96_contrast-white.png deleted file mode 100644 index 39599103635e..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-96_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-100_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-100_contrast-white.png deleted file mode 100644 index 69ca7984c161..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-100_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-125_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-125_contrast-white.png deleted file mode 100644 index 441680c08dbb..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-125_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-150_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-150_contrast-white.png deleted file mode 100644 index 7d4a742ec205..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-150_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-200_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-200_contrast-white.png deleted file mode 100644 index 2cfd75d7cabe..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-200_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-400_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-400_contrast-white.png deleted file mode 100644 index ed1a5afbd1b6..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-400_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-100_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-100_contrast-white.png deleted file mode 100644 index 6a6cb23b1caf..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-100_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-125_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-125_contrast-white.png deleted file mode 100644 index ca2c5e2b67e5..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-125_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-150_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-150_contrast-white.png deleted file mode 100644 index 7c7df0556a13..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-150_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-200_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-200_contrast-white.png deleted file mode 100644 index 97a8567beb60..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-200_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-400_contrast-white.png b/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-400_contrast-white.png deleted file mode 100644 index cc1a45208f4f..000000000000 Binary files a/src/Files.App (Package)/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-400_contrast-white.png and /dev/null differ diff --git a/src/Files.App (Package)/Files.Package.wapproj b/src/Files.App (Package)/Files.Package.wapproj deleted file mode 100644 index 37c1cce34459..000000000000 --- a/src/Files.App (Package)/Files.Package.wapproj +++ /dev/null @@ -1,119 +0,0 @@ - - - - - 8f60fd8e-1921-47d6-97b0-d26d7b3a4999 - 10.0.22621.0 - 10.0.19041.0 - net8.0-windows$(TargetPlatformVersion);$(AssetTargetFallback) - en-US - False - False - True - x86|x64|arm64 - False - True - Scale|DXFeatureLevel - Language=en-US;af;ar;be-BY;bg;ca;cs-CZ;da;de-DE;el;en-GB;es-ES;es-419;fa-IR;fi-FI;fil-PH;fr-FR;he-IL;hi-IN;hr-HR;hu-HU;id-ID;it-IT;ja-JP;ka;km-KH;ko-KR;ku-Arab;lt-LT;lv-LV;ms-MY;nb-NO;nl-NL;or-IN;pl-PL;pt-BR;pt-PT;ro-RO;ru-RU;sk-SK;sq-AL;sr-Cyrl;sv-SE;ta;th-TH;tr-TR;uk-UA;vi;zh-Hans;zh-Hant - StoreUpload - 255 - ..\Files.App\Files.App.csproj - Always - - - 17.0 - - - - Debug - x86 - - - Preview - ARM64 - - - Preview - x64 - - - Preview - x86 - - - Release - x86 - - - Debug - x64 - - - Release - x64 - - - Debug - ARM64 - - - Release - ARM64 - - - Stable - ARM64 - - - Stable - x64 - - - Stable - x86 - - - Store - ARM64 - - - Store - x64 - - - Store - x86 - - - - $(MSBuildExtensionsPath)\Microsoft\DesktopBridge\ - Files.App\ - - - - - Designer - - - - - - True - Properties\PublishProfiles\win-$(Platform).pubxml - - - True - Properties\PublishProfiles\win-$(Platform).pubxml - - - - - - - - - - <_AppxWinmdFilesToHarvest Condition="'%(FileName)' == 'Files.App.Server'" Remove="@(_AppxWinmdFilesToHarvest)" /> - - - \ No newline at end of file diff --git a/src/Files.App.BackgroundTasks/Files.App.BackgroundTasks.csproj b/src/Files.App.BackgroundTasks/Files.App.BackgroundTasks.csproj index bc79f3d79537..fc0b6e7d6cdb 100644 --- a/src/Files.App.BackgroundTasks/Files.App.BackgroundTasks.csproj +++ b/src/Files.App.BackgroundTasks/Files.App.BackgroundTasks.csproj @@ -1,32 +1,27 @@ - + - net8.0-windows10.0.22621.0 + $(WindowsTargetFramework) en-US - 10.0.19041.0 + $(MinimalWindowsVersion) true - Debug;Release;Stable;Preview;Store + Debug;Release x86;x64;ARM64 win-x86;win-x64;win-arm64 + true + true + true true - 10.0.22621.0 + $(TargetWindowsVersion) $(MSBuildProjectDirectory)\bin\$(Platform)\$(Configuration) - - TRACE;DEBUG;NETFX_CORE - - - TRACE;RELEASE;NETFX_CORE - true - - - + diff --git a/src/Files.App.BackgroundTasks/UpdateTask.cs b/src/Files.App.BackgroundTasks/UpdateTask.cs index ca7e48ef84f2..ab7e1f9317d0 100644 --- a/src/Files.App.BackgroundTasks/UpdateTask.cs +++ b/src/Files.App.BackgroundTasks/UpdateTask.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System; using System.IO; diff --git a/src/Files.App.Controls/AdaptiveGridView/AdaptiveGridView.Properties.cs b/src/Files.App.Controls/AdaptiveGridView/AdaptiveGridView.Properties.cs new file mode 100644 index 000000000000..1021ebc24824 --- /dev/null +++ b/src/Files.App.Controls/AdaptiveGridView/AdaptiveGridView.Properties.cs @@ -0,0 +1,159 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Windows.Input; + +namespace Files.App.Controls +{ + /// + /// The AdaptiveGridView control allows to present information within a Grid View perfectly adjusting the + /// total display available space. It reacts to changes in the layout as well as the content so it can adapt + /// to different form factors automatically. + /// + /// + /// The number and the width of items are calculated based on the + /// screen resolution in order to fully leverage the available screen space. The property ItemsHeight define + /// the items fixed height and the property DesiredWidth sets the minimum width for the elements to add a + /// new column. + /// + public partial class AdaptiveGridView + { + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty ItemClickCommandProperty = + DependencyProperty.Register(nameof(ItemClickCommand), typeof(ICommand), typeof(AdaptiveGridView), new PropertyMetadata(null)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty ItemHeightProperty = + DependencyProperty.Register(nameof(ItemHeight), typeof(double), typeof(AdaptiveGridView), new PropertyMetadata(double.NaN)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty OneRowModeEnabledProperty = + DependencyProperty.Register(nameof(OneRowModeEnabled), typeof(bool), typeof(AdaptiveGridView), new PropertyMetadata(false, (o, e) => { OnOneRowModeEnabledChanged(o, e.NewValue); })); + + /// + /// Identifies the dependency property. + /// + private static readonly DependencyProperty ItemWidthProperty = + DependencyProperty.Register(nameof(ItemWidth), typeof(double), typeof(AdaptiveGridView), new PropertyMetadata(double.NaN)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty DesiredWidthProperty = + DependencyProperty.Register(nameof(DesiredWidth), typeof(double), typeof(AdaptiveGridView), new PropertyMetadata(double.NaN, DesiredWidthChanged)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty StretchContentForSingleRowProperty = + DependencyProperty.Register(nameof(StretchContentForSingleRow), typeof(bool), typeof(AdaptiveGridView), new PropertyMetadata(true, OnStretchContentForSingleRowPropertyChanged)); + + private static void OnOneRowModeEnabledChanged(DependencyObject d, object newValue) + { + var self = d as AdaptiveGridView; + self.DetermineOneRowMode(); + } + + private static void DesiredWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var self = d as AdaptiveGridView; + self.RecalculateLayout(self.ActualWidth); + } + + private static void OnStretchContentForSingleRowPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var self = d as AdaptiveGridView; + self.RecalculateLayout(self.ActualWidth); + } + + /// + /// Gets or sets the desired width of each item + /// + /// The width of the desired. + public double DesiredWidth + { + get { return (double)GetValue(DesiredWidthProperty); } + set { SetValue(DesiredWidthProperty, value); } + } + + /// + /// Gets or sets a value indicating whether the control should stretch the content to fill at least one row. + /// + /// + /// If set to true (default) and there is only one row of items, the items will be stretched to fill the complete row. + /// If set to false, items will have their normal size, which means a gap can exist at the end of the row. + /// + /// A value indicating whether the control should stretch the content to fill at least one row. + public bool StretchContentForSingleRow + { + get { return (bool)GetValue(StretchContentForSingleRowProperty); } + set { SetValue(StretchContentForSingleRowProperty, value); } + } + + /// + /// Gets or sets the command to execute when an item is clicked and the IsItemClickEnabled property is true. + /// + /// The item click command. + public ICommand ItemClickCommand + { + get { return (ICommand)GetValue(ItemClickCommandProperty); } + set { SetValue(ItemClickCommandProperty, value); } + } + + /// + /// Gets or sets the height of each item in the grid. + /// + /// The height of the item. + public double ItemHeight + { + get { return (double)GetValue(ItemHeightProperty); } + set { SetValue(ItemHeightProperty, value); } + } + + /// + /// Gets or sets a value indicating whether only one row should be displayed. + /// + /// true if only one row is displayed; otherwise, false. + public bool OneRowModeEnabled + { + get { return (bool)GetValue(OneRowModeEnabledProperty); } + set { SetValue(OneRowModeEnabledProperty, value); } + } + + /// + /// Gets the template that defines the panel that controls the layout of items. + /// + /// + /// This property overrides the base ItemsPanel to prevent changing it. + /// + /// + /// An ItemsPanelTemplate that defines the panel to use for the layout of the items. + /// The default value for the ItemsControl is an ItemsPanelTemplate that specifies + /// a StackPanel. + /// + public new ItemsPanelTemplate ItemsPanel => base.ItemsPanel; + + private double ItemWidth + { + get { return (double)GetValue(ItemWidthProperty); } + set { SetValue(ItemWidthProperty, value); } + } + + private static int CalculateColumns(double containerWidth, double itemWidth) + { + var columns = itemWidth > 0 ? (int)Math.Round(containerWidth / itemWidth) : 0; + if (columns == 0) + { + columns = 1; + } + + return columns; + } + } +} diff --git a/src/Files.App.Controls/AdaptiveGridView/AdaptiveGridView.cs b/src/Files.App.Controls/AdaptiveGridView/AdaptiveGridView.cs new file mode 100644 index 000000000000..e348fbd682bd --- /dev/null +++ b/src/Files.App.Controls/AdaptiveGridView/AdaptiveGridView.cs @@ -0,0 +1,264 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.UI.Xaml.Data; +using Windows.Foundation.Collections; + +namespace Files.App.Controls +{ + /// + /// The AdaptiveGridView control allows to present information within a Grid View perfectly adjusting the + /// total display available space. It reacts to changes in the layout as well as the content so it can adapt + /// to different form factors automatically. + /// + /// + /// The number and the width of items are calculated based on the + /// screen resolution in order to fully leverage the available screen space. The property ItemsHeight define + /// the items fixed height and the property DesiredWidth sets the minimum width for the elements to add a + /// new column. + public partial class AdaptiveGridView : GridView + { + private bool _isLoaded; + private ScrollMode _savedVerticalScrollMode; + private ScrollMode _savedHorizontalScrollMode; + private ScrollBarVisibility _savedVerticalScrollBarVisibility; + private ScrollBarVisibility _savedHorizontalScrollBarVisibility; + private Orientation _savedOrientation; + private bool _needToRestoreScrollStates; + private bool _needContainerMarginForLayout; + + /// + /// Initializes a new instance of the class. + /// + public AdaptiveGridView() + { + IsTabStop = false; + SizeChanged += OnSizeChanged; + ItemClick += OnItemClick; + Items.VectorChanged += ItemsOnVectorChanged; + Loaded += OnLoaded; + Unloaded += OnUnloaded; + + // Prevent issues with higher DPIs and underlying panel. #1803 + UseLayoutRounding = false; + } + + /// + /// Prepares the specified element to display the specified item. + /// + /// The element that's used to display the specified item. + /// The item to display. + protected override void PrepareContainerForItemOverride(DependencyObject obj, object item) + { + base.PrepareContainerForItemOverride(obj, item); + if (obj is FrameworkElement element) + { + var heightBinding = new Binding() + { + Source = this, + Path = new PropertyPath("ItemHeight"), + Mode = BindingMode.TwoWay + }; + + var widthBinding = new Binding() + { + Source = this, + Path = new PropertyPath("ItemWidth"), + Mode = BindingMode.TwoWay + }; + + element.SetBinding(HeightProperty, heightBinding); + element.SetBinding(WidthProperty, widthBinding); + } + + if (obj is ContentControl contentControl) + { + contentControl.HorizontalContentAlignment = HorizontalAlignment.Stretch; + contentControl.VerticalContentAlignment = VerticalAlignment.Stretch; + } + + if (_needContainerMarginForLayout) + { + _needContainerMarginForLayout = false; + RecalculateLayout(ActualWidth); + } + } + + /// + /// Calculates the width of the grid items. + /// + /// The width of the container control. + /// The calculated item width. + protected virtual double CalculateItemWidth(double containerWidth) + { + if (double.IsNaN(DesiredWidth)) + { + return DesiredWidth; + } + + var columns = CalculateColumns(containerWidth, DesiredWidth); + + // If there's less items than there's columns, reduce the column count (if requested); + if (Items != null && Items.Count > 0 && Items.Count < columns && StretchContentForSingleRow) + { + columns = Items.Count; + } + + // subtract the margin from the width so we place the correct width for placement + var fallbackThickness = default(Thickness); + var itemMargin = AdaptiveHeightValueConverter.GetItemMargin(this, fallbackThickness); + if (itemMargin == fallbackThickness) + { + // No style explicitly defined, or no items or no container for the items + // We need to get an actual margin for proper layout + _needContainerMarginForLayout = true; + } + + return columns > 0 ? (containerWidth / columns) - itemMargin.Left - itemMargin.Right : 0; + } + + /// + /// Invoked whenever application code or internal processes (such as a rebuilding layout pass) call + /// ApplyTemplate. In simplest terms, this means the method is called just before a UI element displays + /// in your app. Override this method to influence the default post-template logic of a class. + /// + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + OnOneRowModeEnabledChanged(this, OneRowModeEnabled); + } + + private void ItemsOnVectorChanged(IObservableVector sender, IVectorChangedEventArgs @event) + { + if (!double.IsNaN(ActualWidth)) + { + // If the item count changes, check if more or less columns needs to be rendered, + // in case we were having fewer items than columns. + RecalculateLayout(ActualWidth); + } + } + + private void OnItemClick(object sender, ItemClickEventArgs e) + { + var cmd = ItemClickCommand; + if (cmd != null) + { + if (cmd.CanExecute(e.ClickedItem)) + { + cmd.Execute(e.ClickedItem); + } + } + } + + private void OnSizeChanged(object sender, SizeChangedEventArgs e) + { + // If we are in center alignment, we only care about relayout if the number of columns we can display changes + // Fixes #1737 + if (HorizontalAlignment != HorizontalAlignment.Stretch) + { + var prevColumns = CalculateColumns(e.PreviousSize.Width, DesiredWidth); + var newColumns = CalculateColumns(e.NewSize.Width, DesiredWidth); + + // If the width of the internal list view changes, check if more or less columns needs to be rendered. + if (prevColumns != newColumns) + { + RecalculateLayout(e.NewSize.Width); + } + } + else if (e.PreviousSize.Width != e.NewSize.Width) + { + // We need to recalculate width as our size changes to adjust internal items. + RecalculateLayout(e.NewSize.Width); + } + } + + private void OnLoaded(object sender, RoutedEventArgs e) + { + _isLoaded = true; + DetermineOneRowMode(); + } + + private void OnUnloaded(object sender, RoutedEventArgs e) + { + _isLoaded = false; + } + + private void DetermineOneRowMode() + { + if (_isLoaded) + { + var itemsWrapGridPanel = ItemsPanelRoot as ItemsWrapGrid; + + if (OneRowModeEnabled) + { + var b = new Binding() + { + Source = this, + Path = new PropertyPath("ItemHeight"), + Converter = new AdaptiveHeightValueConverter(), + ConverterParameter = this + }; + + if (itemsWrapGridPanel != null) + { + _savedOrientation = itemsWrapGridPanel.Orientation; + itemsWrapGridPanel.Orientation = Orientation.Vertical; + } + + SetBinding(MaxHeightProperty, b); + + _savedHorizontalScrollMode = ScrollViewer.GetHorizontalScrollMode(this); + _savedVerticalScrollMode = ScrollViewer.GetVerticalScrollMode(this); + _savedHorizontalScrollBarVisibility = ScrollViewer.GetHorizontalScrollBarVisibility(this); + _savedVerticalScrollBarVisibility = ScrollViewer.GetVerticalScrollBarVisibility(this); + _needToRestoreScrollStates = true; + + ScrollViewer.SetVerticalScrollMode(this, ScrollMode.Disabled); + ScrollViewer.SetVerticalScrollBarVisibility(this, ScrollBarVisibility.Hidden); + ScrollViewer.SetHorizontalScrollBarVisibility(this, ScrollBarVisibility.Visible); + ScrollViewer.SetHorizontalScrollMode(this, ScrollMode.Enabled); + } + else + { + ClearValue(MaxHeightProperty); + + if (!_needToRestoreScrollStates) + { + return; + } + + _needToRestoreScrollStates = false; + + if (itemsWrapGridPanel != null) + { + itemsWrapGridPanel.Orientation = _savedOrientation; + } + + ScrollViewer.SetVerticalScrollMode(this, _savedVerticalScrollMode); + ScrollViewer.SetVerticalScrollBarVisibility(this, _savedVerticalScrollBarVisibility); + ScrollViewer.SetHorizontalScrollBarVisibility(this, _savedHorizontalScrollBarVisibility); + ScrollViewer.SetHorizontalScrollMode(this, _savedHorizontalScrollMode); + } + } + } + + private void RecalculateLayout(double containerWidth) + { + var itemsPanel = ItemsPanelRoot as Panel; + var panelMargin = itemsPanel != null ? + itemsPanel.Margin.Left + itemsPanel.Margin.Right : + 0; + var padding = Padding.Left + Padding.Right; + var border = BorderThickness.Left + BorderThickness.Right; + + // width should be the displayable width + containerWidth = containerWidth - padding - panelMargin - border; + if (containerWidth > 0) + { + var newWidth = CalculateItemWidth(containerWidth); + ItemWidth = Math.Floor(newWidth); + } + } + } +} diff --git a/src/Files.App.Controls/AdaptiveGridView/AdaptiveHeightValueConverter.cs b/src/Files.App.Controls/AdaptiveGridView/AdaptiveHeightValueConverter.cs new file mode 100644 index 000000000000..4d9fd74339c6 --- /dev/null +++ b/src/Files.App.Controls/AdaptiveGridView/AdaptiveHeightValueConverter.cs @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.UI.Xaml.Data; + +namespace Files.App.Controls +{ + internal partial class AdaptiveHeightValueConverter : IValueConverter + { + private Thickness thickness = new Thickness(0, 0, 4, 4); + + public Thickness DefaultItemMargin + { + get { return thickness; } + set { thickness = value; } + } + + public object Convert(object value, Type targetType, object parameter, string language) + { + if (value != null) + { + var gridView = (GridView)parameter; + if (gridView == null) + { + return value; + } + + double.TryParse(value.ToString(), out double height); + + var padding = gridView.Padding; + var margin = GetItemMargin(gridView, DefaultItemMargin); + height = height + margin.Top + margin.Bottom + padding.Top + padding.Bottom; + + return height; + } + + return double.NaN; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } + + internal static Thickness GetItemMargin(GridView view, Thickness fallback = default(Thickness)) + { + var setter = view.ItemContainerStyle?.Setters.OfType().FirstOrDefault(s => s.Property == FrameworkElement.MarginProperty); + if (setter != null) + { + return (Thickness)setter.Value; + } + else + { + if (view.Items.Count > 0) + { + var container = (GridViewItem)view.ContainerFromIndex(0); + if (container != null) + { + return container.Margin; + } + } + + // Use the default thickness for a GridViewItem + return fallback; + } + } + } +} diff --git a/src/Files.App.Controls/BladeView/BladeItem.Events.cs b/src/Files.App.Controls/BladeView/BladeItem.Events.cs new file mode 100644 index 000000000000..4b0706c2a16e --- /dev/null +++ b/src/Files.App.Controls/BladeView/BladeItem.Events.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Files.App.Controls +{ + /// + /// The Blade is used as a child in the BladeView + /// + public partial class BladeItem + { + /// + /// Fires when the blade is opened or closed + /// + public event EventHandler? VisibilityChanged; + } +} diff --git a/src/Files.App.Controls/BladeView/BladeItem.Properties.cs b/src/Files.App.Controls/BladeView/BladeItem.Properties.cs new file mode 100644 index 000000000000..327a0d59fa2d --- /dev/null +++ b/src/Files.App.Controls/BladeView/BladeItem.Properties.cs @@ -0,0 +1,89 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.UI; +using Microsoft.UI.Xaml.Media; + +namespace Files.App.Controls +{ + /// + /// The Blade is used as a child in the BladeView + /// + public partial class BladeItem + { + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty CloseButtonBackgroundProperty = DependencyProperty.Register(nameof(CloseButtonBackground), typeof(Brush), typeof(BladeItem), new PropertyMetadata(default(Brush))); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty CloseButtonVisibilityProperty = DependencyProperty.Register(nameof(CloseButtonVisibility), typeof(Visibility), typeof(BladeItem), new PropertyMetadata(default(Visibility))); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty IsOpenProperty = DependencyProperty.Register(nameof(IsOpen), typeof(bool), typeof(BladeItem), new PropertyMetadata(true, IsOpenChangedCallback)); + + /// + /// Identifies the dependency property + /// + public static readonly DependencyProperty CloseButtonForegroundProperty = DependencyProperty.Register(nameof(CloseButtonForeground), typeof(Brush), typeof(BladeItem), new PropertyMetadata(new SolidColorBrush(Colors.Black))); + + private WeakReference _parentBladeView; + + /// + /// Gets or sets the foreground color of the close button + /// + public Brush CloseButtonForeground + { + get { return (Brush)GetValue(CloseButtonForegroundProperty); } + set { SetValue(CloseButtonForegroundProperty, value); } + } + + /// + /// Gets or sets the visibility of the close button + /// + public Visibility CloseButtonVisibility + { + get { return (Visibility)GetValue(CloseButtonVisibilityProperty); } + set { SetValue(CloseButtonVisibilityProperty, value); } + } + + /// + /// Gets or sets the background color of the default close button in the title bar + /// + public Brush CloseButtonBackground + { + get { return (Brush)GetValue(CloseButtonBackgroundProperty); } + set { SetValue(CloseButtonBackgroundProperty, value); } + } + + /// + /// Gets or sets a value indicating whether this blade is opened + /// + public bool IsOpen + { + get { return (bool)GetValue(IsOpenProperty); } + set { SetValue(IsOpenProperty, value); } + } + + internal BladeView ParentBladeView + { + get + { + this._parentBladeView.TryGetTarget(out var bladeView); + return bladeView; + } + set => this._parentBladeView = new WeakReference(value); + } + + private static void IsOpenChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) + { + BladeItem bladeItem = (BladeItem)dependencyObject; + bladeItem.Visibility = bladeItem.IsOpen ? Visibility.Visible : Visibility.Collapsed; + bladeItem.VisibilityChanged?.Invoke(bladeItem, bladeItem.Visibility); + } + } +} diff --git a/src/Files.App.Controls/BladeView/BladeItem.cs b/src/Files.App.Controls/BladeView/BladeItem.cs new file mode 100644 index 000000000000..ab3df482a544 --- /dev/null +++ b/src/Files.App.Controls/BladeView/BladeItem.cs @@ -0,0 +1,255 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using CommunityToolkit.WinUI; +using Microsoft.UI.Input; +using Microsoft.UI.Xaml.Automation.Peers; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Windows.Foundation; + +namespace Files.App.Controls +{ + /// + /// The Blade is used as a child in the BladeView + /// + [TemplatePart(Name = "CloseButton", Type = typeof(Button))] + public partial class BladeItem : ContentControl + { + private const double MINIMUM_WIDTH = 150; + private const double DEFAULT_WIDTH = 200; // Default width for the blade item + + private Button _closeButton; + private Border _bladeResizer; + private bool _draggingSidebarResizer; + private double _preManipulationSidebarWidth = 0; + + /// + /// Initializes a new instance of the class. + /// + public BladeItem() + { + DefaultStyleKey = typeof(BladeItem); + } + + /// + /// Override default OnApplyTemplate to capture child controls + /// + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + _closeButton = GetTemplateChild("CloseButton") as Button; + + if (_closeButton == null) + { + return; + } + + _closeButton.Click -= CloseButton_Click; + _closeButton.Click += CloseButton_Click; + + _bladeResizer = GetTemplateChild("BladeResizer") as Border; + + if (_bladeResizer != null) + { + _bladeResizer.ManipulationStarted -= BladeResizer_ManipulationStarted; + _bladeResizer.ManipulationStarted += BladeResizer_ManipulationStarted; + + _bladeResizer.ManipulationDelta -= BladeResizer_ManipulationDelta; + _bladeResizer.ManipulationDelta += BladeResizer_ManipulationDelta; + + _bladeResizer.ManipulationCompleted -= BladeResizer_ManipulationCompleted; + _bladeResizer.ManipulationCompleted += BladeResizer_ManipulationCompleted; + + _bladeResizer.PointerEntered -= BladeResizer_PointerEntered; + _bladeResizer.PointerEntered += BladeResizer_PointerEntered; + + _bladeResizer.PointerExited -= BladeResizer_PointerExited; + _bladeResizer.PointerExited += BladeResizer_PointerExited; + + _bladeResizer.DoubleTapped -= BladeResizer_DoubleTapped; + _bladeResizer.DoubleTapped += BladeResizer_DoubleTapped; + } + } + + /// + /// Creates AutomationPeer () + /// + /// An automation peer for this . + protected override AutomationPeer OnCreateAutomationPeer() + { + return new BladeItemAutomationPeer(this); + } + + private void CloseButton_Click(object sender, RoutedEventArgs e) + { + IsOpen = false; + } + + private void BladeResizer_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e) + { + _draggingSidebarResizer = true; + _preManipulationSidebarWidth = ActualWidth; + VisualStateManager.GoToState(this, "ResizerPressed", true); + e.Handled = true; + } + + private void BladeResizer_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e) + { + var newWidth = _preManipulationSidebarWidth + e.Cumulative.Translation.X; + if (newWidth < MINIMUM_WIDTH) + newWidth = MINIMUM_WIDTH; + + Width = newWidth; + e.Handled = true; + } + + private void BladeResizer_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e) + { + _draggingSidebarResizer = false; + VisualStateManager.GoToState(this, "ResizerNormal", true); + e.Handled = true; + } + + private void BladeResizer_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e) + { + SetWidth(); + e.Handled = true; + } + + public void SetWidth() + { + var optimalWidth = CalculateOptimalWidth(); + if (optimalWidth > 0) + { + Width = Math.Max(optimalWidth, MINIMUM_WIDTH); + } + else + { + // Fallback to default width if calculation fails + Width = DEFAULT_WIDTH; + } + } + + private double CalculateOptimalWidth() + { + try + { + // Look for any ListView within this BladeItem that contains text content + var listView = this.FindDescendant(); + if (listView?.Items == null || !listView.Items.Any()) + return 0; + + // Calculate the maximum width needed by measuring text content + var maxTextWidth = MeasureContentWidth(listView); + + // Add padding for icon, margins, and other UI elements + // Icon width (32) + margins (24) + padding (24) + chevron/tags (40) = 120 + var totalPadding = 120; + + return maxTextWidth + totalPadding; + } + catch (Exception) + { + return 0; + } + } + + private double MeasureContentWidth(ListView listView) + { + try + { + double maxWidth = 0; + + // Find all TextBlocks in the ListView using visual tree walking + var textBlocks = GetTextBlocksFromVisualTree(listView); + + if (textBlocks.Any()) + { + // Measure each TextBlock and find the widest one + foreach (var textBlock in textBlocks) + { + if (string.IsNullOrEmpty(textBlock.Text)) + continue; + + // Create a measuring TextBlock with the same properties + var measuringBlock = new TextBlock + { + Text = textBlock.Text, + FontSize = textBlock.FontSize, + FontFamily = textBlock.FontFamily, + FontWeight = textBlock.FontWeight, + FontStyle = textBlock.FontStyle + }; + + measuringBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + maxWidth = Math.Max(maxWidth, measuringBlock.DesiredSize.Width); + } + } + else + { + // Fallback: estimate based on item count and average text width + var itemCount = listView.Items.Count; + if (itemCount > 0) + { + // Estimate average filename length and multiply by character width + var estimatedCharWidth = 8; // Approximate pixel width per character + var estimatedMaxLength = Math.Min(50, Math.Max(20, itemCount * 2)); // Heuristic + maxWidth = estimatedCharWidth * estimatedMaxLength; + } + } + + return maxWidth; + } + catch (Exception) + { + // Fallback calculation + return DEFAULT_WIDTH; // Default reasonable width + } + } + + private List GetTextBlocksFromVisualTree(DependencyObject parent) + { + var textBlocks = new List(); + + if (parent == null) + return textBlocks; + + var childrenCount = VisualTreeHelper.GetChildrenCount(parent); + for (int i = 0; i < childrenCount; i++) + { + var child = VisualTreeHelper.GetChild(parent, i); + + if (child is TextBlock textBlock) + { + textBlocks.Add(textBlock); + } + + // Recursively search child elements + textBlocks.AddRange(GetTextBlocksFromVisualTree(child)); + } + + return textBlocks; + } + + private void BladeResizer_PointerEntered(object sender, PointerRoutedEventArgs e) + { + var sidebarResizer = (FrameworkElement)sender; + sidebarResizer.ChangeCursor(InputSystemCursor.Create(InputSystemCursorShape.SizeWestEast)); + VisualStateManager.GoToState(this, "ResizerPointerOver", true); + e.Handled = true; + } + + private void BladeResizer_PointerExited(object sender, PointerRoutedEventArgs e) + { + if (_draggingSidebarResizer) + return; + + var sidebarResizer = (FrameworkElement)sender; + sidebarResizer.ChangeCursor(InputSystemCursor.Create(InputSystemCursorShape.Arrow)); + VisualStateManager.GoToState(this, "ResizerNormal", true); + e.Handled = true; + } + } +} diff --git a/src/Files.App.Controls/BladeView/BladeItemAutomationPeer.cs b/src/Files.App.Controls/BladeView/BladeItemAutomationPeer.cs new file mode 100644 index 000000000000..7c6ec7f7764a --- /dev/null +++ b/src/Files.App.Controls/BladeView/BladeItemAutomationPeer.cs @@ -0,0 +1,132 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using CommunityToolkit.WinUI; +using Microsoft.UI.Xaml.Automation; +using Microsoft.UI.Xaml.Automation.Peers; + +namespace Files.App.Controls +{ + /// + /// Defines a framework element automation peer for the . + /// + public partial class BladeItemAutomationPeer : FrameworkElementAutomationPeer + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The that is associated with this . + /// + public BladeItemAutomationPeer(BladeItem owner) + : base(owner) + { + } + + private BladeItem OwnerBladeItem + { + get { return this.Owner as BladeItem; } + } + + /// + /// Gets the control type for the element that is associated with the UI Automation peer. + /// + /// The control type. + protected override AutomationControlType GetAutomationControlTypeCore() + { + return AutomationControlType.ListItem; + } + + /// + /// Called by GetClassName that gets a human readable name that, in addition to AutomationControlType, + /// differentiates the control represented by this AutomationPeer. + /// + /// The string that contains the name. + protected override string GetClassNameCore() + { + return Owner.GetType().Name; + } + + /// + /// Called by GetName. + /// + /// + /// Returns the first of these that is not null or empty: + /// - Value returned by the base implementation + /// - Name of the owning BladeItem + /// - BladeItem class name + /// + protected override string GetNameCore() + { + string name = AutomationProperties.GetName(this.OwnerBladeItem); + if (!string.IsNullOrEmpty(name)) + { + return name; + } + + name = this.OwnerBladeItem.Name; + if (!string.IsNullOrEmpty(name)) + { + return name; + } + + TextBlock textBlock = this.OwnerBladeItem.FindDescendant(); + if (textBlock != null) + { + return textBlock.Text; + } + + name = base.GetNameCore(); + if (!string.IsNullOrEmpty(name)) + { + return name; + } + + return string.Empty; + } + + /// + /// Returns the size of the set where the element that is associated with the automation peer is located. + /// + /// + /// The size of the set. + /// + protected override int GetSizeOfSetCore() + { + int sizeOfSet = base.GetSizeOfSetCore(); + + if (sizeOfSet != -1) + { + return sizeOfSet; + } + + BladeItem owner = this.OwnerBladeItem; + BladeView parent = owner.ParentBladeView; + sizeOfSet = parent.Items.Count; + + return sizeOfSet; + } + + /// + /// Returns the ordinal position in the set for the element that is associated with the automation peer. + /// + /// + /// The ordinal position in the set. + /// + protected override int GetPositionInSetCore() + { + int positionInSet = base.GetPositionInSetCore(); + + if (positionInSet != -1) + { + return positionInSet; + } + + BladeItem owner = this.OwnerBladeItem; + BladeView parent = owner.ParentBladeView; + positionInSet = parent.IndexFromContainer(owner); + + return positionInSet; + } + } +} diff --git a/src/Files.App.Controls/BladeView/BladeMode.cs b/src/Files.App.Controls/BladeView/BladeMode.cs new file mode 100644 index 000000000000..7e5250700ee2 --- /dev/null +++ b/src/Files.App.Controls/BladeView/BladeMode.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Files.App.Controls +{ + /// + /// The blade mode. + /// + public enum BladeMode + { + /// + /// Default mode : each blade will take the specified Width and Height + /// + Normal, + + /// + /// Fullscreen mode : each blade will take the entire Width and Height of the UI control container (cf ) + /// + Fullscreen + } +} diff --git a/src/Files.App.Controls/BladeView/BladeView.Events.cs b/src/Files.App.Controls/BladeView/BladeView.Events.cs new file mode 100644 index 000000000000..69984d8a7281 --- /dev/null +++ b/src/Files.App.Controls/BladeView/BladeView.Events.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Files.App.Controls +{ + /// + /// A container that hosts controls in a horizontal scrolling list + /// Based on the Azure portal UI + /// + public partial class BladeView + { + /// + /// Fires whenever a is opened + /// + public event EventHandler BladeOpened; + + /// + /// Fires whenever a is closed + /// + public event EventHandler BladeClosed; + } +} diff --git a/src/Files.App.Controls/BladeView/BladeView.Properties.cs b/src/Files.App.Controls/BladeView/BladeView.Properties.cs new file mode 100644 index 000000000000..6956c1117bc0 --- /dev/null +++ b/src/Files.App.Controls/BladeView/BladeView.Properties.cs @@ -0,0 +1,87 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Windows.Foundation; + +namespace Files.App.Controls +{ + /// + /// A container that hosts controls in a horizontal scrolling list + /// Based on the Azure portal UI + /// + public partial class BladeView + { + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty ActiveBladesProperty = DependencyProperty.Register(nameof(ActiveBlades), typeof(IList), typeof(BladeView), new PropertyMetadata(null)); + + /// + /// Identifies the attached property. + /// + public static readonly DependencyProperty BladeModeProperty = DependencyProperty.RegisterAttached(nameof(BladeMode), typeof(BladeMode), typeof(BladeView), new PropertyMetadata(BladeMode.Normal, OnBladeModeChanged)); + + /// + /// Gets or sets a collection of visible blades + /// + public IList ActiveBlades + { + get { return (IList)GetValue(ActiveBladesProperty); } + set { SetValue(ActiveBladesProperty, value); } + } + + /// + /// Gets or sets a value indicating whether blade mode (ex: whether blades are full screen or not) + /// + public BladeMode BladeMode + { + get { return (BladeMode)GetValue(BladeModeProperty); } + set { SetValue(BladeModeProperty, value); } + } + + private static void OnBladeModeChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) + { + var bladeView = (BladeView)dependencyObject; + + if (bladeView.BladeMode == BladeMode.Fullscreen) + { + // Cache previous values of blade items properties (width & height) + bladeView._cachedBladeItemSizes.Clear(); + + if (bladeView.Items != null) + { + foreach (var item in bladeView.Items) + { + var bladeItem = bladeView.GetBladeItem(item); + bladeView._cachedBladeItemSizes.Add(bladeItem, new Size(bladeItem.Width, bladeItem.Height)); + } + } + + VisualStateManager.GoToState(bladeView, "FullScreen", false); + } + + if (bladeView.BladeMode == BladeMode.Normal) + { + // Reset blade items properties & clear cache + foreach (var kvBladeItemSize in bladeView._cachedBladeItemSizes) + { + kvBladeItemSize.Key.Width = kvBladeItemSize.Value.Width; + kvBladeItemSize.Key.Height = kvBladeItemSize.Value.Height; + } + + bladeView._cachedBladeItemSizes.Clear(); + + VisualStateManager.GoToState(bladeView, "Normal", false); + } + + // Execute change of blade item size + bladeView.AdjustBladeItemSize(); + } + + private static void OnOpenBladesChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) + { + var bladeView = (BladeView)dependencyObject; + bladeView.CycleBlades(); + } + } +} diff --git a/src/Files.App.Controls/BladeView/BladeView.cs b/src/Files.App.Controls/BladeView/BladeView.cs new file mode 100644 index 000000000000..57794b3f08f2 --- /dev/null +++ b/src/Files.App.Controls/BladeView/BladeView.cs @@ -0,0 +1,201 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using CommunityToolkit.WinUI; +using Microsoft.UI.Xaml.Automation.Peers; +using Windows.Foundation; +using Windows.Foundation.Collections; + +namespace Files.App.Controls +{ + /// + /// A container that hosts controls in a horizontal scrolling list + /// Based on the Azure portal UI + /// + public partial class BladeView : ItemsControl + { + private ScrollViewer _scrollViewer; + + private Dictionary _cachedBladeItemSizes = new Dictionary(); + + /// + /// Initializes a new instance of the class. + /// + public BladeView() + { + DefaultStyleKey = typeof(BladeView); + + Items.VectorChanged += ItemsVectorChanged; + + Loaded += (sender, e) => AdjustBladeItemSize(); + SizeChanged += (sender, e) => AdjustBladeItemSize(); + } + + /// + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + CycleBlades(); + AdjustBladeItemSize(); + } + + /// + protected override DependencyObject GetContainerForItemOverride() + { + return new BladeItem(); + } + + /// + protected override bool IsItemItsOwnContainerOverride(object item) + { + return item is BladeItem; + } + + /// + protected override void PrepareContainerForItemOverride(DependencyObject element, object item) + { + var blade = element as BladeItem; + if (blade != null) + { + blade.VisibilityChanged += BladeOnVisibilityChanged; + blade.ParentBladeView = this; + } + + base.PrepareContainerForItemOverride(element, item); + CycleBlades(); + } + + /// + protected override void ClearContainerForItemOverride(DependencyObject element, object item) + { + var blade = element as BladeItem; + if (blade != null) + { + blade.VisibilityChanged -= BladeOnVisibilityChanged; + } + + base.ClearContainerForItemOverride(element, item); + } + + /// + /// Creates AutomationPeer () + /// + /// An automation peer for this . + protected override AutomationPeer OnCreateAutomationPeer() + { + return new BladeViewAutomationPeer(this); + } + + private void CycleBlades() + { + ActiveBlades = new ObservableCollection(); + foreach (var item in Items) + { + BladeItem blade = GetBladeItem(item); + if (blade != null) + { + if (blade.IsOpen) + { + ActiveBlades.Add(blade); + } + } + } + } + + private BladeItem GetBladeItem(object item) + { + BladeItem blade = item as BladeItem; + if (blade == null) + { + blade = (BladeItem)ContainerFromItem(item); + } + + return blade; + } + + private async void BladeOnVisibilityChanged(object sender, Visibility visibility) + { + var blade = sender as BladeItem; + + if (visibility == Visibility.Visible) + { + if (Items == null) + { + return; + } + + var item = ItemFromContainer(blade); + Items.Remove(item); + Items.Add(item); + BladeOpened?.Invoke(this, blade); + ActiveBlades.Add(blade); + UpdateLayout(); + + // Need to do this because of touch. See more information here: https://github.com/CommunityToolkit/WindowsCommunityToolkit/issues/760#issuecomment-276466464 + await DispatcherQueue.EnqueueAsync( + () => + { + GetScrollViewer()?.ChangeView(_scrollViewer.ScrollableWidth, null, null); + }, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low); + + return; + } + + BladeClosed?.Invoke(this, blade); + ActiveBlades.Remove(blade); + } + + private ScrollViewer GetScrollViewer() + { + return _scrollViewer ?? (_scrollViewer = this.FindDescendant()); + } + + public void ScrollToEnd() + { + LayoutUpdated += OnLayoutUpdatedScrollToEnd; + } + + private void OnLayoutUpdatedScrollToEnd(object sender, object e) + { + LayoutUpdated -= OnLayoutUpdatedScrollToEnd; + GetScrollViewer()?.ChangeView(_scrollViewer.ScrollableWidth, null, null, false); + } + + private void AdjustBladeItemSize() + { + // Adjust blade items to be full screen + if (BladeMode == BladeMode.Fullscreen && GetScrollViewer() != null) + { + foreach (var item in Items) + { + var blade = GetBladeItem(item); + blade.Width = _scrollViewer.ActualWidth; + blade.Height = _scrollViewer.ActualHeight; + } + } + } + + private void ItemsVectorChanged(IObservableVector sender, IVectorChangedEventArgs e) + { + if (BladeMode == BladeMode.Fullscreen) + { + var bladeItem = GetBladeItem(sender[(int)e.Index]); + if (bladeItem != null) + { + if (!_cachedBladeItemSizes.ContainsKey(bladeItem)) + { + // Execute change of blade item size when a blade item is added in Fullscreen mode + _cachedBladeItemSizes.Add(bladeItem, new Size(bladeItem.Width, bladeItem.Height)); + AdjustBladeItemSize(); + } + } + } + else if (e.CollectionChange == CollectionChange.ItemInserted) + { + UpdateLayout(); + // The following line doesn't work as expected due to the items not being fully loaded yet and thus the scrollable width not being accurate. + //GetScrollViewer()?.ChangeView(_scrollViewer.ScrollableWidth, null, null); + } + } + } +} diff --git a/src/Files.App.Controls/BladeView/BladeView.xaml b/src/Files.App.Controls/BladeView/BladeView.xaml new file mode 100644 index 000000000000..3d223df0ca5a --- /dev/null +++ b/src/Files.App.Controls/BladeView/BladeView.xaml @@ -0,0 +1,167 @@ + + + + + + + + \ No newline at end of file diff --git a/src/Files.App.Controls/BladeView/BladeViewAutomationPeer.cs b/src/Files.App.Controls/BladeView/BladeViewAutomationPeer.cs new file mode 100644 index 000000000000..ffa0424349bb --- /dev/null +++ b/src/Files.App.Controls/BladeView/BladeViewAutomationPeer.cs @@ -0,0 +1,111 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.UI.Xaml.Automation; +using Microsoft.UI.Xaml.Automation.Peers; + +namespace Files.App.Controls +{ + /// + /// Defines a framework element automation peer for the control. + /// + public partial class BladeViewAutomationPeer : ItemsControlAutomationPeer + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The that is associated with this . + /// + public BladeViewAutomationPeer(BladeView owner) + : base(owner) + { + } + + private BladeView OwningBladeView + { + get + { + return Owner as BladeView; + } + } + + /// + /// Gets the control type for the element that is associated with the UI Automation peer. + /// + /// The control type. + protected override AutomationControlType GetAutomationControlTypeCore() + { + return AutomationControlType.List; + } + + /// + /// Called by GetClassName that gets a human readable name that, in addition to AutomationControlType, + /// differentiates the control represented by this AutomationPeer. + /// + /// The string that contains the name. + protected override string GetClassNameCore() + { + return Owner.GetType().Name; + } + + /// + /// Called by GetName. + /// + /// + /// Returns the first of these that is not null or empty: + /// - Value returned by the base implementation + /// - Name of the owning BladeView + /// - BladeView class name + /// + protected override string GetNameCore() + { + string name = AutomationProperties.GetName(this.OwningBladeView); + if (!string.IsNullOrEmpty(name)) + { + return name; + } + + name = this.OwningBladeView.Name; + if (!string.IsNullOrEmpty(name)) + { + return name; + } + + name = base.GetNameCore(); + if (!string.IsNullOrEmpty(name)) + { + return name; + } + + return string.Empty; + } + + /// + /// Gets the collection of elements that are represented in the UI Automation tree as immediate + /// child elements of the automation peer. + /// + /// The children elements. + protected override IList GetChildrenCore() + { + BladeView owner = OwningBladeView; + + ItemCollection items = owner.Items; + if (items.Count <= 0) + { + return null; + } + + List peers = new List(items.Count); + for (int i = 0; i < items.Count; i++) + { + if (owner.ContainerFromIndex(i) is BladeItem element) + { + peers.Add(FromElement(element) ?? CreatePeerForElement(element)); + } + } + + return peers; + } + } +} diff --git a/src/Files.App.Controls/BreadcrumbBar/BreadcrumbBar.Properties.cs b/src/Files.App.Controls/BreadcrumbBar/BreadcrumbBar.Properties.cs new file mode 100644 index 000000000000..d7586b1cd9c2 --- /dev/null +++ b/src/Files.App.Controls/BreadcrumbBar/BreadcrumbBar.Properties.cs @@ -0,0 +1,28 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using CommunityToolkit.WinUI; + +namespace Files.App.Controls +{ + public partial class BreadcrumbBar : Control + { + [GeneratedDependencyProperty] + public partial FrameworkElement? RootItem { get; set; } + + [GeneratedDependencyProperty] + public partial object? ItemsSource { get; set; } + + [GeneratedDependencyProperty] + public partial object? ItemTemplate { get; set; } + + [GeneratedDependencyProperty] + public partial string? EllipsisButtonToolTip { get; set; } + + [GeneratedDependencyProperty] + public partial string? RootItemToolTip { get; set; } + + [GeneratedDependencyProperty] + public partial string? RootItemChevronToolTip { get; set; } + } +} diff --git a/src/Files.App.Controls/BreadcrumbBar/BreadcrumbBar.cs b/src/Files.App.Controls/BreadcrumbBar/BreadcrumbBar.cs new file mode 100644 index 000000000000..d7d86ce2c38b --- /dev/null +++ b/src/Files.App.Controls/BreadcrumbBar/BreadcrumbBar.cs @@ -0,0 +1,166 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Automation; +using Microsoft.UI.Xaml.Input; +using Windows.Foundation; + +namespace Files.App.Controls +{ + public partial class BreadcrumbBar : Control + { + // Constants + + private const string TemplatePartName_RootBreadcrumbBarItem = "PART_RootBreadcrumbBarItem"; + private const string TemplatePartName_EllipsisBreadcrumbBarItem = "PART_EllipsisBreadcrumbBarItem"; + private const string TemplatePartName_MainItemsRepeater = "PART_MainItemsRepeater"; + + // Fields + + private readonly BreadcrumbBarLayout _itemsRepeaterLayout; + + private BreadcrumbBarItem? _rootBreadcrumbBarItem; + private BreadcrumbBarItem? _ellipsisBreadcrumbBarItem; + private BreadcrumbBarItem? _lastBreadcrumbBarItem; + private ItemsRepeater? _itemsRepeater; + + private bool _isEllipsisRendered; + + // Properties + + public int IndexAfterEllipsis + => _itemsRepeaterLayout.IndexAfterEllipsis; + + // Events + + public event TypedEventHandler? ItemClicked; + public event EventHandler? ItemDropDownFlyoutOpening; + public event EventHandler? ItemDropDownFlyoutClosed; + + // Constructor + + public BreadcrumbBar() + { + DefaultStyleKey = typeof(BreadcrumbBar); + + _itemsRepeaterLayout = new(this); + } + + // Methods + + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + _rootBreadcrumbBarItem = GetTemplateChild(TemplatePartName_RootBreadcrumbBarItem) as BreadcrumbBarItem + ?? throw new MissingFieldException($"Could not find {TemplatePartName_RootBreadcrumbBarItem} in the given {nameof(BreadcrumbBar)}'s style."); + _ellipsisBreadcrumbBarItem = GetTemplateChild(TemplatePartName_EllipsisBreadcrumbBarItem) as BreadcrumbBarItem + ?? throw new MissingFieldException($"Could not find {TemplatePartName_EllipsisBreadcrumbBarItem} in the given {nameof(BreadcrumbBar)}'s style."); + _itemsRepeater = GetTemplateChild(TemplatePartName_MainItemsRepeater) as ItemsRepeater + ?? throw new MissingFieldException($"Could not find {TemplatePartName_MainItemsRepeater} in the given {nameof(BreadcrumbBar)}'s style."); + + _rootBreadcrumbBarItem.SetOwner(this); + _ellipsisBreadcrumbBarItem.SetOwner(this); + _itemsRepeater.Layout = _itemsRepeaterLayout; + + _itemsRepeater.ElementPrepared += ItemsRepeater_ElementPrepared; + _itemsRepeater.ElementClearing += ItemsRepeater_ElementClearing; + _itemsRepeater.ItemsSourceView.CollectionChanged += ItemsSourceView_CollectionChanged; + } + + internal protected virtual void RaiseItemClickedEvent(BreadcrumbBarItem item, PointerRoutedEventArgs? pointerRoutedEventArgs = null) + { + var index = _itemsRepeater?.GetElementIndex(item) ?? throw new ArgumentNullException($"{_itemsRepeater} is null."); + var eventArgs = new BreadcrumbBarItemClickedEventArgs(item, index, item == _rootBreadcrumbBarItem, pointerRoutedEventArgs); + ItemClicked?.Invoke(this, eventArgs); + } + + internal protected virtual void RaiseItemDropDownFlyoutOpening(BreadcrumbBarItem item, MenuFlyout flyout) + { + var index = _itemsRepeater?.GetElementIndex(item) ?? throw new ArgumentNullException($"{_itemsRepeater} is null."); + ItemDropDownFlyoutOpening?.Invoke(this, new(flyout, item, index, item == _rootBreadcrumbBarItem)); + } + + internal protected virtual void RaiseItemDropDownFlyoutClosed(BreadcrumbBarItem item, MenuFlyout flyout) + { + var index = _itemsRepeater?.GetElementIndex(item) ?? throw new ArgumentNullException($"{_itemsRepeater} is null."); + ItemDropDownFlyoutClosed?.Invoke(this, new(flyout, item, index, item == _rootBreadcrumbBarItem)); + } + + internal protected virtual void OnLayoutUpdated() + { + if (_itemsRepeater is null || (_itemsRepeaterLayout.IndexAfterEllipsis > _itemsRepeaterLayout.VisibleItemsCount && _isEllipsisRendered)) + return; + + if (_ellipsisBreadcrumbBarItem is not null && _isEllipsisRendered != _itemsRepeaterLayout.EllipsisIsRendered) + _ellipsisBreadcrumbBarItem.Visibility = _itemsRepeaterLayout.EllipsisIsRendered ? Visibility.Visible : Visibility.Collapsed; + + _isEllipsisRendered = _itemsRepeaterLayout.EllipsisIsRendered; + + for (int accessibilityIndex = 0, collectionIndex = _itemsRepeaterLayout.IndexAfterEllipsis; + accessibilityIndex < _itemsRepeaterLayout.VisibleItemsCount; + accessibilityIndex++, collectionIndex++) + { + if (_itemsRepeater.TryGetElement(collectionIndex) is { } element) + { + element.SetValue(AutomationProperties.PositionInSetProperty, accessibilityIndex); + element.SetValue(AutomationProperties.SizeOfSetProperty, _itemsRepeaterLayout.VisibleItemsCount); + } + } + } + + internal bool TryGetElement(int index, out BreadcrumbBarItem? item) + { + item = null; + + if (_itemsRepeater is null) + return false; + + item = _itemsRepeater.TryGetElement(index) as BreadcrumbBarItem; + + return item is not null; + } + + // Event methods + + private void ItemsRepeater_ElementPrepared(ItemsRepeater sender, ItemsRepeaterElementPreparedEventArgs args) + { + if (args.Element is not BreadcrumbBarItem item || _itemsRepeater is null) + return; + + item.IsLastItem = false; + item.IsEllipsis = false; + + if (args.Index == _itemsRepeater.ItemsSourceView.Count - 1) + { + _lastBreadcrumbBarItem = item; + _lastBreadcrumbBarItem.IsLastItem = true; + } + + item.SetOwner(this); + } + + private void ItemsSourceView_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + if (_lastBreadcrumbBarItem is not null) + _lastBreadcrumbBarItem.IsLastItem = false; + + if (e.NewItems is not null && + e.NewItems.Count > 0 && + e.NewItems[e.NewItems.Count - 1] is BreadcrumbBarItem item) + { + _lastBreadcrumbBarItem = item; + item.IsLastItem = true; + } + } + + private void ItemsRepeater_ElementClearing(ItemsRepeater sender, ItemsRepeaterElementClearingEventArgs args) + { + if (args.Element is BreadcrumbBarItem item) + { + item.IsLastItem = false; + item.IsEllipsis = false; + } + } + } +} diff --git a/src/Files.App.Controls/BreadcrumbBar/BreadcrumbBar.xaml b/src/Files.App.Controls/BreadcrumbBar/BreadcrumbBar.xaml new file mode 100644 index 000000000000..07941750a874 --- /dev/null +++ b/src/Files.App.Controls/BreadcrumbBar/BreadcrumbBar.xaml @@ -0,0 +1,350 @@ + + + + 34 + 120 + 16 + + 4,0 + 8,0 + 16,0,8,0 + 2,0,0,0 + 32 + + 2,2,2,2 + 2,2,2,2 + 16,2,2,16 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App.Controls/BreadcrumbBar/BreadcrumbBarItem.Events.cs b/src/Files.App.Controls/BreadcrumbBar/BreadcrumbBarItem.Events.cs new file mode 100644 index 000000000000..b12682d13a77 --- /dev/null +++ b/src/Files.App.Controls/BreadcrumbBar/BreadcrumbBarItem.Events.cs @@ -0,0 +1,56 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Input; +using Windows.System; + +namespace Files.App.Controls +{ + public partial class BreadcrumbBarItem + { + private void ItemChevronButton_Click(object sender, RoutedEventArgs e) + { + FlyoutBase.ShowAttachedFlyout(_itemChevronButton); + } + + private void ItemChevronButton_PreviewKeyDown(object sender, KeyRoutedEventArgs e) + { + if (e.Key == VirtualKey.Down) + FlyoutBase.ShowAttachedFlyout(_itemChevronButton); + } + + private void ItemContentButton_PreviewKeyDown(object sender, KeyRoutedEventArgs e) + { + if (e.Key == VirtualKey.Down) + FlyoutBase.ShowAttachedFlyout(_itemChevronButton); + } + + private void ChevronDropDownMenuFlyout_Opening(object? sender, object e) + { + if (_ownerRef is null || + _ownerRef.TryGetTarget(out var breadcrumbBar) is false || + sender is not MenuFlyout flyout) + return; + + breadcrumbBar.RaiseItemDropDownFlyoutOpening(this, flyout); + } + + private void ChevronDropDownMenuFlyout_Opened(object? sender, object e) + { + VisualStateManager.GoToState(this, "ChevronNormalOn", true); + } + + private void ChevronDropDownMenuFlyout_Closed(object? sender, object e) + { + if (_ownerRef is null || + _ownerRef.TryGetTarget(out var breadcrumbBar) is false || + sender is not MenuFlyout flyout) + return; + + breadcrumbBar.RaiseItemDropDownFlyoutClosed(this, flyout); + + VisualStateManager.GoToState(this, "ChevronNormalOff", true); + VisualStateManager.GoToState(this, "PointerNormal", true); + } + } +} diff --git a/src/Files.App.Controls/BreadcrumbBar/BreadcrumbBarItem.Properties.cs b/src/Files.App.Controls/BreadcrumbBar/BreadcrumbBarItem.Properties.cs new file mode 100644 index 000000000000..578ae62c67ad --- /dev/null +++ b/src/Files.App.Controls/BreadcrumbBar/BreadcrumbBarItem.Properties.cs @@ -0,0 +1,32 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using CommunityToolkit.WinUI; + +namespace Files.App.Controls +{ + public partial class BreadcrumbBarItem + { + [GeneratedDependencyProperty] + public partial bool IsEllipsis { get; set; } + + [GeneratedDependencyProperty] + public partial bool IsLastItem { get; set; } + + [GeneratedDependencyProperty] + public partial string? ItemToolTip { get; set; } + + [GeneratedDependencyProperty] + public partial string? ChevronToolTip { get; set; } + + partial void OnIsEllipsisChanged(bool newValue) + { + VisualStateManager.GoToState(this, newValue ? "ChevronCollapsed" : "ChevronVisible", true); + } + + partial void OnIsLastItemChanged(bool newValue) + { + VisualStateManager.GoToState(this, newValue ? "ChevronCollapsed" : "ChevronVisible", true); + } + } +} diff --git a/src/Files.App.Controls/BreadcrumbBar/BreadcrumbBarItem.cs b/src/Files.App.Controls/BreadcrumbBar/BreadcrumbBarItem.cs new file mode 100644 index 000000000000..31760eddde44 --- /dev/null +++ b/src/Files.App.Controls/BreadcrumbBar/BreadcrumbBarItem.cs @@ -0,0 +1,107 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Input; +using Microsoft.UI.Xaml.Input; + +namespace Files.App.Controls +{ + public partial class BreadcrumbBarItem : ContentControl + { + // Constants + + private const string TemplatePartName_ItemContentButton = "PART_ItemContentButton"; + private const string TemplatePartName_ItemChevronButton = "PART_ItemChevronButton"; + private const string TemplatePartName_ItemEllipsisDropDownMenuFlyout = "PART_ItemEllipsisDropDownMenuFlyout"; + private const string TemplatePartName_ItemChevronDropDownMenuFlyout = "PART_ItemChevronDropDownMenuFlyout"; + + // Fields + + private WeakReference? _ownerRef; + + private Button _itemContentButton = null!; + private Button _itemChevronButton = null!; + private MenuFlyout _itemEllipsisDropDownMenuFlyout = null!; + private MenuFlyout _itemChevronDropDownMenuFlyout = null!; + + // Constructor + + public BreadcrumbBarItem() + { + DefaultStyleKey = typeof(BreadcrumbBarItem); + } + + // Methods + + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + _itemContentButton = GetTemplateChild(TemplatePartName_ItemContentButton) as Button + ?? throw new MissingFieldException($"Could not find {TemplatePartName_ItemContentButton} in the given {nameof(BreadcrumbBarItem)}'s style."); + _itemChevronButton = GetTemplateChild(TemplatePartName_ItemChevronButton) as Button + ?? throw new MissingFieldException($"Could not find {TemplatePartName_ItemChevronButton} in the given {nameof(BreadcrumbBarItem)}'s style."); + _itemEllipsisDropDownMenuFlyout = GetTemplateChild(TemplatePartName_ItemEllipsisDropDownMenuFlyout) as MenuFlyout + ?? throw new MissingFieldException($"Could not find {TemplatePartName_ItemEllipsisDropDownMenuFlyout} in the given {nameof(BreadcrumbBarItem)}'s style."); + _itemChevronDropDownMenuFlyout = GetTemplateChild(TemplatePartName_ItemChevronDropDownMenuFlyout) as MenuFlyout + ?? throw new MissingFieldException($"Could not find {TemplatePartName_ItemChevronDropDownMenuFlyout} in the given {nameof(BreadcrumbBarItem)}'s style."); + + if (IsEllipsis || IsLastItem) + VisualStateManager.GoToState(this, "ChevronCollapsed", true); + + // Handle click event with PointerReleasedEvent to get PointerPoint + _itemContentButton.AddHandler( // Bypass "IsHandled = true" done in the base class + PointerReleasedEvent, + new PointerEventHandler((s, e) => + { + OnItemClicked(e); + e.Handled = true; + }), + handledEventsToo: true); + + _itemContentButton.PreviewKeyDown += ItemContentButton_PreviewKeyDown; + _itemChevronButton.Click += ItemChevronButton_Click; + _itemChevronButton.PreviewKeyDown += ItemChevronButton_PreviewKeyDown; + _itemChevronDropDownMenuFlyout.Opening += ChevronDropDownMenuFlyout_Opening; + _itemChevronDropDownMenuFlyout.Opened += ChevronDropDownMenuFlyout_Opened; + _itemChevronDropDownMenuFlyout.Closed += ChevronDropDownMenuFlyout_Closed; + } + + public void OnItemClicked(PointerRoutedEventArgs? pointerRoutedEventArgs = null) + { + if (_ownerRef is null || + !_ownerRef.TryGetTarget(out var breadcrumbBar)) + return; + + if (IsEllipsis) + { + // Clear items in the ellipsis flyout + _itemEllipsisDropDownMenuFlyout.Items.Clear(); + + // Populate items in the ellipsis flyout + for (int index = 0; index < breadcrumbBar.IndexAfterEllipsis; index++) + { + if (breadcrumbBar.TryGetElement(index, out var item) && item?.Content is string text) + { + var menuFlyoutItem = new MenuFlyoutItem() { Text = text }; + _itemEllipsisDropDownMenuFlyout.Items.Add(menuFlyoutItem); + menuFlyoutItem.Click += (sender, e) => breadcrumbBar.RaiseItemClickedEvent(item, pointerRoutedEventArgs); + } + } + + // Open the ellipsis flyout + FlyoutBase.ShowAttachedFlyout(_itemContentButton); + } + else + { + // Fire a click event + breadcrumbBar.RaiseItemClickedEvent(this, pointerRoutedEventArgs); + } + } + + public void SetOwner(BreadcrumbBar breadcrumbBar) + { + _ownerRef = new(breadcrumbBar); + } + } +} diff --git a/src/Files.App.Controls/BreadcrumbBar/BreadcrumbBarItemAutomationPeer.cs b/src/Files.App.Controls/BreadcrumbBar/BreadcrumbBarItemAutomationPeer.cs new file mode 100644 index 000000000000..bddb74a7bf8f --- /dev/null +++ b/src/Files.App.Controls/BreadcrumbBar/BreadcrumbBarItemAutomationPeer.cs @@ -0,0 +1,57 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Automation.Peers; +using Microsoft.UI.Xaml.Automation.Provider; + +namespace Files.App.Controls +{ + public partial class BreadcrumbBarItemAutomationPeer : FrameworkElementAutomationPeer, IInvokeProvider + { + /// + /// Initializes a new instance of the BreadcrumbBarItemAutomationPeer class. + /// + /// + public BreadcrumbBarItemAutomationPeer(BreadcrumbBarItem owner) : base(owner) + { + } + + // IAutomationPeerOverrides + protected override string GetLocalizedControlTypeCore() + { + return "breadcrumb bar item"; + } + + protected override object GetPatternCore(PatternInterface patternInterface) + { + if (patternInterface is PatternInterface.ExpandCollapse or PatternInterface.Invoke) + return this; + + return base.GetPatternCore(patternInterface); + } + + protected override string GetClassNameCore() + { + return nameof(BreadcrumbBarItem); + } + + protected override AutomationControlType GetAutomationControlTypeCore() + { + return AutomationControlType.SplitButton; + } + + protected override bool IsControlElementCore() + { + return true; + } + + /// + public void Invoke() + { + if (Owner is not BreadcrumbBarItem item) + return; + + item.OnItemClicked(); + } + } +} diff --git a/src/Files.App.Controls/BreadcrumbBar/BreadcrumbBarLayout.cs b/src/Files.App.Controls/BreadcrumbBar/BreadcrumbBarLayout.cs new file mode 100644 index 000000000000..5e1cb05089eb --- /dev/null +++ b/src/Files.App.Controls/BreadcrumbBar/BreadcrumbBarLayout.cs @@ -0,0 +1,118 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Windows.Foundation; + +namespace Files.App.Controls +{ + /// + /// Handles layout of , collapsing items into an ellipsis button when necessary. + /// + public partial class BreadcrumbBarLayout : NonVirtualizingLayout + { + // Fields + + private readonly WeakReference? _ownerRef; + + private Size _availableSize; + private BreadcrumbBarItem? _ellipsisButton = null; + + // Properties + + public bool EllipsisIsRendered { get; private set; } + public int IndexAfterEllipsis { get; private set; } + public int VisibleItemsCount { get; private set; } + + public BreadcrumbBarLayout(BreadcrumbBar breadcrumb) + { + _ownerRef = new(breadcrumb); + } + + protected override Size MeasureOverride(NonVirtualizingLayoutContext context, Size availableSize) + { + var accumulatedSize = new Size(0, 0); + _availableSize = availableSize; + + var indexAfterEllipsis = GetFirstIndexToRender(context); + + // Go through all items and measure them + for (int index = 0; index < context.Children.Count; index++) + { + if (context.Children[index] is BreadcrumbBarItem breadcrumbItem) + { + breadcrumbItem.Measure(availableSize); + accumulatedSize.Width += index < indexAfterEllipsis ? 0 : breadcrumbItem.DesiredSize.Width; + accumulatedSize.Height = Math.Max(accumulatedSize.Height, breadcrumbItem.DesiredSize.Height); + } + } + + // Get a reference to the ellipsis item + if (context.Children.Count > 0) + _ellipsisButton ??= context.Children[0] as BreadcrumbBarItem; + + // Sets the ellipsis item's visibility based on whether the items are overflowing + EllipsisIsRendered = indexAfterEllipsis is not 0; + + return accumulatedSize; + } + + protected override Size ArrangeOverride(NonVirtualizingLayoutContext context, Size finalSize) + { + double accumulatedWidths = 0d; + + IndexAfterEllipsis = GetFirstIndexToRender(context); + VisibleItemsCount = 0; + + // Go through all items and arrange them + for (int index = 0; index < context.Children.Count; index++) + { + if (context.Children[index] is BreadcrumbBarItem breadcrumbItem) + { + if (index < IndexAfterEllipsis) + { + // Collapse + breadcrumbItem.Arrange(new Rect(0, 0, 0, 0)); + } + else + { + // Arrange normally + breadcrumbItem.Arrange(new Rect(accumulatedWidths, 0, breadcrumbItem.DesiredSize.Width, breadcrumbItem.DesiredSize.Height)); + + accumulatedWidths += breadcrumbItem.DesiredSize.Width; + + VisibleItemsCount++; + } + } + } + + if (_ownerRef?.TryGetTarget(out var breadcrumbBar) ?? false) + breadcrumbBar.OnLayoutUpdated(); + + finalSize.Width = accumulatedWidths; + + return finalSize; + } + + private int GetFirstIndexToRender(NonVirtualizingLayoutContext context) + { + var itemCount = context.Children.Count; + var accumulatedWidth = 0d; + + // Handle zero or negative available width - hide all items + if (_availableSize.Width <= 0) + return itemCount; + + // Go through all items from the last item + for (int index = itemCount - 1; index >= 0; index--) + { + var newAccumulatedWidth = accumulatedWidth + context.Children[index].DesiredSize.Width; + if (newAccumulatedWidth >= _availableSize.Width) + return index + 1; + + accumulatedWidth = newAccumulatedWidth; + } + + return 0; + } + } +} \ No newline at end of file diff --git a/src/Files.App.Controls/BreadcrumbBar/EventArgs.cs b/src/Files.App.Controls/BreadcrumbBar/EventArgs.cs new file mode 100644 index 000000000000..d40a11e65caf --- /dev/null +++ b/src/Files.App.Controls/BreadcrumbBar/EventArgs.cs @@ -0,0 +1,11 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Input; + +namespace Files.App.Controls +{ + public record class BreadcrumbBarItemClickedEventArgs(BreadcrumbBarItem Item, int Index, bool IsRootItem = false, PointerRoutedEventArgs? PointerRoutedEventArgs = null); + + public record class BreadcrumbBarItemDropDownFlyoutEventArgs(MenuFlyout Flyout, BreadcrumbBarItem? Item = null, int Index = -1, bool IsRootItem = false); +} diff --git a/src/Files.App.Controls/Files.App.Controls.csproj b/src/Files.App.Controls/Files.App.Controls.csproj index 1be6e0d8325e..fe2442fb7e9c 100644 --- a/src/Files.App.Controls/Files.App.Controls.csproj +++ b/src/Files.App.Controls/Files.App.Controls.csproj @@ -1,17 +1,30 @@ - + - - net8.0-windows10.0.22621.0 - 10.0.19041.0 - true - 10.0.19041.0 - + + $(WindowsTargetFramework) + $(MinimalWindowsVersion) + true + enable + Debug;Release + x86;x64;arm64 + win-x86;win-x64;win-arm64 + true + $(DefineConstants);OMNIBAR_DEBUG + true + - - - - - + + + + + + + + + + + + diff --git a/src/Files.App.Controls/GlobalHelper.cs b/src/Files.App.Controls/GlobalHelper.cs new file mode 100644 index 000000000000..62c27e0b7462 --- /dev/null +++ b/src/Files.App.Controls/GlobalHelper.cs @@ -0,0 +1,32 @@ +using Microsoft.UI.Input; +using System.Reflection; + +namespace Files.App.Controls +{ + public static class GlobalHelper + { + /// + /// Sets cursor when hovering on a specific element. + /// + /// An element to be changed. + /// Cursor to change. + public static void ChangeCursor(this UIElement uiElement, InputCursor cursor) + { + Type type = typeof(UIElement); + + type.InvokeMember( + "ProtectedCursor", + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty | BindingFlags.Instance, + null, + uiElement, + [cursor] + ); + } + + [Conditional("OMNIBAR_DEBUG")] + public static void WriteDebugStringForOmnibar(string? message) + { + Debug.WriteLine($"OMNIBAR DEBUG: [{message}]"); + } + } +} diff --git a/src/Files.App.Controls/GlobalUsings.cs b/src/Files.App.Controls/GlobalUsings.cs new file mode 100644 index 000000000000..d7ddfc87ad6e --- /dev/null +++ b/src/Files.App.Controls/GlobalUsings.cs @@ -0,0 +1,29 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +// System +global using global::System; +global using global::System.Collections; +global using global::System.Collections.Generic; +global using global::System.Collections.ObjectModel; +global using global::System.Linq; +global using global::System.Threading; +global using global::System.Threading.Tasks; +global using global::System.ComponentModel; +global using global::System.Diagnostics; +global using global::System.Text.Json; +global using global::System.Text.Json.Serialization; +global using SystemIO = global::System.IO; + +// Microsoft.UI +global using global::Microsoft.UI.Xaml; +global using global::Microsoft.UI.Xaml.Controls; +global using global::Microsoft.UI.Xaml.Controls.Primitives; + +// Files.App.Controls +global using global::Files.App.Controls.Primitives; + +// Files.Shared +global using global::Files.Shared; +global using global::Files.Shared.Attributes; +global using global::Files.Shared.Extensions; diff --git a/src/Files.App.Controls/GridSplitter/GridSplitter.Data.cs b/src/Files.App.Controls/GridSplitter/GridSplitter.Data.cs new file mode 100644 index 000000000000..af8123f70a33 --- /dev/null +++ b/src/Files.App.Controls/GridSplitter/GridSplitter.Data.cs @@ -0,0 +1,150 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Files.App.Controls +{ + /// + /// Enum to indicate whether GridSplitter resizes Columns or Rows + /// + public enum GridResizeDirection + { + /// + /// Determines whether to resize rows or columns based on its Alignment and + /// width compared to height + /// + Auto, + + /// + /// Resize columns when dragging Splitter. + /// + Columns, + + /// + /// Resize rows when dragging Splitter. + /// + Rows + } + + /// + /// Enum to indicate what Columns or Rows the GridSplitter resizes + /// + public enum GridResizeBehavior + { + /// + /// Determine which columns or rows to resize based on its Alignment. + /// + BasedOnAlignment, + + /// + /// Resize the current and next Columns or Rows. + /// + CurrentAndNext, + + /// + /// Resize the previous and current Columns or Rows. + /// + PreviousAndCurrent, + + /// + /// Resize the previous and next Columns or Rows. + /// + PreviousAndNext + } + + /// + /// Enum to indicate the supported gripper cursor types. + /// + public enum GripperCursorType + { + /// + /// Change the cursor based on the splitter direction + /// + Default = -1, + + /// + /// Standard Arrow cursor + /// + Arrow, + + /// + /// Standard Cross cursor + /// + Cross, + + /// + /// Standard Custom cursor + /// + Custom, + + /// + /// Standard Hand cursor + /// + Hand, + + /// + /// Standard Help cursor + /// + Help, + + /// + /// Standard IBeam cursor + /// + IBeam, + + /// + /// Standard SizeAll cursor + /// + SizeAll, + + /// + /// Standard SizeNortheastSouthwest cursor + /// + SizeNortheastSouthwest, + + /// + /// Standard SizeNorthSouth cursor + /// + SizeNorthSouth, + + /// + /// Standard SizeNorthwestSoutheast cursor + /// + SizeNorthwestSoutheast, + + /// + /// Standard SizeWestEast cursor + /// + SizeWestEast, + + /// + /// Standard UniversalNo cursor + /// + UniversalNo, + + /// + /// Standard UpArrow cursor + /// + UpArrow, + + /// + /// Standard Wait cursor + /// + Wait + } + + /// + /// Enum to indicate the behavior of window cursor on grid splitter hover + /// + public enum SplitterCursorBehavior + { + /// + /// Update window cursor on Grid Splitter hover + /// + ChangeOnSplitterHover, + + /// + /// Update window cursor on Grid Splitter Gripper hover + /// + ChangeOnGripperHover + } +} diff --git a/src/Files.App.Controls/GridSplitter/GridSplitter.Events.cs b/src/Files.App.Controls/GridSplitter/GridSplitter.Events.cs new file mode 100644 index 000000000000..40a346856834 --- /dev/null +++ b/src/Files.App.Controls/GridSplitter/GridSplitter.Events.cs @@ -0,0 +1,322 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.UI.Input; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Windows.System; +using Windows.UI.Core; + +namespace Files.App.Controls +{ + /// + /// Represents the control that redistributes space between columns or rows of a Grid control. + /// + public partial class GridSplitter + { + // Symbols for GripperBar in Segoe MDL2 Assets + private const string GripperBarVertical = "\xE784"; + private const string GripperBarHorizontal = "\xE76F"; + private const string GripperDisplayFont = "Segoe MDL2 Assets"; + + private void GridSplitter_Loaded(object sender, RoutedEventArgs e) + { + _resizeDirection = GetResizeDirection(); + _resizeBehavior = GetResizeBehavior(); + + // Adding Grip to Grid Splitter + if (Element == default(UIElement)) + { + CreateGripperDisplay(); + Element = _gripperDisplay; + } + + if (_hoverWrapper == null) + { + var hoverWrapper = new GripperHoverWrapper( + CursorBehavior == SplitterCursorBehavior.ChangeOnSplitterHover + ? this + : Element, + _resizeDirection, + GripperCursor, + GripperCustomCursorResource); + ManipulationStarted += hoverWrapper.SplitterManipulationStarted; + ManipulationCompleted += hoverWrapper.SplitterManipulationCompleted; + + _hoverWrapper = hoverWrapper; + } + } + + private void CreateGripperDisplay() + { + if (_gripperDisplay == null) + { + _gripperDisplay = new TextBlock + { + FontFamily = new FontFamily(GripperDisplayFont), + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + Foreground = GripperForeground, + Text = _resizeDirection == GridResizeDirection.Columns ? GripperBarVertical : GripperBarHorizontal + }; + _gripperDisplay.SetValue( + Microsoft.UI.Xaml.Automation.AutomationProperties.AccessibilityViewProperty, + Microsoft.UI.Xaml.Automation.Peers.AccessibilityView.Raw); + } + } + + private bool IsCtrlDown() + { + if (Window.Current == null) + { + return false; + } + + var ctrl = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Control); + return ctrl.HasFlag(CoreVirtualKeyStates.Down); + } + + /// + protected override void OnKeyDown(KeyRoutedEventArgs e) + { + var step = 1; + if (IsCtrlDown()) + { + step = 5; + } + + if (_resizeDirection == GridResizeDirection.Columns) + { + if (e.Key == VirtualKey.Left) + { + HorizontalMove(-step); + } + else if (e.Key == VirtualKey.Right) + { + HorizontalMove(step); + } + else + { + return; + } + + e.Handled = true; + return; + } + + if (_resizeDirection == GridResizeDirection.Rows) + { + if (e.Key == VirtualKey.Up) + { + VerticalMove(-step); + } + else if (e.Key == VirtualKey.Down) + { + VerticalMove(step); + } + else + { + return; + } + + e.Handled = true; + } + + base.OnKeyDown(e); + } + + /// + protected override void OnManipulationStarted(ManipulationStartedRoutedEventArgs e) + { + // saving the previous state + PreviousCursor = ProtectedCursor; + if (PreviousCursor == null) + { + PreviousCursor = InputSystemCursor.Create(InputSystemCursorShape.Arrow); + } + + _resizeDirection = GetResizeDirection(); + _resizeBehavior = GetResizeBehavior(); + + if (_resizeDirection == GridResizeDirection.Columns) + { + ProtectedCursor = ColumnsSplitterCursor; + } + else if (_resizeDirection == GridResizeDirection.Rows) + { + ProtectedCursor = RowSplitterCursor; + } + + base.OnManipulationStarted(e); + } + + /// + protected override void OnManipulationCompleted(ManipulationCompletedRoutedEventArgs e) + { + ProtectedCursor = PreviousCursor; + + base.OnManipulationCompleted(e); + } + + /// + protected override void OnManipulationDelta(ManipulationDeltaRoutedEventArgs e) + { + var horizontalChange = e.Delta.Translation.X; + var verticalChange = e.Delta.Translation.Y; + + if (this.FlowDirection == FlowDirection.RightToLeft) + { + horizontalChange *= -1; + } + + if (_resizeDirection == GridResizeDirection.Columns) + { + if (HorizontalMove(horizontalChange)) + { + return; + } + } + else if (_resizeDirection == GridResizeDirection.Rows) + { + if (VerticalMove(verticalChange)) + { + return; + } + } + + base.OnManipulationDelta(e); + } + + private bool VerticalMove(double verticalChange) + { + if (CurrentRow == null || SiblingRow == null) + { + return true; + } + + // if current row has fixed height then resize it + if (!IsStarRow(CurrentRow)) + { + // No need to check for the row Min height because it is automatically respected + if (!SetRowHeight(CurrentRow, verticalChange, GridUnitType.Pixel)) + { + return true; + } + } + + // if sibling row has fixed width then resize it + else if (!IsStarRow(SiblingRow)) + { + // Would adding to this column make the current column violate the MinWidth? + if (IsValidRowHeight(CurrentRow, verticalChange) == false) + { + return false; + } + + if (!SetRowHeight(SiblingRow, verticalChange * -1, GridUnitType.Pixel)) + { + return true; + } + } + + // if both row haven't fixed height (auto *) + else + { + // change current row height to the new height with respecting the auto + // change sibling row height to the new height relative to current row + // respect the other star row height by setting it's height to it's actual height with stars + + // We need to validate current and sibling height to not cause any unexpected behavior + if (!IsValidRowHeight(CurrentRow, verticalChange) || + !IsValidRowHeight(SiblingRow, verticalChange * -1)) + { + return true; + } + + foreach (var rowDefinition in Resizable.RowDefinitions) + { + if (rowDefinition == CurrentRow) + { + SetRowHeight(CurrentRow, verticalChange, GridUnitType.Star); + } + else if (rowDefinition == SiblingRow) + { + SetRowHeight(SiblingRow, verticalChange * -1, GridUnitType.Star); + } + else if (IsStarRow(rowDefinition)) + { + rowDefinition.Height = new GridLength(rowDefinition.ActualHeight, GridUnitType.Star); + } + } + } + + return false; + } + + private bool HorizontalMove(double horizontalChange) + { + if (CurrentColumn == null || SiblingColumn == null) + { + return true; + } + + // if current column has fixed width then resize it + if (!IsStarColumn(CurrentColumn)) + { + // No need to check for the Column Min width because it is automatically respected + if (!SetColumnWidth(CurrentColumn, horizontalChange, GridUnitType.Pixel)) + { + return true; + } + } + + // if sibling column has fixed width then resize it + else if (!IsStarColumn(SiblingColumn)) + { + // Would adding to this column make the current column violate the MinWidth? + if (IsValidColumnWidth(CurrentColumn, horizontalChange) == false) + { + return false; + } + + if (!SetColumnWidth(SiblingColumn, horizontalChange * -1, GridUnitType.Pixel)) + { + return true; + } + } + + // if both column haven't fixed width (auto *) + else + { + // change current column width to the new width with respecting the auto + // change sibling column width to the new width relative to current column + // respect the other star column width by setting it's width to it's actual width with stars + + // We need to validate current and sibling width to not cause any unexpected behavior + if (!IsValidColumnWidth(CurrentColumn, horizontalChange) || + !IsValidColumnWidth(SiblingColumn, horizontalChange * -1)) + { + return true; + } + + foreach (var columnDefinition in Resizable.ColumnDefinitions) + { + if (columnDefinition == CurrentColumn) + { + SetColumnWidth(CurrentColumn, horizontalChange, GridUnitType.Star); + } + else if (columnDefinition == SiblingColumn) + { + SetColumnWidth(SiblingColumn, horizontalChange * -1, GridUnitType.Star); + } + else if (IsStarColumn(columnDefinition)) + { + columnDefinition.Width = new GridLength(columnDefinition.ActualWidth, GridUnitType.Star); + } + } + } + + return false; + } + } +} diff --git a/src/Files.App.Controls/GridSplitter/GridSplitter.Helper.cs b/src/Files.App.Controls/GridSplitter/GridSplitter.Helper.cs new file mode 100644 index 000000000000..aa5c19418cec --- /dev/null +++ b/src/Files.App.Controls/GridSplitter/GridSplitter.Helper.cs @@ -0,0 +1,256 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Files.App.Controls +{ + /// + /// Represents the control that redistributes space between columns or rows of a Grid control. + /// + public partial class GridSplitter + { + private static bool IsStarColumn(ColumnDefinition definition) + { + return ((GridLength)definition.GetValue(ColumnDefinition.WidthProperty)).IsStar; + } + + private static bool IsStarRow(RowDefinition definition) + { + return ((GridLength)definition.GetValue(RowDefinition.HeightProperty)).IsStar; + } + + private bool SetColumnWidth(ColumnDefinition columnDefinition, double horizontalChange, GridUnitType unitType) + { + var newWidth = columnDefinition.ActualWidth + horizontalChange; + + var minWidth = columnDefinition.MinWidth; + if (!double.IsNaN(minWidth) && newWidth < minWidth) + { + newWidth = minWidth; + } + + var maxWidth = columnDefinition.MaxWidth; + if (!double.IsNaN(maxWidth) && newWidth > maxWidth) + { + newWidth = maxWidth; + } + + if (newWidth > ActualWidth) + { + columnDefinition.Width = new GridLength(newWidth, unitType); + return true; + } + + return false; + } + + private bool IsValidColumnWidth(ColumnDefinition columnDefinition, double horizontalChange) + { + var newWidth = columnDefinition.ActualWidth + horizontalChange; + + var minWidth = columnDefinition.MinWidth; + if (!double.IsNaN(minWidth) && newWidth < minWidth) + { + return false; + } + + var maxWidth = columnDefinition.MaxWidth; + if (!double.IsNaN(maxWidth) && newWidth > maxWidth) + { + return false; + } + + if (newWidth <= ActualWidth) + { + return false; + } + + return true; + } + + private bool SetRowHeight(RowDefinition rowDefinition, double verticalChange, GridUnitType unitType) + { + var newHeight = rowDefinition.ActualHeight + verticalChange; + + var minHeight = rowDefinition.MinHeight; + if (!double.IsNaN(minHeight) && newHeight < minHeight) + { + newHeight = minHeight; + } + + var maxWidth = rowDefinition.MaxHeight; + if (!double.IsNaN(maxWidth) && newHeight > maxWidth) + { + newHeight = maxWidth; + } + + if (newHeight > ActualHeight) + { + rowDefinition.Height = new GridLength(newHeight, unitType); + return true; + } + + return false; + } + + private bool IsValidRowHeight(RowDefinition rowDefinition, double verticalChange) + { + var newHeight = rowDefinition.ActualHeight + verticalChange; + + var minHeight = rowDefinition.MinHeight; + if (!double.IsNaN(minHeight) && newHeight < minHeight) + { + return false; + } + + var maxHeight = rowDefinition.MaxHeight; + if (!double.IsNaN(maxHeight) && newHeight > maxHeight) + { + return false; + } + + if (newHeight <= ActualHeight) + { + return false; + } + + return true; + } + + // Return the targeted Column based on the resize behavior + private int GetTargetedColumn() + { + var currentIndex = Grid.GetColumn(TargetControl); + return GetTargetIndex(currentIndex); + } + + // Return the sibling Row based on the resize behavior + private int GetTargetedRow() + { + var currentIndex = Grid.GetRow(TargetControl); + return GetTargetIndex(currentIndex); + } + + // Return the sibling Column based on the resize behavior + private int GetSiblingColumn() + { + var currentIndex = Grid.GetColumn(TargetControl); + return GetSiblingIndex(currentIndex); + } + + // Return the sibling Row based on the resize behavior + private int GetSiblingRow() + { + var currentIndex = Grid.GetRow(TargetControl); + return GetSiblingIndex(currentIndex); + } + + // Gets index based on resize behavior for first targeted row/column + private int GetTargetIndex(int currentIndex) + { + switch (_resizeBehavior) + { + case GridResizeBehavior.CurrentAndNext: + return currentIndex; + case GridResizeBehavior.PreviousAndNext: + return currentIndex - 1; + case GridResizeBehavior.PreviousAndCurrent: + return currentIndex - 1; + default: + return -1; + } + } + + // Gets index based on resize behavior for second targeted row/column + private int GetSiblingIndex(int currentIndex) + { + switch (_resizeBehavior) + { + case GridResizeBehavior.CurrentAndNext: + return currentIndex + 1; + case GridResizeBehavior.PreviousAndNext: + return currentIndex + 1; + case GridResizeBehavior.PreviousAndCurrent: + return currentIndex; + default: + return -1; + } + } + + // Checks the control alignment and Width/Height to detect the control resize direction columns/rows + private GridResizeDirection GetResizeDirection() + { + GridResizeDirection direction = ResizeDirection; + + if (direction == GridResizeDirection.Auto) + { + // When HorizontalAlignment is Left, Right or Center, resize Columns + if (HorizontalAlignment != HorizontalAlignment.Stretch) + { + direction = GridResizeDirection.Columns; + } + + // When VerticalAlignment is Top, Bottom or Center, resize Rows + else if (VerticalAlignment != VerticalAlignment.Stretch) + { + direction = GridResizeDirection.Rows; + } + + // Check Width vs Height + else if (ActualWidth <= ActualHeight) + { + direction = GridResizeDirection.Columns; + } + else + { + direction = GridResizeDirection.Rows; + } + } + + return direction; + } + + // Get the resize behavior (Which columns/rows should be resized) based on alignment and Direction + private GridResizeBehavior GetResizeBehavior() + { + GridResizeBehavior resizeBehavior = ResizeBehavior; + + if (resizeBehavior == GridResizeBehavior.BasedOnAlignment) + { + if (_resizeDirection == GridResizeDirection.Columns) + { + switch (HorizontalAlignment) + { + case HorizontalAlignment.Left: + resizeBehavior = GridResizeBehavior.PreviousAndCurrent; + break; + case HorizontalAlignment.Right: + resizeBehavior = GridResizeBehavior.CurrentAndNext; + break; + default: + resizeBehavior = GridResizeBehavior.PreviousAndNext; + break; + } + } + + // resize direction is vertical + else + { + switch (VerticalAlignment) + { + case VerticalAlignment.Top: + resizeBehavior = GridResizeBehavior.PreviousAndCurrent; + break; + case VerticalAlignment.Bottom: + resizeBehavior = GridResizeBehavior.CurrentAndNext; + break; + default: + resizeBehavior = GridResizeBehavior.PreviousAndNext; + break; + } + } + } + + return resizeBehavior; + } + } +} diff --git a/src/Files.App.Controls/GridSplitter/GridSplitter.Options.cs b/src/Files.App.Controls/GridSplitter/GridSplitter.Options.cs new file mode 100644 index 000000000000..3035b90c44e1 --- /dev/null +++ b/src/Files.App.Controls/GridSplitter/GridSplitter.Options.cs @@ -0,0 +1,226 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.UI.Input; +using Microsoft.UI.Xaml.Media; + +namespace Files.App.Controls +{ + /// + /// Represents the control that redistributes space between columns or rows of a Grid control. + /// + public partial class GridSplitter + { + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty ElementProperty + = DependencyProperty.Register( + nameof(Element), + typeof(UIElement), + typeof(GridSplitter), + new PropertyMetadata(default(UIElement), OnElementPropertyChanged)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty ResizeDirectionProperty + = DependencyProperty.Register( + nameof(ResizeDirection), + typeof(GridResizeDirection), + typeof(GridSplitter), + new PropertyMetadata(GridResizeDirection.Auto)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty ResizeBehaviorProperty + = DependencyProperty.Register( + nameof(ResizeBehavior), + typeof(GridResizeBehavior), + typeof(GridSplitter), + new PropertyMetadata(GridResizeBehavior.BasedOnAlignment)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty GripperForegroundProperty + = DependencyProperty.Register( + nameof(GripperForeground), + typeof(Brush), + typeof(GridSplitter), + new PropertyMetadata(default(Brush), OnGripperForegroundPropertyChanged)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty ParentLevelProperty + = DependencyProperty.Register( + nameof(ParentLevel), + typeof(int), + typeof(GridSplitter), + new PropertyMetadata(default(int))); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty GripperCursorProperty = + DependencyProperty.RegisterAttached( + nameof(GripperCursor), + typeof(GripperCursorType), + typeof(GridSplitter), + new PropertyMetadata(GripperCursorType.Default, OnGripperCursorPropertyChanged)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty GripperCustomCursorResourceProperty = + DependencyProperty.RegisterAttached( + nameof(GripperCustomCursorResource), + typeof(uint), + typeof(GridSplitter), + new PropertyMetadata(GripperCustomCursorDefaultResource, GripperCustomCursorResourcePropertyChanged)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty CursorBehaviorProperty = + DependencyProperty.RegisterAttached( + nameof(CursorBehavior), + typeof(SplitterCursorBehavior), + typeof(GridSplitter), + new PropertyMetadata(SplitterCursorBehavior.ChangeOnSplitterHover, CursorBehaviorPropertyChanged)); + + /// + /// Gets or sets the visual content of this Grid Splitter + /// + public UIElement Element + { + get { return (UIElement)GetValue(ElementProperty); } + set { SetValue(ElementProperty, value); } + } + + /// + /// Gets or sets whether the Splitter resizes the Columns, Rows, or Both. + /// + public GridResizeDirection ResizeDirection + { + get { return (GridResizeDirection)GetValue(ResizeDirectionProperty); } + + set { SetValue(ResizeDirectionProperty, value); } + } + + /// + /// Gets or sets which Columns or Rows the Splitter resizes. + /// + public GridResizeBehavior ResizeBehavior + { + get { return (GridResizeBehavior)GetValue(ResizeBehaviorProperty); } + + set { SetValue(ResizeBehaviorProperty, value); } + } + + /// + /// Gets or sets the foreground color of grid splitter grip + /// + public Brush GripperForeground + { + get { return (Brush)GetValue(GripperForegroundProperty); } + + set { SetValue(GripperForegroundProperty, value); } + } + + /// + /// Gets or sets the level of the parent grid to resize + /// + public int ParentLevel + { + get { return (int)GetValue(ParentLevelProperty); } + + set { SetValue(ParentLevelProperty, value); } + } + + /// + /// Gets or sets the gripper Cursor type + /// + public GripperCursorType GripperCursor + { + get { return (GripperCursorType)GetValue(GripperCursorProperty); } + set { SetValue(GripperCursorProperty, value); } + } + + /// + /// Gets or sets the gripper Custom Cursor resource number + /// + public int GripperCustomCursorResource + { + get { return (int)GetValue(GripperCustomCursorResourceProperty); } + set { SetValue(GripperCustomCursorResourceProperty, value); } + } + + /// + /// Gets or sets splitter cursor on hover behavior + /// + public SplitterCursorBehavior CursorBehavior + { + get { return (SplitterCursorBehavior)GetValue(CursorBehaviorProperty); } + set { SetValue(CursorBehaviorProperty, value); } + } + + private static void OnGripperForegroundPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var gridSplitter = (GridSplitter)d; + + if (gridSplitter._gripperDisplay == null) + { + return; + } + + gridSplitter._gripperDisplay.Foreground = gridSplitter.GripperForeground; + } + + private static void OnGripperCursorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var gridSplitter = (GridSplitter)d; + + if (gridSplitter._hoverWrapper == null) + { + return; + } + + gridSplitter._hoverWrapper.GripperCursor = gridSplitter.GripperCursor; + } + + private static void GripperCustomCursorResourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var gridSplitter = (GridSplitter)d; + + if (gridSplitter._hoverWrapper == null) + { + return; + } + + gridSplitter._hoverWrapper.GripperCustomCursorResource = gridSplitter.GripperCustomCursorResource; + } + + private static void CursorBehaviorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var gridSplitter = (GridSplitter)d; + + gridSplitter._hoverWrapper?.UpdateHoverElement(gridSplitter.CursorBehavior == + SplitterCursorBehavior.ChangeOnSplitterHover + ? gridSplitter + : gridSplitter.Element); + } + + private static void OnElementPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var gridSplitter = (GridSplitter)d; + + gridSplitter._hoverWrapper?.UpdateHoverElement(gridSplitter.CursorBehavior == + SplitterCursorBehavior.ChangeOnSplitterHover + ? gridSplitter + : gridSplitter.Element); + } + } +} diff --git a/src/Files.App.Controls/GridSplitter/GridSplitter.cs b/src/Files.App.Controls/GridSplitter/GridSplitter.cs new file mode 100644 index 000000000000..4f46a2486cef --- /dev/null +++ b/src/Files.App.Controls/GridSplitter/GridSplitter.cs @@ -0,0 +1,242 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.UI.Input; +using Microsoft.UI.Xaml.Automation; +using Microsoft.UI.Xaml.Input; + +namespace Files.App.Controls +{ + /// + /// Represents the control that redistributes space between columns or rows of a Grid control. + /// + public partial class GridSplitter : Control + { + internal const int GripperCustomCursorDefaultResource = -1; + internal static readonly InputCursor ColumnsSplitterCursor = InputSystemCursor.Create(InputSystemCursorShape.SizeWestEast); + internal static readonly InputCursor RowSplitterCursor = InputSystemCursor.Create(InputSystemCursorShape.SizeNorthSouth); + + internal InputCursor PreviousCursor { get; set; } + + private GridResizeDirection _resizeDirection; + private GridResizeBehavior _resizeBehavior; + private GripperHoverWrapper _hoverWrapper; + private TextBlock _gripperDisplay; + + private bool _pressed = false; + private bool _dragging = false; + private bool _pointerEntered = false; + + /// + /// Gets the target parent grid from level + /// + private FrameworkElement TargetControl + { + get + { + if (ParentLevel == 0) + { + return this; + } + + var parent = Parent; + for (int i = 2; i < ParentLevel; i++) + { + var frameworkElement = parent as FrameworkElement; + if (frameworkElement != null) + { + parent = frameworkElement.Parent; + } + } + + return parent as FrameworkElement; + } + } + + /// + /// Gets GridSplitter Container Grid + /// + private Grid Resizable => TargetControl?.Parent as Grid; + + /// + /// Gets the current Column definition of the parent Grid + /// + private ColumnDefinition CurrentColumn + { + get + { + if (Resizable == null) + { + return null; + } + + var gridSplitterTargetedColumnIndex = GetTargetedColumn(); + + if ((gridSplitterTargetedColumnIndex >= 0) + && (gridSplitterTargetedColumnIndex < Resizable.ColumnDefinitions.Count)) + { + return Resizable.ColumnDefinitions[gridSplitterTargetedColumnIndex]; + } + + return null; + } + } + + /// + /// Gets the Sibling Column definition of the parent Grid + /// + private ColumnDefinition SiblingColumn + { + get + { + if (Resizable == null) + { + return null; + } + + var gridSplitterSiblingColumnIndex = GetSiblingColumn(); + + if ((gridSplitterSiblingColumnIndex >= 0) + && (gridSplitterSiblingColumnIndex < Resizable.ColumnDefinitions.Count)) + { + return Resizable.ColumnDefinitions[gridSplitterSiblingColumnIndex]; + } + + return null; + } + } + + /// + /// Gets the current Row definition of the parent Grid + /// + private RowDefinition CurrentRow + { + get + { + if (Resizable == null) + { + return null; + } + + var gridSplitterTargetedRowIndex = GetTargetedRow(); + + if ((gridSplitterTargetedRowIndex >= 0) + && (gridSplitterTargetedRowIndex < Resizable.RowDefinitions.Count)) + { + return Resizable.RowDefinitions[gridSplitterTargetedRowIndex]; + } + + return null; + } + } + + /// + /// Gets the Sibling Row definition of the parent Grid + /// + private RowDefinition SiblingRow + { + get + { + if (Resizable == null) + { + return null; + } + + var gridSplitterSiblingRowIndex = GetSiblingRow(); + + if ((gridSplitterSiblingRowIndex >= 0) + && (gridSplitterSiblingRowIndex < Resizable.RowDefinitions.Count)) + { + return Resizable.RowDefinitions[gridSplitterSiblingRowIndex]; + } + + return null; + } + } + + /// + /// Initializes a new instance of the class. + /// + public GridSplitter() + { + DefaultStyleKey = typeof(GridSplitter); + Loaded += GridSplitter_Loaded; + AutomationProperties.SetName(this, "Grid Splitter"); + } + + /// + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + // Unhook registered events + Loaded -= GridSplitter_Loaded; + PointerEntered -= GridSplitter_PointerEntered; + PointerExited -= GridSplitter_PointerExited; + PointerPressed -= GridSplitter_PointerPressed; + PointerReleased -= GridSplitter_PointerReleased; + ManipulationStarted -= GridSplitter_ManipulationStarted; + ManipulationCompleted -= GridSplitter_ManipulationCompleted; + + _hoverWrapper?.UnhookEvents(); + + // Register Events + Loaded += GridSplitter_Loaded; + PointerEntered += GridSplitter_PointerEntered; + PointerExited += GridSplitter_PointerExited; + PointerPressed += GridSplitter_PointerPressed; + PointerReleased += GridSplitter_PointerReleased; + ManipulationStarted += GridSplitter_ManipulationStarted; + ManipulationCompleted += GridSplitter_ManipulationCompleted; + + _hoverWrapper?.UpdateHoverElement(Element); + + ManipulationMode = ManipulationModes.TranslateX | ManipulationModes.TranslateY; + } + + private void GridSplitter_PointerReleased(object sender, PointerRoutedEventArgs e) + { + _pressed = false; + VisualStateManager.GoToState(this, _pointerEntered ? "PointerOver" : "Normal", true); + } + + private void GridSplitter_PointerPressed(object sender, PointerRoutedEventArgs e) + { + _pressed = true; + VisualStateManager.GoToState(this, "Pressed", true); + } + + private void GridSplitter_PointerExited(object sender, PointerRoutedEventArgs e) + { + _pointerEntered = false; + + if (!_pressed && !_dragging) + { + VisualStateManager.GoToState(this, "Normal", true); + } + } + + private void GridSplitter_PointerEntered(object sender, PointerRoutedEventArgs e) + { + _pointerEntered = true; + + if (!_pressed && !_dragging) + { + VisualStateManager.GoToState(this, "PointerOver", true); + } + } + + private void GridSplitter_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e) + { + _dragging = false; + _pressed = false; + VisualStateManager.GoToState(this, _pointerEntered ? "PointerOver" : "Normal", true); + } + + private void GridSplitter_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e) + { + _dragging = true; + VisualStateManager.GoToState(this, "Pressed", true); + } + } +} diff --git a/src/Files.App.Controls/GridSplitter/GridSplitter.xaml b/src/Files.App.Controls/GridSplitter/GridSplitter.xaml new file mode 100644 index 000000000000..849a4285b30a --- /dev/null +++ b/src/Files.App.Controls/GridSplitter/GridSplitter.xaml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Files.App.Controls/GridSplitter/GripperHoverWrapper.cs b/src/Files.App.Controls/GridSplitter/GripperHoverWrapper.cs new file mode 100644 index 000000000000..69f02cd5a440 --- /dev/null +++ b/src/Files.App.Controls/GridSplitter/GripperHoverWrapper.cs @@ -0,0 +1,183 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.UI.Input; +using Microsoft.UI.Xaml.Input; + +namespace Files.App.Controls +{ + internal class GripperHoverWrapper + { + private readonly GridResizeDirection _gridSplitterDirection; + + private InputCursor _splitterPreviousPointer; + private InputCursor _previousCursor; + private GripperCursorType _gripperCursor; + private int _gripperCustomCursorResource; + private bool _isDragging; + private UIElement _element; + + internal GripperCursorType GripperCursor + { + get + { + return _gripperCursor; + } + + set + { + _gripperCursor = value; + } + } + + internal int GripperCustomCursorResource + { + get + { + return _gripperCustomCursorResource; + } + + set + { + _gripperCustomCursorResource = value; + } + } + + /// + /// Initializes a new instance of the class that add cursor change on hover functionality for GridSplitter. + /// + /// UI element to apply cursor change on hover + /// GridSplitter resize direction + /// GridSplitter gripper on hover cursor type + /// GridSplitter gripper custom cursor resource number + internal GripperHoverWrapper(UIElement element, GridResizeDirection gridSplitterDirection, GripperCursorType gripperCursor, int gripperCustomCursorResource) + { + _gridSplitterDirection = gridSplitterDirection; + _gripperCursor = gripperCursor; + _gripperCustomCursorResource = gripperCustomCursorResource; + _element = element; + UnhookEvents(); + _element.PointerEntered += Element_PointerEntered; + _element.PointerExited += Element_PointerExited; + } + + internal void UpdateHoverElement(UIElement element) + { + UnhookEvents(); + _element = element; + _element.PointerEntered += Element_PointerEntered; + _element.PointerExited += Element_PointerExited; + } + + private void Element_PointerExited(object sender, PointerRoutedEventArgs e) + { + if (_isDragging) + { + // if dragging don't update the cursor just update the splitter cursor with the last window cursor, + // because the splitter is still using the arrow cursor and will revert to original case when drag completes + _splitterPreviousPointer = _previousCursor; + } + else + { + if (Window.Current != null) + { + // Window.Current.CoreWindow.PointerCursor = _previousCursor; + } + } + } + + private void Element_PointerEntered(object sender, PointerRoutedEventArgs e) + { + // if not dragging + if (!_isDragging) + { + if (Window.Current != null) + { + // _previousCursor = _splitterPreviousPointer = Window.Current.CoreWindow.PointerCursor; + } + + UpdateDisplayCursor(); + } + + // if dragging + else + { + _previousCursor = _splitterPreviousPointer; + } + } + + private void UpdateDisplayCursor() + { + if (Window.Current == null) + { + return; + } + + if (_gripperCursor == GripperCursorType.Default) + { + if (_gridSplitterDirection == GridResizeDirection.Columns) + { + // Window.Current.CoreWindow.PointerCursor = GridSplitter.ColumnsSplitterCursor; + } + else if (_gridSplitterDirection == GridResizeDirection.Rows) + { + // Window.Current.CoreWindow.PointerCursor = GridSplitter.RowSplitterCursor; + } + } + else + { + var inputSystemCursorShape = (InputSystemCursorShape)((int)_gripperCursor); + if (_gripperCursor == GripperCursorType.Custom) + { + if (_gripperCustomCursorResource > GridSplitter.GripperCustomCursorDefaultResource) + { + // Window.Current.CoreWindow.PointerCursor = InputDesktopResourceCursor.Create((uint)_gripperCustomCursorResource); + } + } + else + { + // Window.Current.CoreWindow.PointerCursor = InputSystemCursor.Create(inputSystemCursorShape); + } + } + } + + internal void SplitterManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e) + { + var splitter = sender as GridSplitter; + if (splitter == null) + { + return; + } + + _splitterPreviousPointer = splitter.PreviousCursor; + _isDragging = true; + } + + internal void SplitterManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e) + { + var splitter = sender as GridSplitter; + if (splitter == null) + { + return; + } + + if (Window.Current != null) + { + // Window.Current.CoreWindow.PointerCursor = splitter.PreviousCursor = _splitterPreviousPointer; + } + + _isDragging = false; + } + + internal void UnhookEvents() + { + if (_element == null) + { + return; + } + + _element.PointerEntered -= Element_PointerEntered; + _element.PointerExited -= Element_PointerExited; + } + } +} diff --git a/src/Files.App.Controls/Omnibar/EventArgs.cs b/src/Files.App.Controls/Omnibar/EventArgs.cs new file mode 100644 index 000000000000..71622230d9a0 --- /dev/null +++ b/src/Files.App.Controls/Omnibar/EventArgs.cs @@ -0,0 +1,15 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + public record class OmnibarQuerySubmittedEventArgs(OmnibarMode Mode, object? Item, string Text); + + public record class OmnibarSuggestionChosenEventArgs(OmnibarMode Mode, object SelectedItem); + + public record class OmnibarTextChangedEventArgs(OmnibarMode Mode, OmnibarTextChangeReason Reason); + + public record class OmnibarModeChangedEventArgs(OmnibarMode? OldMode, OmnibarMode NewMode); + + public record class OmnibarIsFocusedChangedEventArgs(bool IsFocused); +} diff --git a/src/Files.App.Controls/Omnibar/IOmnibarTextMemberPathProvider.cs b/src/Files.App.Controls/Omnibar/IOmnibarTextMemberPathProvider.cs new file mode 100644 index 000000000000..d38f0d3aeb8c --- /dev/null +++ b/src/Files.App.Controls/Omnibar/IOmnibarTextMemberPathProvider.cs @@ -0,0 +1,20 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + /// + /// An interface that provides a way to get the text member path of . + /// + /// + /// An alternative to this interface is to use an powered by CsWinRT. + /// + public interface IOmnibarTextMemberPathProvider + { + /// + /// Retrieves the path of the text member as a string. This path can be used to identify the location of the text member. + /// + /// Returns a string representing the path of the text member. + string GetTextMemberPath(string textMemberPath); + } +} diff --git a/src/Files.App.Controls/Omnibar/Omnibar.Events.cs b/src/Files.App.Controls/Omnibar/Omnibar.Events.cs new file mode 100644 index 000000000000..c3065f4780de --- /dev/null +++ b/src/Files.App.Controls/Omnibar/Omnibar.Events.cs @@ -0,0 +1,178 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Input; +using Windows.System; + +namespace Files.App.Controls +{ + public partial class Omnibar + { + private void Omnibar_SizeChanged(object sender, SizeChangedEventArgs e) + { + // Popup width has to be set manually because it doesn't stretch with the parent + _textBoxSuggestionsContainerBorder.Width = ActualWidth; + } + + private void AutoSuggestBox_GettingFocus(UIElement sender, GettingFocusEventArgs args) + { + if (args.OldFocusedElement is null) + return; + + GlobalHelper.WriteDebugStringForOmnibar("The TextBox is getting the focus."); + + _previouslyFocusedElement = new(args.OldFocusedElement as UIElement); + } + + private void AutoSuggestBox_LosingFocus(UIElement sender, LosingFocusEventArgs args) + { + // Prevent the TextBox from losing focus when the ModeButton is focused + if (args.NewFocusedElement is not Button button || + args.InputDevice is FocusInputDeviceKind.Keyboard || + button.Name.ToString() != "PART_ModeButton") + return; + + args.TryCancel(); + } + + private void AutoSuggestBox_GotFocus(object sender, RoutedEventArgs e) + { + GlobalHelper.WriteDebugStringForOmnibar("The TextBox got the focus."); + + IsFocused = true; + IsFocusedChanged?.Invoke(this, new(IsFocused)); + + _textBox.SelectAll(); + } + + private void AutoSuggestBox_LostFocus(object sender, RoutedEventArgs e) + { + // TextBox still has focus if the context menu for selected text is open + var element = Microsoft.UI.Xaml.Input.FocusManager.GetFocusedElement(this.XamlRoot); + if (element is FlyoutBase or Popup) + return; + + GlobalHelper.WriteDebugStringForOmnibar("The TextBox lost the focus."); + + IsFocused = false; + IsFocusedChanged?.Invoke(this, new(IsFocused)); + + // Workaround to prevent an issue where if the window loses focus and then regains focus, + // the AutoSuggestBox will regain focus and the suggestions popup will open again. + if (element is TextBox) + { + _previouslyFocusedElement.TryGetTarget(out var previouslyFocusedElement); + previouslyFocusedElement?.Focus(FocusState.Programmatic); + } + } + + private async void AutoSuggestBox_KeyDown(object sender, KeyRoutedEventArgs e) + { + if (e.Key is VirtualKey.Enter) + { + e.Handled = true; + + GlobalHelper.WriteDebugStringForOmnibar("The TextBox accepted the Enter key."); + + SubmitQuery(_textBoxSuggestionsPopup.IsOpen && _textBoxSuggestionsListView.SelectedIndex is not -1 ? _textBoxSuggestionsListView.SelectedItem : null); + } + else if ((e.Key == VirtualKey.Up || e.Key == VirtualKey.Down) && _textBoxSuggestionsPopup.IsOpen) + { + e.Handled = true; + + GlobalHelper.WriteDebugStringForOmnibar("The TextBox accepted the Up/Down key while the suggestions pop-up is open."); + + var currentIndex = _textBoxSuggestionsListView.SelectedIndex; + var nextIndex = currentIndex; + var suggestionsCount = _textBoxSuggestionsListView.Items.Count; + + if (e.Key is VirtualKey.Up) + { + nextIndex--; + } + else if (e.Key is VirtualKey.Down) + { + nextIndex++; + } + + if (0 > nextIndex || nextIndex >= suggestionsCount) + { + RevertTextToUserInput(); + } + else + { + _textBoxSuggestionsListView.SelectedIndex = nextIndex; + + ChooseSuggestionItem(_textBoxSuggestionsListView.SelectedItem, true); + } + } + else if (e.Key == VirtualKey.Escape) + { + e.Handled = true; + + GlobalHelper.WriteDebugStringForOmnibar("The TextBox accepted the Esc key."); + + if (_textBoxSuggestionsPopup.IsOpen) + { + RevertTextToUserInput(); + _textBoxSuggestionsPopup.IsOpen = false; + } + else + { + _previouslyFocusedElement.TryGetTarget(out var previouslyFocusedElement); + previouslyFocusedElement?.Focus(FocusState.Programmatic); + } + } + else + { + _textChangeReason = OmnibarTextChangeReason.UserInput; + } + } + + private void AutoSuggestBox_TextChanged(object sender, TextChangedEventArgs e) + { + if (string.Compare(_textBox.Text, CurrentSelectedMode!.Text, StringComparison.OrdinalIgnoreCase) is not 0) + CurrentSelectedMode!.Text = _textBox.Text; + + // UpdateSuggestionListView(); + + if (_textChangeReason is OmnibarTextChangeReason.ProgrammaticChange) + _textBox.SelectAll(); + else + { + _userInput = _textBox.Text; + } + + TextChanged?.Invoke(this, new(CurrentSelectedMode, _textChangeReason)); + + // Reset + _textChangeReason = OmnibarTextChangeReason.None; + } + + private void AutoSuggestBoxSuggestionsPopup_GettingFocus(UIElement sender, GettingFocusEventArgs args) + { + // The suggestions popup is never wanted to be focused when it come to open. + args.TryCancel(); + } + + private void AutoSuggestBoxSuggestionsPopup_Opened(object? sender, object e) + { + if (_textBoxSuggestionsListView.Items.Count > 0) + _textBoxSuggestionsListView.ScrollIntoView(_textBoxSuggestionsListView.Items[0]); + } + + private void AutoSuggestBoxSuggestionsListView_ItemClick(object sender, ItemClickEventArgs e) + { + if (CurrentSelectedMode is null) + return; + + ChooseSuggestionItem(e.ClickedItem); + SubmitQuery(e.ClickedItem); + } + + private void AutoSuggestBoxSuggestionsListView_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + _textBoxSuggestionsListView.ScrollIntoView(_textBoxSuggestionsListView.SelectedItem); + } + } +} diff --git a/src/Files.App.Controls/Omnibar/Omnibar.Properties.cs b/src/Files.App.Controls/Omnibar/Omnibar.Properties.cs new file mode 100644 index 000000000000..1ee98de0c802 --- /dev/null +++ b/src/Files.App.Controls/Omnibar/Omnibar.Properties.cs @@ -0,0 +1,78 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using CommunityToolkit.WinUI; + +namespace Files.App.Controls +{ + public partial class Omnibar + { + [GeneratedDependencyProperty] + public partial IList? Modes { get; set; } + + [GeneratedDependencyProperty] + public partial OmnibarMode? CurrentSelectedMode { get; set; } + + [GeneratedDependencyProperty] + public partial string? CurrentSelectedModeName { get; set; } + + [GeneratedDependencyProperty] + public partial Thickness AutoSuggestBoxPadding { get; set; } + + [GeneratedDependencyProperty] + public partial bool IsFocused { get; set; } + + partial void OnCurrentSelectedModePropertyChanged(DependencyPropertyChangedEventArgs e) + { + if (e.NewValue is not OmnibarMode newMode) + return; + + if (e.OldValue is OmnibarMode oldMode) + GlobalHelper.WriteDebugStringForOmnibar($"The mode change from {oldMode} to {newMode} has been requested."); + else + GlobalHelper.WriteDebugStringForOmnibar($"The mode change to {newMode} has been requested."); + + ChangeMode(e.OldValue as OmnibarMode, newMode); + CurrentSelectedModeName = newMode.Name; + } + + partial void OnCurrentSelectedModeNameChanged(string? newValue) + { + if (string.IsNullOrEmpty(newValue) || + string.IsNullOrEmpty(CurrentSelectedMode?.Name) || + CurrentSelectedMode.Name.Equals(newValue) || + Modes is null) + return; + + var newMode = Modes.Where(x => x.Name?.Equals(newValue) ?? false).FirstOrDefault(); + if (newMode is null) + return; + + CurrentSelectedMode = newMode; + } + + partial void OnIsFocusedChanged(bool newValue) + { + if (CurrentSelectedMode is null || _textBox is null) + return; + + GlobalHelper.WriteDebugStringForOmnibar($"{nameof(IsFocused)} has been changed to {IsFocused}"); + + if (newValue) + { + VisualStateManager.GoToState(CurrentSelectedMode, "Focused", true); + VisualStateManager.GoToState(_textBox, "InputAreaVisible", true); + } + else + { + if (CurrentSelectedMode?.ContentOnInactive is not null) + { + VisualStateManager.GoToState(CurrentSelectedMode, "CurrentUnfocused", true); + VisualStateManager.GoToState(_textBox, "InputAreaCollapsed", true); + } + } + + TryToggleIsSuggestionsPopupOpen(newValue); + } + } +} diff --git a/src/Files.App.Controls/Omnibar/Omnibar.cs b/src/Files.App.Controls/Omnibar/Omnibar.cs new file mode 100644 index 000000000000..131ca89f4ef5 --- /dev/null +++ b/src/Files.App.Controls/Omnibar/Omnibar.cs @@ -0,0 +1,293 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Markup; +using Microsoft.UI.Xaml.Media.Animation; +using Windows.Foundation; + +namespace Files.App.Controls +{ + // Content + [ContentProperty(Name = nameof(Modes))] + public partial class Omnibar : Control + { + // Constants + + private const string TemplatePartName_AutoSuggestBox = "PART_TextBox"; + private const string TemplatePartName_ModesHostGrid = "PART_ModesHostGrid"; + private const string TemplatePartName_AutoSuggestBoxSuggestionsPopup = "PART_SuggestionsPopup"; + private const string TemplatePartName_AutoSuggestBoxSuggestionsContainerBorder = "PART_SuggestionsContainerBorder"; + private const string TemplatePartName_SuggestionsListView = "PART_SuggestionsListView"; + + // Fields + + private TextBox _textBox = null!; + private Grid _modesHostGrid = null!; + private Popup _textBoxSuggestionsPopup = null!; + private Border _textBoxSuggestionsContainerBorder = null!; + private ListView _textBoxSuggestionsListView = null!; + + private string _userInput = string.Empty; + private OmnibarTextChangeReason _textChangeReason = OmnibarTextChangeReason.None; + + private WeakReference _previouslyFocusedElement = new(null); + + // Events + + public event TypedEventHandler? QuerySubmitted; + public event TypedEventHandler? SuggestionChosen; + public event TypedEventHandler? TextChanged; + public event TypedEventHandler? ModeChanged; + public event TypedEventHandler IsFocusedChanged; + + // Constructor + + public Omnibar() + { + DefaultStyleKey = typeof(Omnibar); + + Modes = []; + AutoSuggestBoxPadding = new(0, 0, 0, 0); + + GlobalHelper.WriteDebugStringForOmnibar("Omnibar has been initialized."); + } + + // Methods + + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + _textBox = GetTemplateChild(TemplatePartName_AutoSuggestBox) as TextBox + ?? throw new MissingFieldException($"Could not find {TemplatePartName_AutoSuggestBox} in the given {nameof(Omnibar)}'s style."); + _modesHostGrid = GetTemplateChild(TemplatePartName_ModesHostGrid) as Grid + ?? throw new MissingFieldException($"Could not find {TemplatePartName_ModesHostGrid} in the given {nameof(Omnibar)}'s style."); + _textBoxSuggestionsPopup = GetTemplateChild(TemplatePartName_AutoSuggestBoxSuggestionsPopup) as Popup + ?? throw new MissingFieldException($"Could not find {TemplatePartName_AutoSuggestBoxSuggestionsPopup} in the given {nameof(Omnibar)}'s style."); + _textBoxSuggestionsContainerBorder = GetTemplateChild(TemplatePartName_AutoSuggestBoxSuggestionsContainerBorder) as Border + ?? throw new MissingFieldException($"Could not find {TemplatePartName_AutoSuggestBoxSuggestionsContainerBorder} in the given {nameof(Omnibar)}'s style."); + _textBoxSuggestionsListView = GetTemplateChild(TemplatePartName_SuggestionsListView) as ListView + ?? throw new MissingFieldException($"Could not find {TemplatePartName_SuggestionsListView} in the given {nameof(Omnibar)}'s style."); + + PopulateModes(); + + SizeChanged += Omnibar_SizeChanged; + _textBox.GettingFocus += AutoSuggestBox_GettingFocus; + _textBox.GotFocus += AutoSuggestBox_GotFocus; + _textBox.LosingFocus += AutoSuggestBox_LosingFocus; + _textBox.LostFocus += AutoSuggestBox_LostFocus; + _textBox.KeyDown += AutoSuggestBox_KeyDown; + _textBox.TextChanged += AutoSuggestBox_TextChanged; + _textBoxSuggestionsPopup.GettingFocus += AutoSuggestBoxSuggestionsPopup_GettingFocus; + _textBoxSuggestionsPopup.Opened += AutoSuggestBoxSuggestionsPopup_Opened; + _textBoxSuggestionsListView.ItemClick += AutoSuggestBoxSuggestionsListView_ItemClick; + _textBoxSuggestionsListView.SelectionChanged += AutoSuggestBoxSuggestionsListView_SelectionChanged; + + // Set the default width + _textBoxSuggestionsContainerBorder.Width = ActualWidth; + + GlobalHelper.WriteDebugStringForOmnibar("The template and the events have been initialized."); + } + + public void PopulateModes() + { + if (Modes is null || _modesHostGrid is null) + return; + + // Populate the modes + foreach (var mode in Modes) + { + // Insert a divider + if (_modesHostGrid.Children.Count is not 0) + { + var divider = new OmnibarModeSeparator(); + + _modesHostGrid.ColumnDefinitions.Add(new() { Width = GridLength.Auto }); + Grid.SetColumn(divider, _modesHostGrid.Children.Count); + _modesHostGrid.Children.Add(divider); + } + + // Insert the mode + _modesHostGrid.ColumnDefinitions.Add(new() { Width = GridLength.Auto }); + Grid.SetColumn(mode, _modesHostGrid.Children.Count); + _modesHostGrid.Children.Add(mode); + mode.SetOwner(this); + } + } + + protected void ChangeMode(OmnibarMode? oldMode, OmnibarMode newMode) + { + if (_modesHostGrid is null || Modes is null || CurrentSelectedMode is null) + return; + + foreach (var mode in Modes) + { + // Add the reposition transition to the all modes + mode.Transitions = [new RepositionThemeTransition()]; + mode.UpdateLayout(); + mode.IsTabStop = false; + } + + var index = _modesHostGrid.Children.IndexOf(newMode); + + if (oldMode is not null) + VisualStateManager.GoToState(oldMode, "Unfocused", true); + + DispatcherQueue.TryEnqueue(() => + { + // Reset + foreach (var column in _modesHostGrid.ColumnDefinitions) + column.Width = GridLength.Auto; + + // Expand the given mode + _modesHostGrid.ColumnDefinitions[index].Width = new(1, GridUnitType.Star); + }); + + var itemCount = Modes.Count; + var itemIndex = Modes.IndexOf(newMode); + var modeButtonWidth = newMode.ActualWidth; + var modeSeparatorWidth = itemCount is not 0 or 1 ? _modesHostGrid.Children[1] is FrameworkElement frameworkElement ? frameworkElement.ActualWidth : 0 : 0; + + var leftPadding = (itemIndex + 1) * modeButtonWidth + modeSeparatorWidth * itemIndex; + var rightPadding = (itemCount - itemIndex - 1) * modeButtonWidth + modeSeparatorWidth * (itemCount - itemIndex - 1) + 8; + + // Set the correct AutoSuggestBox cursor position + AutoSuggestBoxPadding = new(leftPadding, 0, rightPadding, 0); + + _textChangeReason = OmnibarTextChangeReason.ProgrammaticChange; + ChangeTextBoxText(newMode.Text ?? string.Empty); + + VisualStateManager.GoToState(newMode, "Focused", true); + newMode.IsTabStop = false; + + ModeChanged?.Invoke(this, new(oldMode, newMode!)); + + _textBox.PlaceholderText = newMode.PlaceholderText ?? string.Empty; + _textBoxSuggestionsListView.ItemTemplate = newMode.ItemTemplate; + _textBoxSuggestionsListView.ItemsSource = newMode.ItemsSource; + + if (newMode.IsAutoFocusEnabled) + { + _textBox.Focus(FocusState.Pointer); + } + else + { + if (IsFocused) + { + VisualStateManager.GoToState(newMode, "Focused", true); + VisualStateManager.GoToState(_textBox, "InputAreaVisible", true); + } + else if (newMode?.ContentOnInactive is not null) + { + VisualStateManager.GoToState(newMode, "CurrentUnfocused", true); + VisualStateManager.GoToState(_textBox, "InputAreaCollapsed", true); + } + else + { + VisualStateManager.GoToState(_textBox, "InputAreaVisible", true); + } + } + + TryToggleIsSuggestionsPopupOpen(true); + + // Remove the reposition transition from the all modes + foreach (var mode in Modes) + { + mode.Transitions.Clear(); + mode.UpdateLayout(); + } + + GlobalHelper.WriteDebugStringForOmnibar($"Successfully changed Mode from {oldMode} to {newMode}"); + } + + internal protected void FocusTextBox() + { + _textBox.Focus(FocusState.Keyboard); + } + + internal protected bool TryToggleIsSuggestionsPopupOpen(bool wantToOpen) + { + if (_textBoxSuggestionsPopup is null) + return false; + + if (wantToOpen && (!IsFocused || CurrentSelectedMode?.ItemsSource is null || (CurrentSelectedMode?.ItemsSource is IList collection && collection.Count is 0))) + { + _textBoxSuggestionsPopup.IsOpen = false; + + GlobalHelper.WriteDebugStringForOmnibar("The suggestions pop-up closed."); + + return false; + } + + if (CurrentSelectedMode is not null) + { + _textBoxSuggestionsListView.ItemTemplate = CurrentSelectedMode.ItemTemplate; + _textBoxSuggestionsListView.ItemsSource = CurrentSelectedMode.ItemsSource; + } + + _textBoxSuggestionsPopup.IsOpen = wantToOpen; + + GlobalHelper.WriteDebugStringForOmnibar("The suggestions pop-up is open."); + + return false; + } + + public void ChooseSuggestionItem(object obj, bool isOriginatedFromArrowKey = false) + { + if (CurrentSelectedMode is null) + return; + + if (CurrentSelectedMode.UpdateTextOnSelect || + (isOriginatedFromArrowKey && CurrentSelectedMode.UpdateTextOnArrowKeys)) + { + _textChangeReason = OmnibarTextChangeReason.SuggestionChosen; + ChangeTextBoxText(GetObjectText(obj)); + } + + SuggestionChosen?.Invoke(this, new(CurrentSelectedMode, obj)); + } + + internal protected void ChangeTextBoxText(string text) + { + _textBox.Text = text; + + // Move the cursor to the end of the TextBox + if (_textChangeReason == OmnibarTextChangeReason.SuggestionChosen) + _textBox?.Select(_textBox.Text.Length, 0); + } + + private void SubmitQuery(object? item) + { + if (CurrentSelectedMode is null) + return; + + QuerySubmitted?.Invoke(this, new OmnibarQuerySubmittedEventArgs(CurrentSelectedMode, item, _textBox.Text)); + + _textBoxSuggestionsPopup.IsOpen = false; + } + + private string GetObjectText(object obj) + { + if (CurrentSelectedMode is null) + return string.Empty; + + // Get the text to put into the text box from the chosen suggestion item + return obj is string text + ? text + : obj is IOmnibarTextMemberPathProvider textMemberPathProvider + ? textMemberPathProvider.GetTextMemberPath(CurrentSelectedMode.TextMemberPath ?? string.Empty) + : obj.ToString() ?? string.Empty; + } + + private void RevertTextToUserInput() + { + if (CurrentSelectedMode is null) + return; + + _textBoxSuggestionsListView.SelectedIndex = -1; + _textChangeReason = OmnibarTextChangeReason.ProgrammaticChange; + + ChangeTextBoxText(_userInput ?? ""); + } + } +} diff --git a/src/Files.App.Controls/Omnibar/Omnibar.xaml b/src/Files.App.Controls/Omnibar/Omnibar.xaml new file mode 100644 index 000000000000..c7981f6fc7ea --- /dev/null +++ b/src/Files.App.Controls/Omnibar/Omnibar.xaml @@ -0,0 +1,451 @@ + + + + 38 + 34 + 46 + 1 + 20 + + 1 + 2 + -1 + 4,0,4,0 + + 19 + 17 + + + + + + + + + + diff --git a/src/Files.App.Controls/Omnibar/OmnibarMode.Events.cs b/src/Files.App.Controls/Omnibar/OmnibarMode.Events.cs new file mode 100644 index 000000000000..b8633bd24c50 --- /dev/null +++ b/src/Files.App.Controls/Omnibar/OmnibarMode.Events.cs @@ -0,0 +1,55 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Input; + +namespace Files.App.Controls +{ + public partial class OmnibarMode + { + private void ModeButton_PointerEntered(object sender, PointerRoutedEventArgs e) + { + if (_ownerRef is null || _ownerRef.TryGetTarget(out var owner) is false || owner.CurrentSelectedMode == this) + return; + + GlobalHelper.WriteDebugStringForOmnibar($"The mouse pointer has entered the UI area of this Mode ({this})"); + + VisualStateManager.GoToState(this, "PointerOver", true); + } + + private void ModeButton_PointerPressed(object sender, PointerRoutedEventArgs e) + { + if (_ownerRef is null || _ownerRef.TryGetTarget(out var owner) is false || owner.CurrentSelectedMode == this) + return; + + GlobalHelper.WriteDebugStringForOmnibar($"The mouse pointer has been pressed on the UI area of this Mode ({this})"); + + VisualStateManager.GoToState(this, "PointerPressed", true); + } + + private void ModeButton_PointerReleased(object sender, PointerRoutedEventArgs e) + { + if (_ownerRef is null || _ownerRef.TryGetTarget(out var owner) is false || owner.CurrentSelectedMode == this) + return; + + GlobalHelper.WriteDebugStringForOmnibar($"The mouse pointer has been unpressed from the UI area of this Mode ({this})"); + + VisualStateManager.GoToState(this, "PointerOver", true); + } + + private void ModeButton_PointerExited(object sender, PointerRoutedEventArgs e) + { + GlobalHelper.WriteDebugStringForOmnibar($"The mouse pointer has moved away from the UI area of this Mode ({this})"); + + VisualStateManager.GoToState(this, "PointerNormal", true); + } + + private void ModeButton_Click(object sender, RoutedEventArgs e) + { + if (_ownerRef is null || _ownerRef.TryGetTarget(out var owner) is false || owner.CurrentSelectedMode == this) + return; + + owner.CurrentSelectedMode = this; + } + } +} \ No newline at end of file diff --git a/src/Files.App.Controls/Omnibar/OmnibarMode.Properties.cs b/src/Files.App.Controls/Omnibar/OmnibarMode.Properties.cs new file mode 100644 index 000000000000..3130d95d305c --- /dev/null +++ b/src/Files.App.Controls/Omnibar/OmnibarMode.Properties.cs @@ -0,0 +1,54 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using CommunityToolkit.WinUI; + +namespace Files.App.Controls +{ + public partial class OmnibarMode + { + [GeneratedDependencyProperty] + public partial string? Text { get; set; } + + [GeneratedDependencyProperty] + public partial bool IsDefault { get; set; } + + [GeneratedDependencyProperty] + public partial string? PlaceholderText { get; set; } + + [GeneratedDependencyProperty] + public partial string? ModeName { get; set; } + + [GeneratedDependencyProperty] + public partial FrameworkElement? ContentOnInactive { get; set; } + + [GeneratedDependencyProperty] + public partial FrameworkElement? IconOnActive { get; set; } + + [GeneratedDependencyProperty] + public partial FrameworkElement? IconOnInactive { get; set; } + + [GeneratedDependencyProperty] + /// + /// Implement in to get the text member path from the suggestion item correctly. + /// + public partial string? TextMemberPath { get; set; } + + [GeneratedDependencyProperty(DefaultValue = true)] + public partial bool UpdateTextOnSelect { get; set; } + + [GeneratedDependencyProperty(DefaultValue = true)] + public partial bool UpdateTextOnArrowKeys { get; set; } + + [GeneratedDependencyProperty] + public partial bool IsAutoFocusEnabled { get; set; } + + partial void OnTextChanged(string? newValue) + { + if (_ownerRef is null || _ownerRef.TryGetTarget(out var owner) is false) + return; + + owner.ChangeTextBoxText(newValue ?? string.Empty); + } + } +} diff --git a/src/Files.App.Controls/Omnibar/OmnibarMode.cs b/src/Files.App.Controls/Omnibar/OmnibarMode.cs new file mode 100644 index 000000000000..9f413b7a5c75 --- /dev/null +++ b/src/Files.App.Controls/Omnibar/OmnibarMode.cs @@ -0,0 +1,110 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Input; + +namespace Files.App.Controls +{ + [DebuggerDisplay("{" + nameof(ToString) + "(),nq}")] + public partial class OmnibarMode : ItemsControl + { + // Constants + + private const string TemplatePartName_ModeButton = "PART_ModeButton"; + + // Fields + + private WeakReference? _ownerRef; + + private Button _modeButton = null!; + + // Constructor + + public OmnibarMode() + { + DefaultStyleKey = typeof(OmnibarMode); + + GlobalHelper.WriteDebugStringForOmnibar($"Omnibar Mode ({this}) has been initialized."); + } + + // Methods + + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + _modeButton = GetTemplateChild(TemplatePartName_ModeButton) as Button + ?? throw new MissingFieldException($"Could not find {TemplatePartName_ModeButton} in the given {nameof(OmnibarMode)}'s style."); + + RegisterPropertyChangedCallback(ItemsSourceProperty, (d, dp) => + { + if (_ownerRef is not null && _ownerRef.TryGetTarget(out var owner)) + owner.TryToggleIsSuggestionsPopupOpen(true); + }); + + Loaded += OmnibarMode_Loaded; + _modeButton.PointerEntered += ModeButton_PointerEntered; + _modeButton.PointerPressed += ModeButton_PointerPressed; + _modeButton.PointerReleased += ModeButton_PointerReleased; + _modeButton.PointerExited += ModeButton_PointerExited; + _modeButton.Click += ModeButton_Click; + + GlobalHelper.WriteDebugStringForOmnibar($"The template and the events of the Omnibar Mode ({this}) have been initialized."); + } + + protected override void OnKeyUp(KeyRoutedEventArgs args) + { + if (args.Handled || IsEnabled is false) + goto cleanup; + + if (args.Key is Windows.System.VirtualKey.Enter) + { + if (_ownerRef is null || _ownerRef.TryGetTarget(out var owner) is false || owner.CurrentSelectedMode == this) + return; + + VisualStateManager.GoToState(this, "PointerPressed", true); + + // Change the current mode + owner.CurrentSelectedMode = this; + owner.FocusTextBox(); + + VisualStateManager.GoToState(this, "PointerNormal", true); + } + + cleanup: + { + base.OnKeyDown(args); + } + } + + protected override void OnItemsChanged(object e) + { + base.OnItemsChanged(e); + + if (_ownerRef is not null && _ownerRef.TryGetTarget(out var owner)) + owner.TryToggleIsSuggestionsPopupOpen(true); + } + + private void OmnibarMode_Loaded(object sender, RoutedEventArgs e) + { + // Set this mode as the current mode if it is the default mode + if (IsDefault && _ownerRef is not null && _ownerRef.TryGetTarget(out var owner)) + { + DispatcherQueue.TryEnqueue(() => + { + owner.CurrentSelectedMode = this; + }); + } + } + + public void SetOwner(Omnibar owner) + { + _ownerRef = new(owner); + } + + public override string ToString() + { + return Name ?? string.Empty; + } + } +} diff --git a/src/Files.App.Controls/Omnibar/OmnibarModeSeparator.cs b/src/Files.App.Controls/Omnibar/OmnibarModeSeparator.cs new file mode 100644 index 000000000000..2eaf6fbed8ae --- /dev/null +++ b/src/Files.App.Controls/Omnibar/OmnibarModeSeparator.cs @@ -0,0 +1,22 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + public partial class OmnibarModeSeparator : Control + { + // Constructor + + public OmnibarModeSeparator() + { + DefaultStyleKey = typeof(OmnibarMode); + } + + // Methods + + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + } + } +} diff --git a/src/Files.App.Controls/Omnibar/OmnibarTextChangeReason.cs b/src/Files.App.Controls/Omnibar/OmnibarTextChangeReason.cs new file mode 100644 index 000000000000..8a4c41e2b3be --- /dev/null +++ b/src/Files.App.Controls/Omnibar/OmnibarTextChangeReason.cs @@ -0,0 +1,16 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + public enum OmnibarTextChangeReason + { + UserInput, + + SuggestionChosen, + + ProgrammaticChange, + + None, + } +} diff --git a/src/Files.App.Controls/SamplePanel/SamplePanel.Properties.cs b/src/Files.App.Controls/SamplePanel/SamplePanel.Properties.cs new file mode 100644 index 000000000000..c187fbf1111b --- /dev/null +++ b/src/Files.App.Controls/SamplePanel/SamplePanel.Properties.cs @@ -0,0 +1,24 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using CommunityToolkit.WinUI; + +namespace Files.App.Controls +{ + public sealed partial class SamplePanel + { + [GeneratedDependencyProperty] + public partial string? Header { get; set; } + + [GeneratedDependencyProperty] + public partial UIElement? MainContent { get; set; } + + [GeneratedDependencyProperty] + public partial UIElement? SideContent { get; set; } + + partial void OnSideContentChanged(UIElement? newValue) + { + UpdateVisualStates(); + } + } +} diff --git a/src/Files.App.Controls/SamplePanel/SamplePanel.cs b/src/Files.App.Controls/SamplePanel/SamplePanel.cs new file mode 100644 index 000000000000..8d2dbc1ff2e0 --- /dev/null +++ b/src/Files.App.Controls/SamplePanel/SamplePanel.cs @@ -0,0 +1,28 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + // Visual states + [TemplateVisualState(GroupName = "SideContentVisibilityStates", Name = "NothingToShowTextCollapsed")] + [TemplateVisualState(GroupName = "SideContentVisibilityStates", Name = "NothingToShowTextVisible")] + public sealed partial class SamplePanel : Control + { + public SamplePanel() + { + DefaultStyleKey = typeof(SamplePanel); + } + + protected override void OnApplyTemplate() + { + UpdateVisualStates(); + + base.OnApplyTemplate(); + } + + private void UpdateVisualStates() + { + VisualStateManager.GoToState(this, SideContent is null ? "NothingToShowTextVisible" : "NothingToShowTextCollapsed", true); + } + } +} diff --git a/src/Files.App.Controls/SamplePanel/SamplePanel.xaml b/src/Files.App.Controls/SamplePanel/SamplePanel.xaml new file mode 100644 index 000000000000..32b39686bda2 --- /dev/null +++ b/src/Files.App.Controls/SamplePanel/SamplePanel.xaml @@ -0,0 +1,108 @@ + + + + 360 + 24 + + + + diff --git a/src/Files.App.Controls/Sidebar/ISidebarItemModel.cs b/src/Files.App.Controls/Sidebar/ISidebarItemModel.cs new file mode 100644 index 000000000000..ab0e4742ae6c --- /dev/null +++ b/src/Files.App.Controls/Sidebar/ISidebarItemModel.cs @@ -0,0 +1,24 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + public interface ISidebarItemModel : INotifyPropertyChanged + { + /// + /// The children of this item that will be rendered as child elements of the SidebarItem + /// + object? Children { get; } + + /// + /// Determines whether the SidebarItem is expanded and the children are visible + /// or if it is collapsed and children are not visible. + /// + bool IsExpanded { get; set; } + + /// + /// Indicates whether the children should have an indentation or not. + /// + bool PaddedItem { get; } + } +} diff --git a/src/Files.App.Controls/Sidebar/ISidebarViewModel.cs b/src/Files.App.Controls/Sidebar/ISidebarViewModel.cs new file mode 100644 index 000000000000..41d8c037bceb --- /dev/null +++ b/src/Files.App.Controls/Sidebar/ISidebarViewModel.cs @@ -0,0 +1,14 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Input; +using Windows.ApplicationModel.DataTransfer; +using Windows.Foundation; + +namespace Files.App.Controls +{ + public record ItemInvokedEventArgs(PointerUpdateKind PointerUpdateKind) { } + public record ItemDroppedEventArgs(object DropTarget, DataPackageView DroppedItem, SidebarItemDropPosition dropPosition, DragEventArgs RawEvent) { } + public record ItemDragOverEventArgs(object DropTarget, DataPackageView DroppedItem, SidebarItemDropPosition dropPosition, DragEventArgs RawEvent) { } + public record ItemContextInvokedArgs(object? Item, Point Position) { } +} diff --git a/src/Files.App/UserControls/Sidebar/SidebarDisplayMode.cs b/src/Files.App.Controls/Sidebar/SidebarDisplayMode.cs similarity index 79% rename from src/Files.App/UserControls/Sidebar/SidebarDisplayMode.cs rename to src/Files.App.Controls/Sidebar/SidebarDisplayMode.cs index 520f93c72f0e..6b0bd385b5bb 100644 --- a/src/Files.App/UserControls/Sidebar/SidebarDisplayMode.cs +++ b/src/Files.App.Controls/Sidebar/SidebarDisplayMode.cs @@ -1,7 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -namespace Files.App.UserControls.Sidebar +namespace Files.App.Controls { /// /// The display mode of the diff --git a/src/Files.App/UserControls/Sidebar/SidebarItem.Properties.cs b/src/Files.App.Controls/Sidebar/SidebarItem.Properties.cs similarity index 93% rename from src/Files.App/UserControls/Sidebar/SidebarItem.Properties.cs rename to src/Files.App.Controls/Sidebar/SidebarItem.Properties.cs index 2edd84b80b71..6c1d5d8922b4 100644 --- a/src/Files.App/UserControls/Sidebar/SidebarItem.Properties.cs +++ b/src/Files.App.Controls/Sidebar/SidebarItem.Properties.cs @@ -1,10 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; +using CommunityToolkit.WinUI; -namespace Files.App.UserControls.Sidebar +namespace Files.App.Controls { public sealed partial class SidebarItem : Control { @@ -47,7 +46,7 @@ public double ChildrenPresenterHeight } // Using 30 as a default in case something goes wrong public static readonly DependencyProperty ChildrenPresenterHeightProperty = - DependencyProperty.Register(nameof(ChildrenPresenterHeight), typeof(double), typeof(SidebarItem), new PropertyMetadata(30d)); + DependencyProperty.Register(nameof(ChildrenPresenterHeight), typeof(double), typeof(SidebarItem), new PropertyMetadata(30d)); public ISidebarItemModel? Item { @@ -89,6 +88,12 @@ public SidebarDisplayMode DisplayMode public static readonly DependencyProperty DisplayModeProperty = DependencyProperty.Register(nameof(DisplayMode), typeof(SidebarDisplayMode), typeof(SidebarItem), new PropertyMetadata(SidebarDisplayMode.Expanded, OnPropertyChanged)); + [GeneratedDependencyProperty] + public partial string? Text { get; set; } + + [GeneratedDependencyProperty] + public partial object? ToolTip { get; set; } + public static void SetTemplateRoot(DependencyObject target, FrameworkElement value) { target.SetValue(TemplateRootProperty, value); @@ -115,7 +120,7 @@ public static void OnPropertyChanged(DependencyObject sender, DependencyProperty { item.UpdateExpansionState(); } - else if(e.Property == ItemProperty) + else if (e.Property == ItemProperty) { item.HandleItemChange(); } diff --git a/src/Files.App/UserControls/Sidebar/SidebarItem.cs b/src/Files.App.Controls/Sidebar/SidebarItem.cs similarity index 78% rename from src/Files.App/UserControls/Sidebar/SidebarItem.cs rename to src/Files.App.Controls/Sidebar/SidebarItem.cs index bd58365a477e..ddd0a3df3dc5 100644 --- a/src/Files.App/UserControls/Sidebar/SidebarItem.cs +++ b/src/Files.App.Controls/Sidebar/SidebarItem.cs @@ -1,17 +1,16 @@ // Copyright (c) Microsoft Corporation and Contributors. // Licensed under the MIT License. -using CommunityToolkit.WinUI.UI; +using CommunityToolkit.WinUI; using Microsoft.UI.Input; -using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Automation; using Microsoft.UI.Xaml.Automation.Peers; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; using System.Collections.Specialized; +using System.IO; using Windows.ApplicationModel.DataTransfer; +using Windows.Storage; -namespace Files.App.UserControls.Sidebar +namespace Files.App.Controls { public sealed partial class SidebarItem : Control { @@ -21,9 +20,6 @@ public sealed partial class SidebarItem : Control public bool IsGroupHeader => Item?.Children is not null; public bool CollapseEnabled => DisplayMode != SidebarDisplayMode.Compact; - // TODO: Do not use localized text for comparison. This is a workaround to avoid major refactoring for now, it should be done any time soon - public bool IsHomeItem => Item?.Text == "Home".GetLocalizedResource() && Owner?.MenuItemsSource is IList enumerable && enumerable.IndexOf(Item) == 0; - private bool hasChildSelection => selectedChildItem != null; private bool isPointerOver = false; private bool isClicking = false; @@ -64,18 +60,18 @@ private void SidebarItem_Loaded(object sender, RoutedEventArgs e) { HookupOwners(); - if (GetTemplateChild("ElementGrid") is Grid grid) - { - grid.PointerEntered += ItemGrid_PointerEntered; - grid.PointerExited += ItemGrid_PointerExited; - grid.PointerCanceled += ItemGrid_PointerCanceled; - grid.PointerPressed += ItemGrid_PointerPressed; - grid.ContextRequested += ItemGrid_ContextRequested; - grid.DragLeave += ItemGrid_DragLeave; - grid.DragOver += ItemGrid_DragOver; - grid.Drop += ItemGrid_Drop; - grid.AllowDrop = true; - grid.IsTabStop = true; + if (GetTemplateChild("ElementBorder") is Border border) + { + border.PointerEntered += ItemBorder_PointerEntered; + border.PointerExited += ItemBorder_PointerExited; + border.PointerCanceled += ItemBorder_PointerCanceled; + border.PointerPressed += ItemBorder_PointerPressed; + border.ContextRequested += ItemBorder_ContextRequested; + border.DragLeave += ItemBorder_DragLeave; + border.DragOver += ItemBorder_DragOver; + border.Drop += ItemBorder_Drop; + border.AllowDrop = true; + border.IsTabStop = false; } if (GetTemplateChild("ChildrenPresenter") is ItemsRepeater repeater) @@ -96,9 +92,7 @@ public void HandleItemChange() HookupItemChangeListener(null, Item); UpdateExpansionState(); ReevaluateSelection(); - - if (Item is not null) - Decorator = Item.ItemDecorator; + CanDrag = Item?.GetType().GetProperty("Path")?.GetValue(Item) is string path && Path.IsPathRooted(path); } private void HookupOwners() @@ -127,30 +121,49 @@ private void HookupItemChangeListener(ISidebarItemModel? oldItem, ISidebarItemMo { if (lastSubscriber != null) { - lastSubscriber.PropertyChanged -= ItemPropertyChangedHandler; if (lastSubscriber.Children is INotifyCollectionChanged observableCollection) observableCollection.CollectionChanged -= ChildItems_CollectionChanged; } if (oldItem != null) { - oldItem.PropertyChanged -= ItemPropertyChangedHandler; if (oldItem.Children is INotifyCollectionChanged observableCollection) observableCollection.CollectionChanged -= ChildItems_CollectionChanged; } if (newItem != null) { - newItem.PropertyChanged += ItemPropertyChangedHandler; lastSubscriber = newItem; if (newItem.Children is INotifyCollectionChanged observableCollection) observableCollection.CollectionChanged += ChildItems_CollectionChanged; } - UpdateIcon(); } private void SidebarItem_DragStarting(UIElement sender, DragStartingEventArgs args) { - args.Data.SetData(StandardDataFormats.Text, Item!.Text.ToString()); + if (Item?.GetType().GetProperty("Path")?.GetValue(Item) is not string dragPath || !Path.IsPathRooted(dragPath)) + return; + + args.Data.SetData(StandardDataFormats.Text, dragPath); + args.Data.RequestedOperation = DataPackageOperation.Move | DataPackageOperation.Copy | DataPackageOperation.Link; + args.Data.SetDataProvider(StandardDataFormats.StorageItems, async request => + { + var deferral = request.GetDeferral(); + try + { + if (Directory.Exists(dragPath)) + { + var folder = await StorageFolder.GetFolderFromPathAsync(dragPath); + request.SetData(new IStorageItem[] { folder }); + } + } + catch + { + } + finally + { + deferral.Complete(); + } + }); } private void SetFlyoutOpen(bool isOpen = true) @@ -178,14 +191,6 @@ private void ChildItems_CollectionChanged(object? sender, System.Collections.Spe } } - void ItemPropertyChangedHandler(object? sender, PropertyChangedEventArgs args) - { - if (args.PropertyName == nameof(ISidebarItemModel.IconSource)) - { - UpdateIcon(); - } - } - private void ReevaluateSelection() { if (!IsGroupHeader) @@ -283,13 +288,6 @@ private void UpdateSelectionState() UpdatePointerState(); } - private void UpdateIcon() - { - Icon = Item?.IconSource?.CreateIconElement(); - if (Icon is not null) - AutomationProperties.SetAccessibilityView(Icon, AccessibilityView.Raw); - } - private bool ShouldShowSelectionIndicator() { if (IsExpanded && CollapseEnabled) @@ -323,7 +321,7 @@ private void UpdateExpansionState(bool useAnimations = true) { if (Item?.Children is null || !CollapseEnabled) { - VisualStateManager.GoToState(this, IsHomeItem ? "NoExpansionWithPadding" : "NoExpansion", useAnimations); + VisualStateManager.GoToState(this, Item?.PaddedItem == true ? "NoExpansionWithPadding" : "NoExpansion", useAnimations); } else if (!HasChildren) { @@ -345,26 +343,26 @@ private void UpdateExpansionState(bool useAnimations = true) UpdateSelectionState(); } - private void ItemGrid_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) + private void ItemBorder_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) { isPointerOver = true; UpdatePointerState(); } - private void ItemGrid_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) + private void ItemBorder_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) { isPointerOver = false; isClicking = false; UpdatePointerState(); } - private void ItemGrid_PointerCanceled(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) + private void ItemBorder_PointerCanceled(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) { isClicking = false; UpdatePointerState(); } - private void ItemGrid_PointerPressed(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) + private void ItemBorder_PointerPressed(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) { isClicking = true; UpdatePointerState(true); @@ -389,7 +387,7 @@ private void Item_PointerReleased(object sender, Microsoft.UI.Xaml.Input.Pointer } } - private async void ItemGrid_DragOver(object sender, DragEventArgs e) + private async void ItemBorder_DragOver(object sender, DragEventArgs e) { if (HasChildren) { @@ -410,34 +408,24 @@ private async void ItemGrid_DragOver(object sender, DragEventArgs e) VisualStateManager.GoToState(this, "DragInsertBelow", true); } - if (Owner is not null) - { - var deferral = e.GetDeferral(); - await Owner.RaiseItemDragOver(this, insertsAbove, e); - deferral.Complete(); - } + Owner?.RaiseItemDragOver(this, insertsAbove, e); } - private void ItemGrid_ContextRequested(UIElement sender, Microsoft.UI.Xaml.Input.ContextRequestedEventArgs args) + private void ItemBorder_ContextRequested(UIElement sender, Microsoft.UI.Xaml.Input.ContextRequestedEventArgs args) { Owner?.RaiseContextRequested(this, args.TryGetPosition(this, out var point) ? point : default); args.Handled = true; } - private void ItemGrid_DragLeave(object sender, DragEventArgs e) + private void ItemBorder_DragLeave(object sender, DragEventArgs e) { UpdatePointerState(); } - private async void ItemGrid_Drop(object sender, DragEventArgs e) + private void ItemBorder_Drop(object sender, DragEventArgs e) { UpdatePointerState(); - if (Owner is not null) - { - var deferral = e.GetDeferral(); - await Owner.RaiseItemDropped(this, DetermineDropTargetPosition(e), e); - deferral.Complete(); - } + Owner?.RaiseItemDropped(this, DetermineDropTargetPosition(e), e); } private SidebarItemDropPosition DetermineDropTargetPosition(DragEventArgs args) diff --git a/src/Files.App/UserControls/Sidebar/SidebarItemAutomationPeer.cs b/src/Files.App.Controls/Sidebar/SidebarItemAutomationPeer.cs similarity index 80% rename from src/Files.App/UserControls/Sidebar/SidebarItemAutomationPeer.cs rename to src/Files.App.Controls/Sidebar/SidebarItemAutomationPeer.cs index 3f6466805b01..d4e4da352f63 100644 --- a/src/Files.App/UserControls/Sidebar/SidebarItemAutomationPeer.cs +++ b/src/Files.App.Controls/Sidebar/SidebarItemAutomationPeer.cs @@ -1,15 +1,18 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using CommunityToolkit.WinUI.UI; +using CommunityToolkit.WinUI; using Microsoft.UI.Input; using Microsoft.UI.Xaml.Automation; using Microsoft.UI.Xaml.Automation.Peers; using Microsoft.UI.Xaml.Automation.Provider; -namespace Files.App.UserControls.Sidebar +namespace Files.App.Controls { - public sealed class SidebarItemAutomationPeer : FrameworkElementAutomationPeer, IInvokeProvider, IExpandCollapseProvider, ISelectionItemProvider + /// + /// Exposes types to Microsoft UI Automation. + /// + public sealed partial class SidebarItemAutomationPeer : FrameworkElementAutomationPeer, IInvokeProvider, IExpandCollapseProvider, ISelectionItemProvider { public ExpandCollapseState ExpandCollapseState { @@ -17,6 +20,7 @@ public ExpandCollapseState ExpandCollapseState { if (Owner.HasChildren) return Owner.IsExpanded ? ExpandCollapseState.Expanded : ExpandCollapseState.Collapsed; + return ExpandCollapseState.LeafNode; } } @@ -27,7 +31,7 @@ public ExpandCollapseState ExpandCollapseState public SidebarItemAutomationPeer(SidebarItem owner) : base(owner) { - this.Owner = owner; + Owner = owner; } protected override AutomationControlType GetAutomationControlTypeCore() @@ -37,7 +41,7 @@ protected override AutomationControlType GetAutomationControlTypeCore() protected override string GetNameCore() { - return Owner.Item?.Text ?? ""; + return Owner?.Text ?? string.Empty; } protected override object GetPatternCore(PatternInterface patternInterface) @@ -49,28 +53,22 @@ protected override object GetPatternCore(PatternInterface patternInterface) else if (patternInterface == PatternInterface.ExpandCollapse) { if (Owner.CollapseEnabled) - { return this; - } } + return base.GetPatternCore(patternInterface); } public void Collapse() { if (Owner.CollapseEnabled) - { Owner.IsExpanded = false; - } } public void Expand() { - if (Owner.CollapseEnabled) - { Owner.IsExpanded = true; - } } public void Invoke() @@ -106,13 +104,11 @@ protected override int GetPositionInSetCore() private IList GetOwnerCollection() { if (Owner.FindAscendant() is SidebarItem parent && parent.Item?.Children is IList list) - { return list; - } - if (Owner?.Owner is not null && Owner.Owner.ViewModel.SidebarItems is IList items) - { + + if (Owner?.Owner is not null && Owner.Owner?.MenuItemsSource is IList items) return items; - } + return new List(); } } diff --git a/src/Files.App/UserControls/Sidebar/SidebarItemDropPosition.cs b/src/Files.App.Controls/Sidebar/SidebarItemDropPosition.cs similarity index 82% rename from src/Files.App/UserControls/Sidebar/SidebarItemDropPosition.cs rename to src/Files.App.Controls/Sidebar/SidebarItemDropPosition.cs index 169d6894027e..7ce253577529 100644 --- a/src/Files.App/UserControls/Sidebar/SidebarItemDropPosition.cs +++ b/src/Files.App.Controls/Sidebar/SidebarItemDropPosition.cs @@ -1,7 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -namespace Files.App.UserControls.Sidebar +namespace Files.App.Controls { /// /// The position of the item that was dropped on the sidebar item. diff --git a/src/Files.App.Controls/Sidebar/SidebarStyles.xaml b/src/Files.App.Controls/Sidebar/SidebarStyles.xaml new file mode 100644 index 000000000000..a51d65ef1ac2 --- /dev/null +++ b/src/Files.App.Controls/Sidebar/SidebarStyles.xaml @@ -0,0 +1,485 @@ + + + + 300 + -300 + 56 + -56 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App/UserControls/Sidebar/SidebarView.Properties.cs b/src/Files.App.Controls/Sidebar/SidebarView.Properties.cs similarity index 87% rename from src/Files.App/UserControls/Sidebar/SidebarView.Properties.cs rename to src/Files.App.Controls/Sidebar/SidebarView.Properties.cs index 2bc46c456118..9bf6bebed9ec 100644 --- a/src/Files.App/UserControls/Sidebar/SidebarView.Properties.cs +++ b/src/Files.App.Controls/Sidebar/SidebarView.Properties.cs @@ -1,9 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Microsoft.UI.Xaml; - -namespace Files.App.UserControls.Sidebar +namespace Files.App.Controls { public sealed partial class SidebarView { @@ -23,6 +21,14 @@ public UIElement InnerContent public static readonly DependencyProperty InnerContentProperty = DependencyProperty.Register(nameof(InnerContent), typeof(UIElement), typeof(SidebarView), new PropertyMetadata(null)); + public UIElement SidebarContent + { + get { return (UIElement)GetValue(SidebarContentProperty); } + set { SetValue(SidebarContentProperty, value); } + } + public static readonly DependencyProperty SidebarContentProperty = + DependencyProperty.Register("SidebarContent", typeof(UIElement), typeof(SidebarView), new PropertyMetadata(null)); + public UIElement Footer { get { return (UIElement)GetValue(FooterProperty); } @@ -59,14 +65,6 @@ public double NegativeOpenPaneLength public static readonly DependencyProperty NegativeOpenPaneLengthProperty = DependencyProperty.Register(nameof(NegativeOpenPaneLength), typeof(double), typeof(SidebarView), new PropertyMetadata(null)); - public ISidebarViewModel ViewModel - { - get => (ISidebarViewModel)GetValue(ViewModelProperty); - set => SetValue(ViewModelProperty, value); - } - public static readonly DependencyProperty ViewModelProperty = - DependencyProperty.Register(nameof(ViewModel), typeof(ISidebarViewModel), typeof(SidebarView), new PropertyMetadata(null)); - public ISidebarItemModel SelectedItem { get => (ISidebarItemModel)GetValue(SelectedItemProperty); diff --git a/src/Files.App/UserControls/Sidebar/SidebarView.xaml b/src/Files.App.Controls/Sidebar/SidebarView.xaml similarity index 97% rename from src/Files.App/UserControls/Sidebar/SidebarView.xaml rename to src/Files.App.Controls/Sidebar/SidebarView.xaml index 6943e756e691..aae90d4bf35d 100644 --- a/src/Files.App/UserControls/Sidebar/SidebarView.xaml +++ b/src/Files.App.Controls/Sidebar/SidebarView.xaml @@ -1,10 +1,9 @@ - + @@ -59,11 +57,21 @@ + + + @@ -118,7 +126,6 @@ Visibility="{Binding ElementName=PaneRoot, Path=Visibility, Mode=OneWay}"> diff --git a/src/Files.App/UserControls/Sidebar/SidebarView.xaml.cs b/src/Files.App.Controls/Sidebar/SidebarView.xaml.cs similarity index 82% rename from src/Files.App/UserControls/Sidebar/SidebarView.xaml.cs rename to src/Files.App.Controls/Sidebar/SidebarView.xaml.cs index 0fc4c3bf2674..1ec03c66f9f4 100644 --- a/src/Files.App/UserControls/Sidebar/SidebarView.xaml.cs +++ b/src/Files.App.Controls/Sidebar/SidebarView.xaml.cs @@ -1,24 +1,24 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Input; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; using Microsoft.UI.Xaml.Markup; using Windows.Foundation; using Windows.System; using Windows.UI.Core; -namespace Files.App.UserControls.Sidebar +namespace Files.App.Controls { [ContentProperty(Name = "InnerContent")] public sealed partial class SidebarView : UserControl, INotifyPropertyChanged { private const double COMPACT_MAX_WIDTH = 200; - public event EventHandler? ItemInvoked; + public event EventHandler? ItemInvoked; public event EventHandler? ItemContextInvoked; + public event EventHandler? ItemDragOver; + public event EventHandler? ItemDropped; public event PropertyChangedEventHandler? PropertyChanged; internal SidebarItem? SelectedItemContainer = null; @@ -42,26 +42,24 @@ internal void RaiseItemInvoked(SidebarItem item, PointerUpdateKind pointerUpdate if (item.Item is null || item.IsGroupHeader) return; SelectedItem = item.Item; - ItemInvoked?.Invoke(item, item.Item); - ViewModel.HandleItemInvokedAsync(item.Item, pointerUpdateKind); + ItemInvoked?.Invoke(item, new(pointerUpdateKind)); } internal void RaiseContextRequested(SidebarItem item, Point e) { - ItemContextInvoked?.Invoke(item, new ItemContextInvokedArgs(item.Item, e)); - ViewModel.HandleItemContextInvokedAsync(item, new ItemContextInvokedArgs(item.Item, e)); + ItemContextInvoked?.Invoke(item, new(item.Item, e)); } - internal async Task RaiseItemDropped(SidebarItem sideBarItem, SidebarItemDropPosition dropPosition, DragEventArgs rawEvent) + internal void RaiseItemDropped(SidebarItem sideBarItem, SidebarItemDropPosition dropPosition, DragEventArgs rawEvent) { if (sideBarItem.Item is null) return; - await ViewModel.HandleItemDroppedAsync(new ItemDroppedEventArgs(sideBarItem.Item, rawEvent.DataView, dropPosition, rawEvent)); + ItemDropped?.Invoke(this, new(sideBarItem.Item, rawEvent.DataView, dropPosition, rawEvent)); } - internal async Task RaiseItemDragOver(SidebarItem sideBarItem, SidebarItemDropPosition dropPosition, DragEventArgs rawEvent) + internal void RaiseItemDragOver(SidebarItem sideBarItem, SidebarItemDropPosition dropPosition, DragEventArgs rawEvent) { if (sideBarItem.Item is null) return; - await ViewModel.HandleItemDragOverAsync(new ItemDragOverEventArgs(sideBarItem.Item, rawEvent.DataView, dropPosition, rawEvent)); + ItemDragOver?.Invoke(this, new(sideBarItem.Item, rawEvent.DataView, dropPosition, rawEvent)); } private void UpdateMinimalMode() @@ -230,10 +228,9 @@ private void SidebarResizer_ManipulationCompleted(object sender, ManipulationCom e.Handled = true; } - private void PaneColumnGrid_ContextRequested(UIElement sender, ContextRequestedEventArgs e) + private void MenuItemHostScrollViewer_ContextRequested(UIElement sender, ContextRequestedEventArgs e) { - var newArgs = new ItemContextInvokedArgs(null, e.TryGetPosition(this, out var point) ? point : default); - ViewModel.HandleItemContextInvokedAsync(this, newArgs); + ItemContextInvoked?.Invoke(this, new(null, e.TryGetPosition(this, out var point) ? point : default)); e.Handled = true; } diff --git a/src/Files.App.Controls/Sidebar/SidebarViewAutomationPeer.cs b/src/Files.App.Controls/Sidebar/SidebarViewAutomationPeer.cs new file mode 100644 index 000000000000..6940756ea0da --- /dev/null +++ b/src/Files.App.Controls/Sidebar/SidebarViewAutomationPeer.cs @@ -0,0 +1,40 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Automation.Peers; +using Microsoft.UI.Xaml.Automation.Provider; + +namespace Files.App.Controls +{ + /// + /// Exposes types to Microsoft UI Automation. + /// + public sealed partial class SidebarViewAutomationPeer : FrameworkElementAutomationPeer, ISelectionProvider + { + public bool CanSelectMultiple => false; + public bool IsSelectionRequired => true; + + private new SidebarView Owner { get; init; } + + public SidebarViewAutomationPeer(SidebarView owner) : base(owner) + { + Owner = owner; + } + + protected override object GetPatternCore(PatternInterface patternInterface) + { + if (patternInterface is PatternInterface.Selection) + return this; + + return base.GetPatternCore(patternInterface); + } + + public IRawElementProviderSimple[] GetSelection() + { + if (Owner.SelectedItemContainer != null) + return [ProviderFromPeer(CreatePeerForElement(Owner.SelectedItemContainer))]; + + return []; + } + } +} diff --git a/src/Files.App.Controls/Storage/Data/BarShapes.cs b/src/Files.App.Controls/Storage/Data/BarShapes.cs new file mode 100644 index 000000000000..740a622e9edd --- /dev/null +++ b/src/Files.App.Controls/Storage/Data/BarShapes.cs @@ -0,0 +1,26 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + /// + /// Defines BarShape for . + /// + public enum BarShapes + { + /// + /// The BarShape for Round StorageBars. Default state. + /// + Round, + + /// + /// The BarShape for Soft StorageBars. + /// + Soft, + + /// + /// The BarShape for Flat StorageBars. + /// + Flat, + } +} diff --git a/src/Files.App.Controls/Storage/Data/ThicknessCheck.cs b/src/Files.App.Controls/Storage/Data/ThicknessCheck.cs new file mode 100644 index 000000000000..dfd271731a55 --- /dev/null +++ b/src/Files.App.Controls/Storage/Data/ThicknessCheck.cs @@ -0,0 +1,27 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + /// + /// Defines ThicknessCheck values for and . + /// + public enum ThicknessCheck + { + /// + /// The ThicknessCheck for when the Value Thickness is thickest. + /// + Value, + + /// + /// The ThicknessCheck for when the Track Thickness is thickest. + /// + Track, + + /// + /// The ThicknessCheck for when the both Value and Track + /// Thickness is equal. + /// + Equal, + } +} diff --git a/src/Files.App.Controls/Storage/RingShape/RingShape.Properties.cs b/src/Files.App.Controls/Storage/RingShape/RingShape.Properties.cs new file mode 100644 index 000000000000..2d6680a3115b --- /dev/null +++ b/src/Files.App.Controls/Storage/RingShape/RingShape.Properties.cs @@ -0,0 +1,86 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using CommunityToolkit.WinUI; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Shapes; +using Windows.Foundation; + +namespace Files.App.Controls.Primitives +{ + public partial class RingShape : Path + { + [GeneratedDependencyProperty(DefaultValue = 0.0d)] + public partial double StartAngle { get; set; } + + [GeneratedDependencyProperty(DefaultValue = 90.0d)] + public partial double EndAngle { get; set; } + + [GeneratedDependencyProperty(DefaultValue = SweepDirection.Clockwise)] + public partial SweepDirection SweepDirection { get; set; } + + [GeneratedDependencyProperty(DefaultValue = 0.0d)] + public partial double MinAngle { get; set; } + + [GeneratedDependencyProperty(DefaultValue = 360.0d)] + public partial double MaxAngle { get; set; } + + [GeneratedDependencyProperty(DefaultValue = 0.0d)] + public partial double RadiusWidth { get; set; } + + [GeneratedDependencyProperty(DefaultValue = 0.0d)] + public partial double RadiusHeight { get; set; } + + [GeneratedDependencyProperty(DefaultValue = false)] + public partial bool IsCircle { get; set; } + + [GeneratedDependencyProperty] + public partial Point Center { get; set; } + + [GeneratedDependencyProperty(DefaultValue = 0.0d)] + public partial double ActualRadiusWidth { get; set; } + + [GeneratedDependencyProperty(DefaultValue = 0.0d)] + public partial double ActualRadiusHeight { get; set; } + + partial void OnStartAngleChanged(double newValue) + { + StartAngleChanged(); + } + + partial void OnEndAngleChanged(double newValue) + { + EndAngleChanged(); + } + + partial void OnSweepDirectionChanged(SweepDirection newValue) + { + SweepDirectionChanged(); + } + + partial void OnMinAngleChanged(double newValue) + { + MinMaxAngleChanged(false); + } + + partial void OnMaxAngleChanged(double newValue) + { + MinMaxAngleChanged(true); + } + + partial void OnRadiusWidthChanged(double newValue) + { + RadiusWidthChanged(); + } + + partial void OnRadiusHeightChanged(double newValue) + { + RadiusHeightChanged(); + } + + partial void OnIsCircleChanged(bool newValue) + { + IsCircleChanged(); + } + } +} diff --git a/src/Files.App.Controls/Storage/RingShape/RingShape.cs b/src/Files.App.Controls/Storage/RingShape/RingShape.cs new file mode 100644 index 000000000000..8bcc95b2e03c --- /dev/null +++ b/src/Files.App.Controls/Storage/RingShape/RingShape.cs @@ -0,0 +1,496 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Shapes; +using Windows.Foundation; + +namespace Files.App.Controls.Primitives +{ + /// + /// Represents primitive Path shape for drawing a circular or elliptical ring. + /// + public partial class RingShape : Path + { + #region Fields and Constructors + + // Fields + + private bool _isUpdating; // Is True when path is updating + private bool _isCircle; // When True, Width and Height are equalized + private Size _equalSize; // Calculated where Width and Height are equal + private double _equalRadius; // Calculated where RadiusWidth and RadiusHeight are equal + private Point _centerPoint; // Center Point within Width and Height bounds + private double _normalizedMinAngle; // Normalized MinAngle between -180 and 540 + private double _normalizedMaxAngle; // Normalized MaxAngle between 0 and 360 + private double _validStartAngle; // The validated StartAngle + private double _validEndAngle; // The validated EndAngle + private double _radiusWidth; // The radius Width + private double _radiusHeight; // The radius Height + private SweepDirection _sweepDirection; // The SweepDirection + + // Constants + + private const double DegreesToRadians = Math.PI / 180; + + // Constructor + + /// + /// Initializes an instance of the class. + /// + public RingShape() + { + SizeChanged += RingShape_SizeChanged; + RegisterPropertyChangedCallback(StrokeThicknessProperty, OnStrokeThicknessChanged); + } + + #endregion + + #region PropertyChanged Events + + private void StartAngleChanged() + { + BeginUpdate(); + ValidateAngle(this, StartAngle, true); + EndUpdate(); + } + + private void EndAngleChanged() + { + BeginUpdate(); + ValidateAngle(this, EndAngle, false); + EndUpdate(); + } + + private void IsCircleChanged() + { + BeginUpdate(); + _isCircle = IsCircle; + EndUpdate(); + } + + private void RadiusWidthChanged() + { + BeginUpdate(); + AdjustRadiusWidth(this, RadiusWidth, StrokeThickness); + EndUpdate(); + } + + private void RadiusHeightChanged() + { + BeginUpdate(); + AdjustRadiusHeight(this, RadiusHeight, StrokeThickness); + EndUpdate(); + } + + private void RingShape_SizeChanged(object obj, SizeChangedEventArgs e) + { + BeginUpdate(); + EndUpdate(); + } + + private void OnStrokeThicknessChanged(DependencyObject d, DependencyProperty dp) + { + BeginUpdate(); + EndUpdate(); + } + + private void MinMaxAngleChanged(bool isMax) + { + BeginUpdate(); + CalculateAndSetNormalizedAngles(this, MinAngle, MaxAngle); + EndUpdate(); + } + + private void SweepDirectionChanged() + { + BeginUpdate(); + _sweepDirection = SweepDirection; + EndUpdate(); + } + + #endregion + + #region RingShape Path Updates + + /// + /// Suspends path updates until EndUpdate is called + /// + public void BeginUpdate() + { + _isUpdating = true; + } + + /// + /// Resumes immediate path updates every time a component property value changes. Updates the path + /// + public void EndUpdate() + { + _isUpdating = false; + UpdatePath(); + } + + private void UpdatePath() + { + if (_isUpdating || + ActualWidth <= 0 || ActualHeight <= 0 || + _radiusWidth <= 0 || _radiusHeight <= 0) + return; + + UpdateSizeAndStroke(this); + + var startAngle = _validStartAngle; + var endAngle = _validEndAngle; + + // If the ring is closed and complete + if (endAngle >= startAngle + 360) + { + Data = DrawEllipse(_isCircle, _centerPoint, _equalRadius, _radiusWidth, _radiusHeight); + } + else + { + this.InvalidateArrange(); + this.Data = DrawArc(this, _sweepDirection, _isCircle, _centerPoint, startAngle, endAngle, _equalRadius, _radiusWidth, _radiusHeight); + } + } + + #endregion + + #region Drawing Updates + + /// + /// Updates sizes, center point and radii + /// + /// The DependencyObject calling the function + public void UpdateSizeAndStroke(DependencyObject d) + { + RingShape ringShape = (RingShape)d; + + AdjustRadiusWidth(ringShape, ringShape.RadiusWidth, ringShape.StrokeThickness); + AdjustRadiusHeight(ringShape, ringShape.RadiusHeight, ringShape.StrokeThickness); + + _equalSize = CalculateEqualSize(new Size(ringShape.Width, ringShape.Height), ringShape.StrokeThickness); + _equalRadius = CalculateEqualRadius(ringShape, ringShape.RadiusWidth, ringShape.RadiusHeight, ringShape.StrokeThickness); + + _centerPoint = new Point(ringShape.Width / 2, ringShape.Height / 2); + ringShape.Center = _centerPoint; + + CalculateAndSetNormalizedAngles(ringShape, ringShape.MinAngle, ringShape.MaxAngle); + + ValidateAngle(ringShape, ringShape.StartAngle, true); + ValidateAngle(ringShape, ringShape.EndAngle, false); + } + + private static EllipseGeometry DrawEllipse(bool IsCircle, Point Center, double EqualRadius, double RadiusWidth, double RadiusHeight) + { + EllipseGeometry eg; + + if (IsCircle == true) + { + eg = new EllipseGeometry + { + Center = Center, + RadiusX = EqualRadius, + RadiusY = EqualRadius, + }; + } + else + { + eg = new EllipseGeometry + { + Center = Center, + RadiusX = RadiusWidth, + RadiusY = RadiusHeight, + }; + } + + return eg; + } + + private static PathGeometry DrawArc(RingShape RingShape, SweepDirection SweepDirection, bool IsCircle, Point Center, double StartAngle, double EndAngle, double EqualRadius, double RadiusWidth, double RadiusHeight) + { + var pathGeometry = new PathGeometry(); + var pathFigure = new PathFigure(); + pathFigure.IsClosed = false; + pathFigure.IsFilled = false; + + var newCenter = Center; + + var arcSegment = new ArcSegment(); + + if (IsCircle == true) + { + var radius = EqualRadius; + + RingShape.ActualRadiusWidth = radius; + RingShape.ActualRadiusHeight = radius; + + // Start Point + pathFigure.StartPoint = ArcStartPoint(SweepDirection, newCenter, StartAngle, radius, radius); + + + // Arc Segment and End Point + arcSegment = CreateArcSegment(SweepDirection, newCenter, StartAngle, EndAngle, radius, radius); + } + else + { + var radiusWidth = RadiusWidth; + var radiusHeight = RadiusHeight; + + RingShape.ActualRadiusWidth = radiusWidth; + RingShape.ActualRadiusHeight = radiusHeight; + + // Start Point + pathFigure.StartPoint = ArcStartPoint(SweepDirection, newCenter, StartAngle, radiusWidth, radiusHeight); + + + // Arc Segment and End Point + arcSegment = CreateArcSegment(SweepDirection, newCenter, StartAngle, EndAngle, radiusWidth, radiusHeight); + } + + pathFigure.Segments.Add(arcSegment); + pathGeometry.Figures.Add(pathFigure); + + return pathGeometry; + } + + private static Point ArcStartPoint(SweepDirection SweepDirection, Point Center, double StartAngle, double RadiusWidth, double RadiusHeight) + { + var finalPoint = new Point(); + + // Counterclockwise + if (SweepDirection == SweepDirection.Counterclockwise) + { + finalPoint = + new Point( + Center.X - Math.Sin(StartAngle * DegreesToRadians) * RadiusWidth, + Center.Y - Math.Cos(StartAngle * DegreesToRadians) * RadiusHeight); + } + // Clockwise + else + { + finalPoint = + new Point( + Center.X + Math.Sin(StartAngle * DegreesToRadians) * RadiusWidth, + Center.Y - Math.Cos(StartAngle * DegreesToRadians) * RadiusHeight); + } + + return finalPoint; + } + + private static ArcSegment CreateArcSegment(SweepDirection SweepDirection, Point Center, double StartAngle, double EndAngle, double RadiusWidth, double RadiusHeight) + { + var finalArcSegment = new ArcSegment(); + + // Counterclockwise + if (SweepDirection == SweepDirection.Counterclockwise) + { + finalArcSegment.Point = + new Point( + Center.X - Math.Sin(EndAngle * DegreesToRadians) * RadiusWidth, + Center.Y - Math.Cos(EndAngle * DegreesToRadians) * RadiusHeight); + + if (EndAngle < StartAngle) + { + finalArcSegment.IsLargeArc = (EndAngle - StartAngle) <= -180.0; + finalArcSegment.SweepDirection = SweepDirection.Clockwise; + } + else + { + finalArcSegment.IsLargeArc = (EndAngle - StartAngle) >= 180.0; + finalArcSegment.SweepDirection = SweepDirection.Counterclockwise; + } + } + // Clockwise + else + { + finalArcSegment.Point = + new Point( + Center.X + Math.Sin(EndAngle * DegreesToRadians) * RadiusWidth, + Center.Y - Math.Cos(EndAngle * DegreesToRadians) * RadiusHeight); + //ArcSegment.IsLargeArc = ( EndAngle - StartAngle ) >= 180.0; + if (EndAngle < StartAngle) + { + finalArcSegment.IsLargeArc = (EndAngle - StartAngle) <= -180.0; + finalArcSegment.SweepDirection = SweepDirection.Counterclockwise; + } + else + { + finalArcSegment.IsLargeArc = (EndAngle - StartAngle) >= 180.0; + finalArcSegment.SweepDirection = SweepDirection.Clockwise; + } + } + finalArcSegment.Size = new Size(RadiusWidth, RadiusHeight); + + return finalArcSegment; + } + + #endregion + + #region Value Calculations + + private static Size CalculateEqualSize(Size size, double strokeThickness) + { + double adjWidth = size.Width; + double adjHeight = size.Height; + + var smaller = Math.Min(adjWidth, adjHeight); + + if (smaller > strokeThickness * 2) + { + return new Size(smaller, smaller); + } + else + { + return new Size(strokeThickness * 2, strokeThickness * 2); + } + } + + private static double CalculateEqualRadius(DependencyObject d, double radiusWidth, double radiusHeight, double strokeThickness) + { + RingShape ringShape = (RingShape)d; + + double adjWidth = radiusWidth; + double adjHeight = radiusHeight; + + var smaller = Math.Min(adjWidth, adjHeight); + + if (smaller <= strokeThickness) + { + return strokeThickness; + } + else if (smaller >= ((ringShape._equalSize.Width / 2) - (strokeThickness / 2))) + { + return (ringShape._equalSize.Width / 2) - (strokeThickness / 2); + } + else + { + return smaller; + } + } + + private static Point CalculateCenter(double Width, double Height) + { + Point calculatedCenter = new Point((Width / 2.0), (Height / 2.0)); + + return calculatedCenter; + } + + private static void CalculateAndSetNormalizedAngles(DependencyObject d, double minAngle, double maxAngle) + { + RingShape ringShape = (RingShape)d; + + var result = CalculateModulus(minAngle, 360); + + if (result >= 180) + result = result - 360; + + ringShape._normalizedMinAngle = result; + + result = CalculateModulus(maxAngle, 360); + + if (result < 180) + result = result + 360; + + if (result > ringShape._normalizedMinAngle + 360) + result = result - 360; + + + ringShape._normalizedMaxAngle = result; + } + + private static double CalculateModulus(double number, double divider) + { + // Calculate the modulus + var result = number % divider; + + // Ensure the result is positive or zero + result = result < 0 ? result + divider : result; + + return result; + } + + private void ValidateAngle(DependencyObject d, double angle, bool isStart) + { + RingShape ringShape = (RingShape)d; + + if (angle >= _normalizedMaxAngle) + { + if (isStart == true) + { + _validStartAngle = _normalizedMaxAngle; + } + else + { + _validEndAngle = _normalizedMaxAngle; + } + } + else if (angle <= _normalizedMinAngle) + { + if (isStart == true) + { + _validStartAngle = _normalizedMinAngle; + } + else + { + _validEndAngle = _normalizedMinAngle; + } + } + else + { + if (isStart == true) + { + _validStartAngle = angle; + } + else + { + _validEndAngle = angle; + } + } + } + + private void AdjustRadiusWidth(DependencyObject d, double radiusWidth, double strokeThickness) + { + RingShape ringShape = (RingShape)d; + + var maxValue = (ringShape.Width / 2) - (ringShape.StrokeThickness / 2); + var threshold = strokeThickness; + + if (radiusWidth >= maxValue) + { + ringShape._radiusWidth = maxValue; + } + else if (radiusWidth <= maxValue && radiusWidth >= threshold) + { + ringShape._radiusWidth = radiusWidth; + } + else + { + ringShape._radiusWidth = threshold; + } + } + + private void AdjustRadiusHeight(DependencyObject d, double radiusHeight, double strokeThickness) + { + RingShape ringShape = (RingShape)d; + + var maxValue = (ringShape.Height / 2) - (ringShape.StrokeThickness / 2); + var threshold = strokeThickness; + + if (radiusHeight >= maxValue) + { + ringShape._radiusHeight = maxValue; + } + else if (radiusHeight <= maxValue && radiusHeight >= threshold) + { + ringShape._radiusHeight = radiusHeight; + } + else + { + ringShape._radiusHeight = threshold; + } + } + + #endregion + } +} diff --git a/src/Files.App.Controls/Storage/StorageBar/StorageBar.Properties.cs b/src/Files.App.Controls/Storage/StorageBar/StorageBar.Properties.cs new file mode 100644 index 000000000000..bee33c739576 --- /dev/null +++ b/src/Files.App.Controls/Storage/StorageBar/StorageBar.Properties.cs @@ -0,0 +1,82 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using CommunityToolkit.WinUI; + +namespace Files.App.Controls +{ + public partial class StorageBar + { + [GeneratedDependencyProperty(DefaultValue = 6.0d)] + public partial double ValueBarHeight { get; set; } + + [GeneratedDependencyProperty(DefaultValue = 3.0d)] + public partial double TrackBarHeight { get; set; } + + [GeneratedDependencyProperty(DefaultValue = BarShapes.Round)] + public partial BarShapes BarShape { get; set; } + + [GeneratedDependencyProperty(DefaultValue = 0.0d)] + public partial double Percent { get; set; } + + [GeneratedDependencyProperty(DefaultValue = 75.1d)] + public partial double PercentCaution { get; set; } + + [GeneratedDependencyProperty(DefaultValue = 89.9d)] + public partial double PercentCritical { get; set; } + + partial void OnValueBarHeightChanged(double newValue) + { + UpdateControl(); + } + + partial void OnTrackBarHeightChanged(double newValue) + { + UpdateControl(); + } + + partial void OnBarShapeChanged(BarShapes newValue) + { + UpdateControl(); + } + + partial void OnPercentChanged(double newValue) + { + return; //Read-only + } + + partial void OnPercentCautionChanged(double newValue) + { + UpdateControl(); + } + + partial void OnPercentCriticalChanged(double newValue) + { + UpdateControl(); + } + + /// + protected override void OnValueChanged(double oldValue, double newValue) + { + base.OnValueChanged(oldValue, newValue); + + UpdateValue(Value, _oldValue, false, -1.0); + } + + /// + protected override void OnMaximumChanged(double oldValue, double newValue) + { + base.OnMaximumChanged(oldValue, newValue); + + UpdateValue(oldValue, newValue, false, -1.0); + } + + /// + protected override void OnMinimumChanged(double oldValue, double newValue) + { + base.OnMinimumChanged(oldValue, newValue); + + UpdateValue(oldValue, newValue, false, -1.0); + } + } +} diff --git a/src/Files.App.Controls/Storage/StorageBar/StorageBar.cs b/src/Files.App.Controls/Storage/StorageBar/StorageBar.cs new file mode 100644 index 000000000000..d570cc88d1fa --- /dev/null +++ b/src/Files.App.Controls/Storage/StorageBar/StorageBar.cs @@ -0,0 +1,386 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Windows.Foundation; + +namespace Files.App.Controls +{ + // TemplateParts + [TemplatePart(Name = TemplatePartName_Container, Type = typeof(Grid))] + [TemplatePart(Name = TemplatePartName_ValueColumn, Type = typeof(ColumnDefinition))] + [TemplatePart(Name = TemplatePartName__GapColumn, Type = typeof(ColumnDefinition))] + [TemplatePart(Name = TemplatePartName_TrackColumn, Type = typeof(ColumnDefinition))] + [TemplatePart(Name = TemplatePartName_ValueBorder, Type = typeof(Border))] + [TemplatePart(Name = TemplatePartName_TrackBorder, Type = typeof(Border))] + // VisualStates + [TemplateVisualState(GroupName = TemplateVisualStateGroupName_ControlStates, Name = TemplateVisualStateName_Safe)] + [TemplateVisualState(GroupName = TemplateVisualStateGroupName_ControlStates, Name = TemplateVisualStateName_Caution)] + [TemplateVisualState(GroupName = TemplateVisualStateGroupName_ControlStates, Name = TemplateVisualStateName_Critical)] + [TemplateVisualState(GroupName = TemplateVisualStateGroupName_ControlStates, Name = TemplateVisualStateName_Disabled)] + /// + /// Represents percentage ring islands. + /// + public partial class StorageBar : RangeBase + { + // Constants + + private const string TemplatePartName_Container = "PART_Container"; + private const string TemplatePartName_ValueColumn = "PART_ValueColumn"; + private const string TemplatePartName__GapColumn = "PART_GapColumn"; + private const string TemplatePartName_TrackColumn = "PART_TrackColumn"; + private const string TemplatePartName_ValueBorder = "PART_ValueBar"; + private const string TemplatePartName_TrackBorder = "PART_TrackBar"; + + private const string TemplateVisualStateGroupName_ControlStates = "ControlStates"; + private const string TemplateVisualStateName_Safe = "Safe"; + private const string TemplateVisualStateName_Caution = "Caution"; + private const string TemplateVisualStateName_Critical = "Critical"; + private const string TemplateVisualStateName_Disabled = "Disabled"; + + // Fields + + Grid _containerGrid = null!; + ColumnDefinition _valueColumn = null!; + ColumnDefinition _trackColumn = null!; + ColumnDefinition _gapColumn = null!; + Border _valueBarBorder = null!; + Border _trackBarBorder = null!; + + double _oldValue; + Size? _containerSize; + BarShapes _barShape; + double _gapWidth; + double _smallerHeight; + + // Constructor + + /// Initializes an instance of class. + public StorageBar() + { + DefaultStyleKey = typeof(StorageBar); + } + + // Methods + + /// + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + _containerGrid = GetTemplateChild(TemplatePartName_Container) as Grid + ?? throw new MissingFieldException($"Could not find {TemplatePartName_Container} in the given {nameof(StorageBar)}'s style."); + _valueColumn = GetTemplateChild(TemplatePartName_ValueColumn) as ColumnDefinition + ?? throw new MissingFieldException($"Could not find {TemplatePartName_ValueColumn} in the given {nameof(StorageBar)}'s style."); + _trackColumn = GetTemplateChild(TemplatePartName_TrackColumn) as ColumnDefinition + ?? throw new MissingFieldException($"Could not find {TemplatePartName_TrackColumn} in the given {nameof(StorageBar)}'s style."); + _gapColumn = GetTemplateChild(TemplatePartName__GapColumn) as ColumnDefinition + ?? throw new MissingFieldException($"Could not find {TemplatePartName__GapColumn} in the given {nameof(StorageBar)}'s style."); + _valueBarBorder = GetTemplateChild(TemplatePartName_ValueBorder) as Border + ?? throw new MissingFieldException($"Could not find {TemplatePartName_ValueBorder} in the given {nameof(StorageBar)}'s style."); + _trackBarBorder = GetTemplateChild(TemplatePartName_TrackBorder) as Border + ?? throw new MissingFieldException($"Could not find {TemplatePartName_TrackBorder} in the given {nameof(StorageBar)}'s style."); + + _barShape = BarShape; + + SizeChanged += StorageBar_SizeChanged; + Unloaded += StorageBar_Unloaded; + IsEnabledChanged += StorageBar_IsEnabledChanged; + } + + // Methods + + private void UpdateControl() + { + UpdateContainerHeightsAndCorners(); + UpdateColumnWidths(); + UpdateVisualState(); + } + + private void UpdateValue(double newValue, double oldValue, bool isPercent, double newPercent) + { + _oldValue = oldValue; + + var adjustedValue = isPercent ? StorageControlsHelpers.PercentageToValue(newPercent, Minimum, Maximum) : newValue; + Percent = StorageControlsHelpers.DoubleToPercentage(adjustedValue, Minimum, Maximum); + + UpdateControl(); + } + + private void UpdateContainerHeightsAndCorners() + { + // Finds the larger of the two height values + double calculatedLargerHeight = Math.Max(ValueBarHeight, TrackBarHeight); + double calculatedSmallerHeight = Math.Min(ValueBarHeight, TrackBarHeight); + + if (_valueBarBorder != null && _trackBarBorder != null && _containerGrid != null) + { + _valueBarBorder.Height = ValueBarHeight; + _trackBarBorder.Height = TrackBarHeight; + + if (_barShape is BarShapes.Round) + { + _valueBarBorder.CornerRadius = new(ValueBarHeight / 2); + _trackBarBorder.CornerRadius = new(TrackBarHeight / 2); + } + else if (_barShape is BarShapes.Soft) + { + _valueBarBorder.CornerRadius = new(ValueBarHeight / 4); + _trackBarBorder.CornerRadius = new(TrackBarHeight / 4); + } + else + { + _valueBarBorder.CornerRadius = new(0); + _trackBarBorder.CornerRadius = new(0); + } + + _containerGrid.Height = calculatedLargerHeight; + } + + _gapWidth = calculatedLargerHeight; + _smallerHeight = calculatedSmallerHeight; + + } + + private void UpdateColumnWidths() + { + if (_gapColumn != null && _valueColumn != null && _trackColumn != null && _valueBarBorder != null && _trackBarBorder != null) + { + if (_containerSize is not Size containerSize) + return; + + if (containerSize.Width > TrackBarHeight || containerSize.Width > ValueBarHeight) + { + double valuePercent = StorageControlsHelpers.DoubleToPercentage(Value, Minimum, Maximum); + double minPercent = StorageControlsHelpers.DoubleToPercentage(Minimum, Minimum, Maximum); + double maxPercent = StorageControlsHelpers.DoubleToPercentage(Maximum, Minimum, Maximum); + + if (valuePercent <= minPercent) + { + _gapColumn.Width = new(1, GridUnitType.Star); + _valueColumn.Width = new(1, GridUnitType.Star); + _trackColumn.Width = new(1, GridUnitType.Star); + + Grid.SetColumnSpan(_trackBarBorder, 3); + Grid.SetColumn(_trackBarBorder, 0); + + _valueBarBorder.Visibility = Visibility.Collapsed; + _trackBarBorder.Visibility = Visibility.Visible; + } + else if (valuePercent >= maxPercent) + { + _gapColumn.Width = new(1, GridUnitType.Star); + _valueColumn.Width = new(1, GridUnitType.Star); + _trackColumn.Width = new(1, GridUnitType.Star); + + Grid.SetColumnSpan(_valueBarBorder, 3); + Grid.SetColumn(_valueBarBorder, 0); + + _valueBarBorder.Visibility = Visibility.Visible; + _trackBarBorder.Visibility = Visibility.Collapsed; + } + else + { + Grid.SetColumnSpan(_valueBarBorder, 1); + Grid.SetColumn(_valueBarBorder, 0); + + Grid.SetColumnSpan(_trackBarBorder, 1); + Grid.SetColumn(_trackBarBorder, 2); + + _valueBarBorder.Visibility = Visibility.Visible; + _trackBarBorder.Visibility = Visibility.Visible; + + var valueBarHeight = ValueBarHeight; + var trackBarHeight = TrackBarHeight; + var gapWidth = _gapWidth; + + _valueColumn.MaxWidth = containerSize.Width; + _trackColumn.MaxWidth = containerSize.Width; + + var valueLarger = valueBarHeight > trackBarHeight; + + if (valuePercent > minPercent && valuePercent <= minPercent + 2.0) // Between 0% and 2% + { + var interpolatedValueBarHeight = StorageControlsHelpers.CalculateInterpolatedValue( + minPercent, + Percent, + minPercent + 2.0, + 0.0, + valueBarHeight, + true); + + var interpolatedTrackBarHeight = StorageControlsHelpers.CalculateInterpolatedValue( + minPercent, + Percent, + minPercent + 2.0, + 0.0, + trackBarHeight, + true); + + var interpolatedGapWidth = valueLarger + ? StorageControlsHelpers.CalculateInterpolatedValue( + minPercent, + Percent, + minPercent + 2.0, + 0.0, + gapWidth, + true) + : StorageControlsHelpers.CalculateInterpolatedValue( + minPercent, + Percent, + minPercent + 2.0, + 0.0, + _smallerHeight, + true); + + _valueColumn.MinWidth = interpolatedValueBarHeight; + _trackColumn.MinWidth = interpolatedTrackBarHeight; + + _valueBarBorder.Height = interpolatedValueBarHeight; + _trackBarBorder.Height = trackBarHeight; + + var calculatedValueWidth = (_valueColumn.MaxWidth / 100) * valuePercent; + + _valueColumn.Width = new(calculatedValueWidth); + _gapColumn.Width = new(interpolatedGapWidth); + _trackColumn.Width = new(1, GridUnitType.Star); + } + else if (valuePercent >= maxPercent - 1.0 && valuePercent < maxPercent) // Between 98% and 100% + { + var interpolatedValueBarHeight = StorageControlsHelpers.CalculateInterpolatedValue( + maxPercent - 2.0, + Percent, + maxPercent, + valueBarHeight, + 0.0, + true); + + var interpolatedTrackBarHeight = StorageControlsHelpers.CalculateInterpolatedValue( + maxPercent - 2.0, + Percent, + maxPercent, + trackBarHeight, + 0.0, + true); + + var interpolatedGapWidth = valueLarger + ? StorageControlsHelpers.CalculateInterpolatedValue( + maxPercent - 2.0, + Percent, + maxPercent, + 0.0, + _smallerHeight, + true) + : StorageControlsHelpers.CalculateInterpolatedValue( + maxPercent - 2.0, + Percent, + maxPercent, + 0.0, + gapWidth, + true); + + _valueColumn.MinWidth = interpolatedValueBarHeight; + _trackColumn.MinWidth = interpolatedTrackBarHeight; + + var calculatedValueWidth = (_valueColumn.MaxWidth / 100) * valuePercent; + + _valueColumn.Width = new(calculatedValueWidth); + _trackColumn.Width = new(1, GridUnitType.Star); + _gapColumn.Width = new(interpolatedGapWidth); + + _valueBarBorder.Height = valueBarHeight; + _trackBarBorder.Height = interpolatedTrackBarHeight; + } + else // Between 2% and 98% + { + _valueColumn.MinWidth = valueBarHeight; + _trackColumn.MinWidth = trackBarHeight; + + double calculatedValueWidth = (_valueColumn.MaxWidth / 100) * valuePercent; + + _valueColumn.Width = new(calculatedValueWidth); + _trackColumn.Width = new(1, GridUnitType.Star); + + var interpolatedGapWidth = valueLarger + ? StorageControlsHelpers.CalculateInterpolatedValue( + minPercent + 2.0, + Percent, + maxPercent - 2.0, + gapWidth, + _smallerHeight, + true) + : StorageControlsHelpers.CalculateInterpolatedValue( + minPercent + 2.0, + Percent, + maxPercent - 2.0, + _smallerHeight, + gapWidth, + true); + + _gapColumn.Width = new(interpolatedGapWidth); + + _valueBarBorder.Height = valueBarHeight; + _trackBarBorder.Height = trackBarHeight; + } + } + } + } + } + + private void UpdateContainer(Size newSize) + { + double containerWidth = newSize.Width - (Padding.Left + Padding.Right); + double containerHeight = newSize.Height - (Padding.Top + Padding.Bottom); + + _containerSize = new(containerWidth, containerHeight); + } + + private void UpdateVisualState() + { + VisualStateManager.GoToState( + this, + IsEnabled + ? Percent >= PercentCritical + ? TemplateVisualStateName_Critical + : Percent >= PercentCaution + ? TemplateVisualStateName_Caution + : TemplateVisualStateName_Safe + : TemplateVisualStateName_Disabled, + true); + } + + // Event methods + + private void StorageBar_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) + { + UpdateControl(); + } + + private void StorageBar_SizeChanged(object sender, SizeChangedEventArgs e) + { + Size minSize; + + if (DesiredSize.Width < MinWidth || DesiredSize.Height < MinHeight || + e.NewSize.Width < MinWidth || e.NewSize.Height < MinHeight) + { + Width = MinWidth; + Height = MinHeight; + + minSize = new Size(MinWidth, MinHeight); + } + else + { + minSize = e.NewSize; + } + + UpdateContainer(minSize); + UpdateControl(); + } + + private void StorageBar_Unloaded(object sender, RoutedEventArgs e) + { + SizeChanged -= StorageBar_SizeChanged; + Unloaded -= StorageBar_Unloaded; + IsEnabledChanged -= StorageBar_IsEnabledChanged; + } + } +} diff --git a/src/Files.App.Controls/Storage/StorageBar/StorageBar.xaml b/src/Files.App.Controls/Storage/StorageBar/StorageBar.xaml new file mode 100644 index 000000000000..e34925b52793 --- /dev/null +++ b/src/Files.App.Controls/Storage/StorageBar/StorageBar.xaml @@ -0,0 +1,180 @@ + + + + + + + + 6 + 2 + + 4,4,4,4 + + + + + + + + + + + + + + + + + + 6 + 2 + + 4,4,4,4 + + + + + + + + + + + + + + + + + + 6 + 2 + + 4,4,4,4 + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App.Controls/Storage/StorageControlsHelpers.cs b/src/Files.App.Controls/Storage/StorageControlsHelpers.cs new file mode 100644 index 000000000000..96c62f0fb0d7 --- /dev/null +++ b/src/Files.App.Controls/Storage/StorageControlsHelpers.cs @@ -0,0 +1,262 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + /// + /// Helpers for and . + /// + public static partial class StorageControlsHelpers + { + /// + /// Calculates the modulus of a number with respect to a divider. + /// The result is always positive or zero, regardless of the input values. + /// + /// The input number. + /// The divider (non-zero). + /// The positive modulus result. + public static double CalculateModulus(double number, double divider) + { + // Calculate the modulus + var result = number % divider; + + // Ensure the result is positive or zero + result = result < 0 ? result + divider : result; + + return result; + } + + /// + /// Calculates an interpolated thickness value based on the provided parameters. + /// + /// The starting value for interpolation. + /// The current value to interpolate. + /// The ending value for interpolation. + /// The starting thickness value. + /// The ending thickness value. + /// Indicates whether to apply an easing function. + /// The interpolated thickness value. + public static double GetThicknessTransition(double startValue, double value, double endValue, double startThickness, double endThickness, bool useEasing) + { + // Ensure that value is within the range [startValue, endValue] + value = Math.Max(startValue, Math.Min(endValue, value)); + + // Calculate the interpolation factor (t) between 0 and 1 + var t = (value - startValue) / (endValue - startValue); + + double interpolatedThickness; + + if (useEasing) + { + // Apply an easing function (e.g., quadratic ease-in-out) + var easedT = EaseOutCubic(t); + + // Interpolate the thickness + interpolatedThickness = startThickness + easedT * (endThickness - startThickness); + } + else + { + // Interpolate the thickness + interpolatedThickness = startThickness + t * (endThickness - startThickness); + } + + return interpolatedThickness; + } + + /// + /// Calculates an interpolated angle based on the provided parameters. + /// + /// The starting value for interpolation. + /// The current value to interpolate. + /// The ending value for interpolation. + /// The starting angle value. + /// The ending angle value. + /// The angle corresponding to the current value. + /// Indicates whether to apply an easing function. + /// The interpolated angle value. + public static double GetAdjustedAngle(double startValue, double value, double endValue, double startAngle, double endAngle, double valueAngle, bool useEasing) + { + // Ensure that value is within the range [startValue, endValue] + value = Math.Max(startValue, Math.Min(endValue, value)); + + // Calculate the interpolation factor (t) between 0 and 1 + var t = (value - startValue) / (endValue - startValue); + + double interpolatedAngle; + + if (useEasing) + { + // Apply an easing function + var easedT = StorageControlsHelpers.EaseOutCubic(t); + + // Interpolate the angle + interpolatedAngle = startAngle + easedT * (endAngle - startAngle); + } + else + { + // Interpolate the angle + interpolatedAngle = startAngle + t * (endAngle - startAngle); + } + + return interpolatedAngle; + } + + /// + /// Converts a value within a specified range to a percentage. + /// + /// The value to convert. + /// The minimum value of the input range. + /// The maximum value of the input range. + /// The percentage value (between 0 and 100). + public static double DoubleToPercentage(double value, double minValue, double maxValue) + { + // Ensure value is within the specified range + if (value < minValue) + { + return 0.0; // Below the range + } + else if (value > maxValue) + { + return 100.0; // Above the range + } + else + { + // Calculate the normalized value + var normalizedValue = (value - minValue) / (maxValue - minValue); + + // Convert to percentage + var percentage = normalizedValue * 100.0; + + double roundedPercentage = Math.Round(percentage, 2, MidpointRounding.ToEven); + return roundedPercentage; + } + } + + /// + /// Converts a percentage within a specified range to a value. + /// + /// The percentage to convert. + /// The minimum value of the input range. + /// The maximum value of the input range. + /// The percentage value (between 0 and 100). + public static double PercentageToValue(double percentage, double minValue, double maxValue) + { + double convertedValue = percentage * (maxValue - minValue) / 100.0; + + // Ensure the converted value stays within the specified range + if (convertedValue < minValue) + convertedValue = minValue; + else if (convertedValue > maxValue) + convertedValue = maxValue; + + return convertedValue; + } + + /// + /// Calculates the total angle needed to accommodate a gap between two strokes around a circle. + /// + /// The Thickness radius to measure. + /// The radius of the rings. + /// The gap angle (sum of angles for the larger and smaller strokes). + public static double GapThicknessToAngle(double radius, double thickness) + { + if (radius > 0 && thickness > 0) + { + // Calculate the maximum number of circles + double n = Math.PI * (radius / thickness); + + // Calculate the angle between each small circle + double angle = 360.0 / n; + + return angle; + } + + return 0; + } + + /// + /// Calculates an adjusted angle using linear interpolation (lerp) between the start and end angles. + /// + /// The initial angle. + /// The final angle. + /// A value between 0 and 1 representing the interpolation factor. + /// The adjusted angle based on linear interpolation. + public static double GetInterpolatedAngle(double startAngle, double endAngle, double valueAngle) + { + // Linear interpolation formula (lerp): GetInterpolatedAngle = (startAngle + valueAngle) * (endAngle - startAngle) + return (startAngle + valueAngle) * (endAngle - startAngle); + } + + /// + /// Checks True if the Angle range is a Full Circle + /// + /// + /// + /// + public static bool IsFullCircle(double MinAngle, double MaxAngle) + { + // Calculate the absolute difference between angles + double angleDifference = Math.Abs(MaxAngle - MinAngle); + + // Check if the angle difference is equal to 360 degrees + //return angleDifference == 360; + + // Changed to this as suggested by Marcel + return Math.Abs(angleDifference - 360) < Double.Epsilon; + } + + /// + /// Calculates an interpolated value based on the provided parameters. + /// + /// The starting value for interpolation. + /// The current value to interpolate. + /// The ending value for interpolation. + /// The starting Output value. + /// The ending Output value. + /// Indicates whether to apply an easing function. + /// The interpolated thickness value. + public static double CalculateInterpolatedValue(double startValue, double value, double endValue, double startOutput, double endOutput, bool useEasing) + { + // Ensure that value is within the range [startValue, endValue] + value = Math.Max(startValue, Math.Min(endValue, value)); + + // Calculate the interpolation factor (t) between 0 and 1 + var t = (value - startValue) / (endValue - startValue); + + double interpolatedOutput; + + if (useEasing) + { + // Apply an easing function (e.g., quadratic ease-in-out) + //var easedT = EaseInOutFunction(t); + var easedT = EaseOutCubic(t); + + // Interpolate the thickness + interpolatedOutput = startOutput + easedT * (endOutput - startOutput); + } + else + { + // Interpolate the thickness + interpolatedOutput = startOutput + t * (endOutput - startOutput); + } + + return interpolatedOutput; + } + + /// + /// Example quadratic ease-in-out function + /// + public static double EasingInOutFunction(double t) + { + return t < 0.5 ? 2 * t * t : 1 - Math.Pow(-2 * t + 2, 2) / 2; + } + + /// + /// Example ease-out cubic function + /// + public static double EaseOutCubic(double t) + { + return 1.0 - Math.Pow(1.0 - t, 3.0); + } + } +} diff --git a/src/Files.App.Controls/Storage/StorageRing/StorageRing.Properties.cs b/src/Files.App.Controls/Storage/StorageRing/StorageRing.Properties.cs new file mode 100644 index 000000000000..fb7891115028 --- /dev/null +++ b/src/Files.App.Controls/Storage/StorageRing/StorageRing.Properties.cs @@ -0,0 +1,133 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using CommunityToolkit.WinUI; + +namespace Files.App.Controls +{ + public partial class StorageRing + { + [GeneratedDependencyProperty(DefaultValue = 0d)] + public partial double ValueRingThickness { get; set; } + + [GeneratedDependencyProperty(DefaultValue = 0d)] + public partial double TrackRingThickness { get; set; } + + [GeneratedDependencyProperty(DefaultValue = 0.0d)] + public partial double MinAngle { get; set; } + + [GeneratedDependencyProperty(DefaultValue = 360.0d)] + public partial double MaxAngle { get; set; } + + [GeneratedDependencyProperty(DefaultValue = 0.0d)] + public partial double StartAngle { get; set; } + + [GeneratedDependencyProperty(DefaultValue = 0.0d)] + public partial double Percent { get; set; } + + [GeneratedDependencyProperty(DefaultValue = 75.01d)] + public partial double PercentCaution { get; set; } + + [GeneratedDependencyProperty(DefaultValue = 90.01d)] + public partial double PercentCritical { get; set; } + + [GeneratedDependencyProperty(DefaultValue = 0.0d)] + public partial double ValueAngle { get; set; } + + [GeneratedDependencyProperty(DefaultValue = 16.0d)] + public partial double AdjustedSize { get; set; } + + partial void OnValueRingThicknessChanged(double newValue) + { + UpdateRingThickness(newValue, false); + UpdateRings(); + } + + partial void OnTrackRingThicknessChanged(double newValue) + { + UpdateRingThickness(newValue, true); + UpdateRings(); + } + + partial void OnMinAngleChanged(double newValue) + { + UpdateValues(Value, _oldValue, false, -1.0); + UpdateNormalizedAngles(newValue, MaxAngle); + UpdateRings(); + } + + partial void OnMaxAngleChanged(double newValue) + { + UpdateValues(Value, _oldValue, false, -1.0); + UpdateNormalizedAngles(MinAngle, newValue); + UpdateRings(); + } + + partial void OnStartAngleChanged(double newValue) + { + UpdateValues(Value, _oldValue, false, -1.0); + UpdateNormalizedAngles(MinAngle, newValue); + ValidateStartAngle(newValue); + UpdateRings(); + } + + partial void OnPercentChanged(double newValue) + { + return; //Read-only + + //Helpers.DoubleToPercentage(Value, Minimum, Maximum); + + //double adjustedPercentage; + + //if (newValue <= 0.0) + // adjustedPercentage = 0.0; + //else if (newValue <= 100.0) + // adjustedPercentage = 100.0; + //else + // adjustedPercentage = newValue; + + //UpdateValues(Value, _oldValue, true, adjustedPercentage); + //UpdateVisualState(); + //UpdateRings(); + } + + partial void OnPercentCautionChanged(double newValue) + { + UpdateValues(Value, _oldValue, false, -1.0); + UpdateVisualState(); + UpdateRings(); + } + + partial void OnPercentCriticalChanged(double newValue) + { + UpdateValues(Value, _oldValue, false, -1.0); + UpdateVisualState(); + UpdateRings(); + } + + /// + protected override void OnValueChanged(double oldValue, double newValue) + { + base.OnValueChanged(oldValue, newValue); + + UpdateValues(newValue, oldValue, false, -1.0); + UpdateRings(); + } + + /// + protected override void OnMinimumChanged(double oldMinimum, double newMinimum) + { + base.OnMinimumChanged(oldMinimum, newMinimum); + + UpdateRings(); + } + + /// + protected override void OnMaximumChanged(double oldMaximum, double newMaximum) + { + base.OnMaximumChanged(oldMaximum, newMaximum); + + UpdateRings(); + } + } +} diff --git a/src/Files.App.Controls/Storage/StorageRing/StorageRing.cs b/src/Files.App.Controls/Storage/StorageRing/StorageRing.cs new file mode 100644 index 000000000000..725162cbf856 --- /dev/null +++ b/src/Files.App.Controls/Storage/StorageRing/StorageRing.cs @@ -0,0 +1,591 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Media; +using Windows.Foundation; + +namespace Files.App.Controls +{ + // TemplateParts + [TemplatePart(Name = TemplatePartName_Container, Type = typeof(Grid))] + [TemplatePart(Name = TemplatePartName_ValueRingShape, Type = typeof(RingShape))] + [TemplatePart(Name = TemplatePartName_TrackRingShape, Type = typeof(RingShape))] + // VisualStates + [TemplateVisualState(GroupName = TemplateVisualStateGroupName_ControlStates, Name = TemplateVisualStateName_Safe)] + [TemplateVisualState(GroupName = TemplateVisualStateGroupName_ControlStates, Name = TemplateVisualStateName_Caution)] + [TemplateVisualState(GroupName = TemplateVisualStateGroupName_ControlStates, Name = TemplateVisualStateName_Critical)] + [TemplateVisualState(GroupName = TemplateVisualStateGroupName_ControlStates, Name = TemplateVisualStateName_Disabled)] + /// + /// Represents percentage bar islands. + /// + public partial class StorageRing : RangeBase + { + private const string TemplatePartName_Container = "PART_Container"; + private const string TemplatePartName_ValueRingShape = "PART_ValueRingShape"; + private const string TemplatePartName_TrackRingShape = "PART_TrackRingShape"; + + private const string TemplateVisualStateGroupName_ControlStates = "ControlStates"; + private const string TemplateVisualStateName_Safe = "Safe"; + private const string TemplateVisualStateName_Caution = "Caution"; + private const string TemplateVisualStateName_Critical = "Critical"; + private const string TemplateVisualStateName_Disabled = "Disabled"; + + private const double DegreesToRadians = Math.PI / 180; + private const double minSize = 8; + + // Fields + + private Grid _containerGrid = null!; + private RingShape _valueRingShape = null!; + private RingShape _trackRingShape = null!; + + private double _containerSize; // Size of the inner container after padding + private double _containerCenter; // Center X and Y value of the inner container + private double _sharedRadius; // Radius to be shared by both rings (smaller of the two) + + private double _oldValue; // Stores the previous Value + private double _oldValueAngle; // Stored the old ValueAngle + + private double _valueRingThickness; // The stored value ring thickness + private double _trackRingThickness; // The stored track ring thickness + private ThicknessCheck _thicknessCheck; // Determines how the two ring thicknesses compare + private double _largerThickness; // The larger of the two ring thicknesses + private double _smallerThickness; // The smaller of the two ring thicknesses + + private RectangleGeometry? _clipRect; // Clipping RectangleGeometry for the canvas + + private double _normalizedMinAngle; // Stores the normalized Minimum Angle + private double _normalizedMaxAngle; // Stores the normalized Maximum Angle + private double _gapAngle; // Stores the angle to be used to separate Value and Track rings + private double _validStartAngle; // The validated StartAngle + + // Constructor + + /// Initializes an instance of class. + public StorageRing() + { + DefaultStyleKey = typeof(StorageRing); + } + + // Methods + + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + // Retrieve references to visual elements + _containerGrid = GetTemplateChild(TemplatePartName_Container) as Grid + ?? throw new MissingFieldException($"Could not find {TemplatePartName_Container} in the given {nameof(StorageRing)}'s style."); + _valueRingShape = GetTemplateChild(TemplatePartName_ValueRingShape) as RingShape + ?? throw new MissingFieldException($"Could not find {TemplatePartName_ValueRingShape} in the given {nameof(StorageRing)}'s style."); + _trackRingShape = GetTemplateChild(TemplatePartName_TrackRingShape) as RingShape + ?? throw new MissingFieldException($"Could not find {TemplatePartName_TrackRingShape} in the given {nameof(StorageRing)}'s style."); + + // Initialize the ring + UpdateValues(Value, 0.0, false, -1.0); + UpdateRings(); + + // Hook control events + SizeChanged += StorageRing_SizeChanged; + IsEnabledChanged += StorageRing_IsEnabledChanged; + Unloaded += StorageRing_Unloaded; + } + + private void UpdateRings() + { + if (_valueRingShape is null || _trackRingShape is null) + return; + + // Update every detail of the control + UpdateContainerCenterAndSizes(); + UpdateNormalizedAngles(MinAngle, MaxAngle); + UpdateRingSizes(_valueRingShape, _trackRingShape); + UpdateRadii(_sharedRadius, false); + UpdateRadii(_sharedRadius, true); + UpdateGapAngle(); + UpdateRingLayouts(_valueRingShape, _trackRingShape); + UpdateRingAngles(_valueRingShape, _trackRingShape); + UpdateRingStrokes(_valueRingShape, _trackRingShape); + UpdateRingThickness(ValueRingThickness, false); + UpdateRingThickness(TrackRingThickness, true); + UpdateVisualState(); + } + + #region Update methods + private void UpdateContainerCenterAndSizes() + { + var borderThickness = BorderThickness; + + var borderWidth = borderThickness.Left + borderThickness.Right; + var borderHeight = borderThickness.Top + borderThickness.Bottom; + + // Set Container Size + double correctedWidth = Width - (borderWidth * 2) - (Padding.Left + Padding.Right); + double correctedHeight = Height - (borderHeight * 2) - (Padding.Top + Padding.Bottom); + double check = Math.Min(correctedWidth, correctedHeight); + _containerSize = check < minSize ? minSize : check; + AdjustedSize = _containerSize; + + // Set Container Center + _containerCenter = (AdjustedSize / 2); + + // Set Clipping Rectangle + RectangleGeometry rectGeo = new() { Rect = new(0 - borderWidth, 0 - borderHeight, AdjustedSize + borderWidth, AdjustedSize + borderHeight) }; + _clipRect = rectGeo; + + // Get Container + var container = _containerGrid; + + // If the Container is not null. + if (container != null) + { + // Set the _container width and height to the adjusted size (AdjustedSize). + container.Width = AdjustedSize; + container.Height = AdjustedSize; + container.Clip = _clipRect; + } + } + + private void UpdateNormalizedAngles(double minAngle, double maxAngle) + { + var result = StorageControlsHelpers.CalculateModulus(minAngle, 360); + + if (result >= 180) + result -= 360; + + _normalizedMinAngle = result; + + result = StorageControlsHelpers.CalculateModulus(maxAngle, 360); + + if (result < 180) + result += 360; + + if (result > _normalizedMinAngle + 360) + result -= 360; + + _normalizedMaxAngle = result; + } + + private void UpdateRingSizes(RingShape valueRingShape, RingShape trackRingShape) + { + if (valueRingShape is null || trackRingShape is null) + return; + + // Set sizes for the rings as needed + if (_thicknessCheck is ThicknessCheck.Value) + { + valueRingShape.Width = _containerSize; + valueRingShape.Height = _containerSize; + + trackRingShape.Width = _containerSize - (_largerThickness / 2); + trackRingShape.Height = _containerSize - (_largerThickness / 2); + } + else if (_thicknessCheck is ThicknessCheck.Track) + { + valueRingShape.Width = _containerSize - (_largerThickness / 2); + valueRingShape.Height = _containerSize - (_largerThickness / 2); + + trackRingShape.Width = _containerSize; + trackRingShape.Height = _containerSize; + } + else // ThicknessCheck == Equal + { + valueRingShape.Width = _containerSize; + valueRingShape.Height = _containerSize; + + trackRingShape.Width = _containerSize; + trackRingShape.Height = _containerSize; + } + + valueRingShape.UpdateLayout(); + trackRingShape.UpdateLayout(); + } + + private void UpdateRadii(double newRadius, bool isTrack) + { + double valueRingThickness = _valueRingThickness; + double trackRingThickness = _trackRingThickness; + + // Limit the Thickness values to no more than 1/5 of the container size + if (isTrack) + trackRingThickness = newRadius > (AdjustedSize / 5) ? (AdjustedSize / 5) : newRadius; + else + valueRingThickness = newRadius > (AdjustedSize / 5) ? (AdjustedSize / 5) : newRadius; + + // If both Rings have Equal thickness, use 0; otherwise, use the larger thickness to adjust the size + double check = (AdjustedSize / 2) - (_thicknessCheck is ThicknessCheck.Equal ? 0 : _largerThickness / 2); + double minSize = 4; + + _sharedRadius = check <= minSize ? minSize : check; + } + + private void UpdateGapAngle() + { + double angle = StorageControlsHelpers.GapThicknessToAngle(_sharedRadius, (_largerThickness * 0.75)); + _gapAngle = angle; + } + + private void UpdateValues(double newValue, double oldValue, bool percentChanged, double newPercent) + { + UpdateNormalizedAngles(MinAngle, MaxAngle); + + double adjustedValue; + + if (percentChanged) + { + var percentToValue = StorageControlsHelpers.PercentageToValue(newPercent, Minimum, Maximum); + adjustedValue = percentToValue; + } + else + { + adjustedValue = newValue; + } + + ValueAngle = DoubleToAngle(adjustedValue, Minimum, Maximum, _normalizedMinAngle, _normalizedMaxAngle); + Percent = StorageControlsHelpers.DoubleToPercentage(adjustedValue, Minimum, Maximum); + + _oldValue = oldValue; + _oldValueAngle = DoubleToAngle(oldValue, Minimum, Maximum, _normalizedMinAngle, _normalizedMaxAngle); + } + + private void UpdateRingLayouts(RingShape valueRingShape, RingShape trackRingShape) + { + if (valueRingShape is null || trackRingShape is null) + return; + + valueRingShape.RadiusWidth = _sharedRadius; + valueRingShape.RadiusHeight = _sharedRadius; + + trackRingShape.RadiusWidth = _sharedRadius; + trackRingShape.RadiusHeight = _sharedRadius; + + // Apply Width and Heights + valueRingShape.Width = AdjustedSize; + valueRingShape.Height = AdjustedSize; + + trackRingShape.Width = AdjustedSize; + trackRingShape.Height = AdjustedSize; + } + + private void UpdateRingAngles(RingShape valueRingShape, RingShape trackRingShape) + { + if (valueRingShape is null || trackRingShape is null) + return; + + double valueStartAngle = _normalizedMinAngle; + double valueEndAngle; + double trackStartAngle = _normalizedMaxAngle; + double trackEndAngle; + + // + // We get percentage values to use for manipulating how we draw the rings. + var minPercent = StorageControlsHelpers.DoubleToPercentage(Minimum, Minimum, Maximum); + var maxPercent = StorageControlsHelpers.DoubleToPercentage(Maximum, Minimum, Maximum); + var percent = Percent; + + // + // Percent is below or at its Minimum + if (percent <= minPercent) + { + valueEndAngle = _normalizedMinAngle; + + trackStartAngle = _normalizedMaxAngle - 0.01; + trackEndAngle = _normalizedMinAngle; + } + // + // Percent is between it's Minimum and its Minimum + 2 (between 0% and 2%) + else if (percent > minPercent && percent < minPercent + 2.0) + { + valueEndAngle = ValueAngle; + + double interpolatedStartTo; + double interpolatedEndTo; + + // + // We need to interpolate the track start and end angles between pRing.Minimum and pRing.Minimum + 0.75 + interpolatedStartTo = StorageControlsHelpers.GetAdjustedAngle( + minPercent, + percent, + minPercent + 2.0, + _normalizedMinAngle, + _normalizedMinAngle + _gapAngle, + ValueAngle, + true); + + if (StorageControlsHelpers.IsFullCircle(_normalizedMinAngle, _normalizedMaxAngle) == true) + { + interpolatedEndTo = StorageControlsHelpers.GetAdjustedAngle( + minPercent, + percent, + minPercent + 2.0, + _normalizedMaxAngle, + _normalizedMaxAngle - (_gapAngle + ValueAngle), + ValueAngle, + true); + } + else + { + interpolatedEndTo = _normalizedMaxAngle; + } + + trackStartAngle = interpolatedEndTo; + trackEndAngle = interpolatedStartTo; + } + // + // Percent is at or above its Maximum value + else if (percent >= maxPercent) + { + valueEndAngle = _normalizedMaxAngle; + + trackStartAngle = _normalizedMaxAngle; + trackEndAngle = _normalizedMinAngle; + } + // + // Any value between the Minimum and the Maximum value + else + { + valueEndAngle = ValueAngle; + + if (StorageControlsHelpers.IsFullCircle(MinAngle, MaxAngle) == true) + { + trackStartAngle = _normalizedMaxAngle - _gapAngle; + + // + // When the trackRing's EndAngle meets or exceeds its adjusted StartAngle + if (ValueAngle > (_normalizedMaxAngle - (_gapAngle * 2))) + { + trackEndAngle = _normalizedMaxAngle - (_gapAngle - 0.0001); + } + else + { + // We take the MaxAngle - the GapAngle, then minus the ValueAngle from it + trackEndAngle = (_normalizedMinAngle + _gapAngle) - (_normalizedMinAngle - ValueAngle); + } + } + else + { + trackStartAngle = _normalizedMaxAngle; + + // + // When the trackRing's EndAngle meets or exceeds its adjusted StartAngle + if (ValueAngle > (_normalizedMaxAngle - (_gapAngle / 20))) + { + trackEndAngle = (_normalizedMaxAngle - 0.0001); + } + else + { + // We take the MaxAngle - the GapAngle, then minus the ValueAngle from it + trackEndAngle = (_normalizedMinAngle + (_gapAngle - (_normalizedMinAngle - ValueAngle))); + } + } + } + + valueRingShape.StartAngle = valueStartAngle; + trackRingShape.StartAngle = trackStartAngle; + + valueRingShape.EndAngle = valueEndAngle; + trackRingShape.EndAngle = trackEndAngle; + } + + private void UpdateRingStrokes(RingShape valueRingShape, RingShape trackRingShape) + { + if (valueRingShape is null || trackRingShape is null) + return; + + var normalizedMinAngle = _normalizedMinAngle; + var normalizedMaxAngle = _normalizedMaxAngle; + + // We get percentage values to use for manipulating how we draw the rings. + var minPercent = StorageControlsHelpers.DoubleToPercentage(Minimum, Minimum, Maximum); + var maxPercent = StorageControlsHelpers.DoubleToPercentage(Maximum, Minimum, Maximum); + var percent = Percent; + + // Percent is below or at its Minimum + if (percent <= minPercent) + { + valueRingShape.StrokeThickness = 0; + trackRingShape.StrokeThickness = _trackRingThickness; + } + // Percent is between it's Minimum and its Minimum + 2.0 (between 0% and 2%) + else if (percent > minPercent && percent < minPercent + 2.0) + { + valueRingShape.StrokeThickness = StorageControlsHelpers.GetThicknessTransition( + minPercent, + percent, + minPercent + 2.0, + 0.0, + _valueRingThickness, + true); + + trackRingShape.StrokeThickness = _trackRingThickness; + } + // + // Percent is at or above its Maximum value + else if (percent >= maxPercent) + { + valueRingShape.StrokeThickness = _valueRingThickness; + trackRingShape.StrokeThickness = 0; + } + // + // Any percent value between the Minimum + 2 and the Maximum value + else + { + valueRingShape.StrokeThickness = ValueRingThickness; + + if (StorageControlsHelpers.IsFullCircle(normalizedMinAngle, normalizedMaxAngle) == true) + { + if (ValueAngle > (normalizedMaxAngle + 1.0) - (_gapAngle * 2)) + { + valueRingShape.StrokeThickness = _valueRingThickness; + + trackRingShape.StrokeThickness = StorageControlsHelpers.GetThicknessTransition( + (normalizedMaxAngle + 0.1) - (_gapAngle * 2), + ValueAngle, (normalizedMaxAngle) - (_gapAngle), + _trackRingThickness, + 0.0, + true); + } + else + { + valueRingShape.StrokeThickness = _valueRingThickness; + trackRingShape.StrokeThickness = _trackRingThickness; + } + } + else + { + if (ValueAngle > (normalizedMaxAngle - _gapAngle)) + { + valueRingShape.StrokeThickness = _valueRingThickness; + trackRingShape.StrokeThickness = StorageControlsHelpers.GetThicknessTransition( + (normalizedMaxAngle + 0.1) - (_gapAngle / 2), + ValueAngle, (normalizedMaxAngle) - (_gapAngle / 2), + _trackRingThickness, + 0.0, + true); + } + else + { + valueRingShape.StrokeThickness = _valueRingThickness; + trackRingShape.StrokeThickness = _trackRingThickness; + } + } + } + ; + } + + private void UpdateRingThickness(double newThickness, bool isTrack) + { + if (isTrack) + _trackRingThickness = newThickness; + else + _valueRingThickness = newThickness; + + if (_valueRingThickness > _trackRingThickness) + _thicknessCheck = ThicknessCheck.Value; + else if (_valueRingThickness < _trackRingThickness) + _thicknessCheck = ThicknessCheck.Track; + else + _thicknessCheck = ThicknessCheck.Equal; + + if (_thicknessCheck is ThicknessCheck.Value) + { + _largerThickness = _valueRingThickness; + _smallerThickness = _trackRingThickness; + } + else if (_thicknessCheck is ThicknessCheck.Track) + { + _largerThickness = _trackRingThickness; + _smallerThickness = _valueRingThickness; + } + else // ThicknessCheck == Equal + { + _largerThickness = _valueRingThickness; + _smallerThickness = _valueRingThickness; + } + } + + private void UpdateVisualState() + { + VisualStateManager.GoToState( + this, + IsEnabled + ? Percent >= PercentCritical + ? TemplateVisualStateName_Critical + : Percent >= PercentCaution + ? TemplateVisualStateName_Caution + : TemplateVisualStateName_Safe + : TemplateVisualStateName_Disabled, + true); + } + + private void ValidateStartAngle(double startAngle) + { + _validStartAngle = startAngle switch + { + _ when startAngle >= _normalizedMaxAngle => _normalizedMaxAngle, + _ when startAngle <= _normalizedMinAngle => _normalizedMinAngle, + _ => startAngle, + }; + } + #endregion + + private double DoubleToAngle(double value, double minValue, double maxValue, double minAngle, double maxAngle) + { + // Converts a value within a specified range to an angle within another specified range. + + // If value is below the Minimum set + if (value < minValue) + return minAngle; + + // If value is above the Maximum set + if (value > maxValue) + return maxAngle; + + // Calculate the normalized value + double normalizedValue = (value - minValue) / (maxValue - minValue); + + // Determine the angle range + double angleRange = MaxAngle - MinAngle; + + // Calculate the actual angle + double angle = MinAngle + (normalizedValue * angleRange); + + return angle; + } + + // Event methods + + private void StorageRing_SizeChanged(object sender, SizeChangedEventArgs e) + { + Size minSize; + + if (DesiredSize.Width < MinWidth || DesiredSize.Height < MinHeight || + e.NewSize.Width < MinWidth || e.NewSize.Height < MinHeight) + { + Width = MinWidth; + Height = MinHeight; + + minSize = new Size(MinWidth, MinHeight); + } + else + { + minSize = e.NewSize; + } + + UpdateContainerCenterAndSizes(); + UpdateRings(); + } + + private void StorageRing_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) + { + UpdateVisualState(); + } + + private void StorageRing_Unloaded(object sender, RoutedEventArgs e) + { + SizeChanged -= StorageRing_SizeChanged; + IsEnabledChanged -= StorageRing_IsEnabledChanged; + Unloaded -= StorageRing_Unloaded; + } + } +} diff --git a/src/Files.App.Controls/Storage/StorageRing/StorageRing.xaml b/src/Files.App.Controls/Storage/StorageRing/StorageRing.xaml new file mode 100644 index 000000000000..c297e701d51b --- /dev/null +++ b/src/Files.App.Controls/Storage/StorageRing/StorageRing.xaml @@ -0,0 +1,201 @@ + + + + + + 3 + 1 + + 0 + 0 + + Round + Round + Round + Round + + + + + + + + + + + + + + + + 4 + 2 + + 0 + 0 + + Round + Round + Round + Round + + + + + + + + + + + + + + + + 3 + 1 + + 0 + 0 + + Round + Round + Round + Round + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App.Controls/ThemedIcon/Data/ThemedIconColorType.cs b/src/Files.App.Controls/ThemedIcon/Data/ThemedIconColorType.cs new file mode 100644 index 000000000000..99f394efc540 --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/Data/ThemedIconColorType.cs @@ -0,0 +1,50 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + /// + /// Defines the IconColorTypes for which sets the visual state + /// to use the correct brush values which match system signal colors. + /// + public enum ThemedIconColorType + { + None, + + /// + /// Icon color type of is Normal. Default Value. + /// + Normal, + + /// + /// Icon color type of is Critical. + /// + Critical, + + /// + /// Icon color type of is Caution. + /// + Caution, + + /// + /// Icon color type of is Success. + /// + Success, + + /// + /// Icon color type of is Neutral. + /// + Neutral, + + /// + /// Icon color type of is Accent. + /// + Accent, + + /// + /// Icon color type of is Custom. Used in combination + /// with the IconColor and Foreground brushes. + /// + Custom + } +} diff --git a/src/Files.App.Controls/ThemedIcon/Data/ThemedIconLayerType.cs b/src/Files.App.Controls/ThemedIcon/Data/ThemedIconLayerType.cs new file mode 100644 index 000000000000..818ab2852b6d --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/Data/ThemedIconLayerType.cs @@ -0,0 +1,28 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + /// + /// Defines LayerTypes for . + /// + public enum ThemedIconLayerType + { + /// + /// The LayerType uses the Base color. Default state. + /// + Base, + /// + /// The LayerType uses the Alt color. + /// + Alt, + /// + /// The LayerType uses an Accented color. + /// + Accent, + /// + /// The LayerType uses a contrasting color for the Accented color. + /// + AccentContrast, + } +} diff --git a/src/Files.App.Controls/ThemedIcon/Data/ThemedIconLayers.cs b/src/Files.App.Controls/ThemedIcon/Data/ThemedIconLayers.cs new file mode 100644 index 000000000000..7a9d4f4693a0 --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/Data/ThemedIconLayers.cs @@ -0,0 +1,12 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + /// + /// A collection of Layers for the ThemedIcon's Layered IconType + /// + public sealed partial class ThemedIconLayers : List + { + } +} diff --git a/src/Files.App.Controls/ThemedIcon/Data/ThemedIconToggleBehaviors.cs b/src/Files.App.Controls/ThemedIcon/Data/ThemedIconToggleBehaviors.cs new file mode 100644 index 000000000000..55c631a12369 --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/Data/ThemedIconToggleBehaviors.cs @@ -0,0 +1,26 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + /// + /// Defines IconTypes for . + /// + public enum ToggleBehaviors + { + /// + /// Auto enables the ThemedIcon to listen to owner control states. + /// + Auto, + + /// + /// On will always use the ThemedIcon's Toggle state + /// + On, + + /// + /// Off will not use the ThemedIcon's Toggle state + /// + Off, + } +} diff --git a/src/Files.App.Controls/ThemedIcon/Data/ThemedIconTypes.cs b/src/Files.App.Controls/ThemedIcon/Data/ThemedIconTypes.cs new file mode 100644 index 000000000000..ffda1e77e2ba --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/Data/ThemedIconTypes.cs @@ -0,0 +1,21 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + /// + /// Defines IconTypes for . + /// + public enum ThemedIconTypes + { + /// + /// Icon type of is Outline. + /// + Outline, + + /// + /// Icon type of is Layered. + /// + Layered, + } +} diff --git a/src/Files.App.Controls/ThemedIcon/Styles/Icons.Action.xaml b/src/Files.App.Controls/ThemedIcon/Styles/Icons.Action.xaml new file mode 100644 index 000000000000..300f6aad3e79 --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/Styles/Icons.Action.xaml @@ -0,0 +1,396 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App.Controls/ThemedIcon/Styles/Icons.Common.xaml b/src/Files.App.Controls/ThemedIcon/Styles/Icons.Common.xaml new file mode 100644 index 000000000000..9b04a322ba70 --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/Styles/Icons.Common.xaml @@ -0,0 +1,1448 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App.Controls/ThemedIcon/Styles/Icons.Favorite.xaml b/src/Files.App.Controls/ThemedIcon/Styles/Icons.Favorite.xaml new file mode 100644 index 000000000000..98c5141dea46 --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/Styles/Icons.Favorite.xaml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + diff --git a/src/Files.App.Controls/ThemedIcon/Styles/Icons.Git.xaml b/src/Files.App.Controls/ThemedIcon/Styles/Icons.Git.xaml new file mode 100644 index 000000000000..182117212f6e --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/Styles/Icons.Git.xaml @@ -0,0 +1,284 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App.Controls/ThemedIcon/Styles/Icons.Misc16.xaml b/src/Files.App.Controls/ThemedIcon/Styles/Icons.Misc16.xaml new file mode 100644 index 000000000000..214d848f2374 --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/Styles/Icons.Misc16.xaml @@ -0,0 +1,306 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App.Controls/ThemedIcon/Styles/Icons.New.xaml b/src/Files.App.Controls/ThemedIcon/Styles/Icons.New.xaml new file mode 100644 index 000000000000..ce2fa353d470 --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/Styles/Icons.New.xaml @@ -0,0 +1,257 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App.Controls/ThemedIcon/Styles/Icons.Open.xaml b/src/Files.App.Controls/ThemedIcon/Styles/Icons.Open.xaml new file mode 100644 index 000000000000..2a9c1a391eee --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/Styles/Icons.Open.xaml @@ -0,0 +1,344 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App.Controls/ThemedIcon/Styles/Icons.Panel.xaml b/src/Files.App.Controls/ThemedIcon/Styles/Icons.Panel.xaml new file mode 100644 index 000000000000..e114554e6f13 --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/Styles/Icons.Panel.xaml @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App.Controls/ThemedIcon/Styles/Icons.Properties.Dialog.xaml b/src/Files.App.Controls/ThemedIcon/Styles/Icons.Properties.Dialog.xaml new file mode 100644 index 000000000000..f774c2a199d0 --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/Styles/Icons.Properties.Dialog.xaml @@ -0,0 +1,322 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App.Controls/ThemedIcon/Styles/Icons.Settings.Sidebar.xaml b/src/Files.App.Controls/ThemedIcon/Styles/Icons.Settings.Sidebar.xaml new file mode 100644 index 000000000000..29d340a7671f --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/Styles/Icons.Settings.Sidebar.xaml @@ -0,0 +1,316 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App.Controls/ThemedIcon/Styles/Icons.Settings.xaml b/src/Files.App.Controls/ThemedIcon/Styles/Icons.Settings.xaml new file mode 100644 index 000000000000..c2591f72cbe0 --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/Styles/Icons.Settings.xaml @@ -0,0 +1,1014 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App.Controls/ThemedIcon/Styles/Icons.SizeLayout.xaml b/src/Files.App.Controls/ThemedIcon/Styles/Icons.SizeLayout.xaml new file mode 100644 index 000000000000..322b6447fe71 --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/Styles/Icons.SizeLayout.xaml @@ -0,0 +1,317 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App.Controls/ThemedIcon/Styles/Icons.SizeLayout28.xaml b/src/Files.App.Controls/ThemedIcon/Styles/Icons.SizeLayout28.xaml new file mode 100644 index 000000000000..5fdafd4dd663 --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/Styles/Icons.SizeLayout28.xaml @@ -0,0 +1,290 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App.Controls/ThemedIcon/Styles/Icons.Status.xaml b/src/Files.App.Controls/ThemedIcon/Styles/Icons.Status.xaml new file mode 100644 index 000000000000..15cbc1474dbd --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/Styles/Icons.Status.xaml @@ -0,0 +1,554 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App.Controls/ThemedIcon/Styles/Icons.TabPane.xaml b/src/Files.App.Controls/ThemedIcon/Styles/Icons.TabPane.xaml new file mode 100644 index 000000000000..3b5a5472c322 --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/Styles/Icons.TabPane.xaml @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App.Controls/ThemedIcon/ThemedIcon.Consts.cs b/src/Files.App.Controls/ThemedIcon/ThemedIcon.Consts.cs new file mode 100644 index 000000000000..3de66bb5294c --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/ThemedIcon.Consts.cs @@ -0,0 +1,67 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + // Template Parts + [TemplatePart(Name = FilledPathIconViewBox, Type = typeof(Viewbox))] + [TemplatePart(Name = OutlinePathIconViewBox, Type = typeof(Viewbox))] + [TemplatePart(Name = LayeredPathIconViewBox, Type = typeof(Viewbox))] + [TemplatePart(Name = LayeredPathCanvas, Type = typeof(Canvas))] + // Icon Type Visual States + [TemplateVisualState(Name = OutlineTypeStateName, GroupName = IconTypeStateGroupName)] + [TemplateVisualState(Name = LayeredTypeStateName, GroupName = IconTypeStateGroupName)] + [TemplateVisualState(Name = FilledTypeStateName, GroupName = IconTypeStateGroupName)] + // Icon Color Visual States + [TemplateVisualState(Name = NormalStateName, GroupName = IconColorStateGroupName)] + [TemplateVisualState(Name = CriticalStateName, GroupName = IconColorStateGroupName)] + [TemplateVisualState(Name = CautionStateName, GroupName = IconColorStateGroupName)] + [TemplateVisualState(Name = SuccessStateName, GroupName = IconColorStateGroupName)] + [TemplateVisualState(Name = NeutralStateName, GroupName = IconColorStateGroupName)] + [TemplateVisualState(Name = AccentStateName, GroupName = IconColorStateGroupName)] + [TemplateVisualState(Name = CustomColorStateName, GroupName = IconColorStateGroupName)] + [TemplateVisualState(Name = ToggleStateName, GroupName = IconColorStateGroupName)] + [TemplateVisualState(Name = DisabledColorStateName, GroupName = IconColorStateGroupName)] + [TemplateVisualState(Name = DisabledToggleColorStateName, GroupName = IconColorStateGroupName)] + // Icon IsEnabled Visual States + [TemplateVisualState(Name = EnabledStateName, GroupName = EnabledStateGroupName)] + [TemplateVisualState(Name = DisabledStateName, GroupName = EnabledStateGroupName)] + public partial class ThemedIcon + { + // Visual State Group Names + internal const string IconTypeStateGroupName = "IconTypeStates"; + internal const string IconColorStateGroupName = "IconColorStates"; + internal const string EnabledStateGroupName = "EnabledStates"; + + // "Icon Type" Visual State Names + internal const string OutlineTypeStateName = "Outline"; + internal const string FilledTypeStateName = "Filled"; + internal const string LayeredTypeStateName = "Layered"; + + // "Icon State" Visual State Names + internal const string NormalStateName = "Normal"; + internal const string CriticalStateName = "Critical"; + internal const string CautionStateName = "Caution"; + internal const string SuccessStateName = "Success"; + internal const string NeutralStateName = "Neutral"; + internal const string AccentStateName = "Accent"; + internal const string CustomColorStateName = "Custom"; + internal const string ToggleStateName = "Toggle"; + internal const string DisabledColorStateName = "DisabledColor"; + internal const string DisabledToggleColorStateName = "DisabledToggleColor"; + + // "Enabled" Visual State Names + internal const string EnabledStateName = "Enabled"; + internal const string DisabledStateName = "Disabled"; + + // ViewBox Controls + internal const string FilledPathIconViewBox = "PART_FilledIconViewBox"; + internal const string OutlinePathIconViewBox = "PART_OutlineIconViewBox"; + internal const string LayeredPathIconViewBox = "PART_LayeredIconViewBox"; + internal const string LayeredPathCanvas = "PART_LayerCanvas"; + + // Path Controls + internal const string FilledIconPath = "PART_FilledPath"; + internal const string OutlineIconPath = "PART_OutlinePath"; + } +} diff --git a/src/Files.App.Controls/ThemedIcon/ThemedIcon.Owner.cs b/src/Files.App.Controls/ThemedIcon/ThemedIcon.Owner.cs new file mode 100644 index 000000000000..52572e76381f --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/ThemedIcon.Owner.cs @@ -0,0 +1,59 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using CommunityToolkit.WinUI; + +namespace Files.App.Controls +{ + public partial class ThemedIcon + { + private bool _isOwnerToggled; + private bool _isOwnerEnabled; + + private Control? ownerControl = null; + private ToggleButton? ownerToggleButton = null; + + private void FindOwnerControlStates() + { + if (this.FindAscendant() is ToggleButton toggleButton) + { + ownerToggleButton = toggleButton; + + // IsChecked/IsToggled change aware + ownerToggleButton.Checked += OwnerControl_IsCheckedChanged; + ownerToggleButton.Unchecked += OwnerControl_IsCheckedChanged; + _isOwnerToggled = ownerToggleButton.IsChecked is true; + } + + if (this.FindAscendant() is Control control) + { + ownerControl = control; + + // IsEnabled change aware + ownerControl.IsEnabledChanged += OwnerControl_IsEnabledChanged; + _isOwnerEnabled = ownerControl.IsEnabled; + + UpdateVisualStates(); + } + } + + private void OwnerControl_IsCheckedChanged(object sender, RoutedEventArgs e) + { + if (ownerToggleButton is null) + return; + + _isOwnerToggled = ownerToggleButton.IsChecked is true; + UpdateVisualStates(); + + } + + private void OwnerControl_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) + { + if (ownerControl is null) + return; + + _isOwnerEnabled = ownerControl.IsEnabled; + UpdateVisualStates(); + } + } +} diff --git a/src/Files.App.Controls/ThemedIcon/ThemedIcon.Properties.cs b/src/Files.App.Controls/ThemedIcon/ThemedIcon.Properties.cs new file mode 100644 index 000000000000..263a8545e5cf --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/ThemedIcon.Properties.cs @@ -0,0 +1,118 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using CommunityToolkit.WinUI; +using Microsoft.UI.Xaml.Media; + +namespace Files.App.Controls +{ + public partial class ThemedIcon : Control + { + [GeneratedDependencyProperty] + public partial string FilledIconData { get; set; } + + [GeneratedDependencyProperty] + public partial string OutlineIconData { get; set; } + + [GeneratedDependencyProperty] + public partial Brush Color { get; set; } + + [GeneratedDependencyProperty(DefaultValue = ThemedIconTypes.Layered)] + public partial ThemedIconTypes IconType { get; set; } + + [GeneratedDependencyProperty(DefaultValue = ThemedIconColorType.None)] + public partial ThemedIconColorType IconColorType { get; set; } + + [GeneratedDependencyProperty(DefaultValue = 16.0d)] + public partial double IconSize { get; set; } + + [GeneratedDependencyProperty] + public partial bool IsToggled { get; set; } + + [GeneratedDependencyProperty] + public partial bool IsFilled { get; set; } + + [GeneratedDependencyProperty] + public partial bool IsHighContrast { get; set; } + + [GeneratedDependencyProperty] + public partial object Layers { get; set; } + + [GeneratedDependencyProperty(DefaultValue = ToggleBehaviors.Auto)] + public partial ToggleBehaviors ToggleBehavior { get; set; } + + partial void OnFilledIconDataChanged(string newValue) + { + OnFilledIconChanged(); + } + + partial void OnOutlineIconDataChanged(string newValue) + { + OnOutlineIconChanged(); + } + + partial void OnColorChanged(Brush newValue) + { + OnIconTypeChanged(); + OnIconColorChanged(); + } + + partial void OnIconTypeChanged(ThemedIconTypes newValue) + { + OnIconTypeChanged(); + } + + partial void OnIconColorTypeChanged(ThemedIconColorType newValue) + { + OnIconColorTypeChanged(); + } + + partial void OnIconSizeChanged(double newValue) + { + UpdateVisualStates(); + OnIconSizeChanged(); + } + + partial void OnIsToggledChanged(bool newValue) + { + UpdateVisualStates(); + } + + partial void OnIsFilledChanged(bool newValue) + { + UpdateVisualStates(); + } + + partial void OnIsHighContrastChanged(bool newValue) + { + UpdateVisualStates(); + } + + partial void OnLayersChanged(object newValue) + { + UpdateVisualStates(); + } + + partial void OnToggleBehaviorChanged(ToggleBehaviors newValue) + { + UpdateVisualStates(); + } + + private void OnStylePropertyChanged(DependencyObject sender, DependencyProperty dp) + { + if (dp != StyleProperty) + return; + + DispatcherQueue.TryEnqueue(() => + { + GetTemplateParts(); + OnFilledIconChanged(); + OnOutlineIconChanged(); + OnLayeredIconChanged(); + OnIconTypeChanged(); + OnIconColorTypeChanged(); + OnIconSizeChanged(); + }); + } + } +} diff --git a/src/Files.App.Controls/ThemedIcon/ThemedIcon.ThemeResources.xaml b/src/Files.App.Controls/ThemedIcon/ThemedIcon.ThemeResources.xaml new file mode 100644 index 000000000000..d06e686b1191 --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/ThemedIcon.ThemeResources.xaml @@ -0,0 +1,87 @@ + + + + + + + + + + + #DBF0F0F0 + #66161616 + + + + + + + + + + + + + + + + + + + False + + + + #DB161616 + #66F0F0F0 + + + + + + + + + + + + + + + + + + + False + + + + #FF000000 + #00000000 + + + + + + + + + + + + + + + + + + + True + + + + + diff --git a/src/Files.App.Controls/ThemedIcon/ThemedIcon.cs b/src/Files.App.Controls/ThemedIcon/ThemedIcon.cs new file mode 100644 index 000000000000..17f1f4a2b75b --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/ThemedIcon.cs @@ -0,0 +1,270 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Markup; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Shapes; + +namespace Files.App.Controls +{ + /// + /// A control for a State and Color aware Icon + /// + public partial class ThemedIcon : Control + { + private Viewbox? _filledViewBox; + private Viewbox? _outlineViewBox; + private Viewbox? _layeredViewBox; + private Canvas? _layeredCanvas; + + private long _stylePropertyChangedToken; + + public ThemedIcon() + { + DefaultStyleKey = typeof(ThemedIcon); + _stylePropertyChangedToken = RegisterPropertyChangedCallback( + StyleProperty, + OnStylePropertyChanged + ); + + Unloaded += OnUnloaded; + } + + private void OnUnloaded(object sender, RoutedEventArgs e) + { + UnregisterPropertyChangedCallback(StyleProperty, _stylePropertyChangedToken); + IsEnabledChanged -= OnIsEnabledChanged; + Unloaded -= OnUnloaded; + } + + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + IsEnabledChanged += OnIsEnabledChanged; + + _isOwnerEnabled = IsEnabled; + + GetTemplateParts(); + + FindOwnerControlStates(); + OnFilledIconChanged(); + OnOutlineIconChanged(); + OnLayeredIconChanged(); + + OnIconTypeChanged(); + OnIconColorTypeChanged(); + OnIconSizeChanged(); + } + + private void GetTemplateParts() + { + // Gets the template parts and sets the private fields + _outlineViewBox = GetTemplateChild(OutlinePathIconViewBox) as Viewbox; + _filledViewBox = GetTemplateChild(FilledPathIconViewBox) as Viewbox; + _layeredViewBox = GetTemplateChild(LayeredPathIconViewBox) as Viewbox; + + _layeredCanvas = GetTemplateChild(LayeredPathCanvas) as Canvas; + } + + // Updates paths and layers + + private void OnFilledIconChanged() + { + // Updates Filled Icon from Path Data + if (_filledViewBox == null) + return; + + SetPathData(FilledIconPath, FilledIconData ?? string.Empty, _filledViewBox); + } + + private void OnOutlineIconChanged() + { + // Updates Outline Icon from Path Data + if (_outlineViewBox == null) + return; + + SetPathData(OutlineIconPath, OutlineIconData ?? string.Empty, _outlineViewBox); + } + + private void OnLayeredIconChanged() + { + // Updates Layered Icon from it's Layers + if (_layeredViewBox == null || + _layeredCanvas == null || + Layers is not ICollection layers) + return; + + _layeredCanvas.Children.Clear(); + + foreach (var layer in layers) + { + _layeredCanvas.Children.Add( + new ThemedIconLayer() + { + LayerType = layer.LayerType, + IconColorType = layer.IconColorType, + PathData = layer.PathData, + Opacity = layer.Opacity, + LayerColor = Color, + Foreground = Foreground, + HorizontalAlignment = HorizontalAlignment.Stretch, + VerticalAlignment = VerticalAlignment.Stretch, + LayerSize = IconSize, + Width = layer.LayerSize, + Height = layer.LayerSize + + }); + } + } + + // Updates visual states + + private void OnIconTypeChanged() + { + switch (ToggleBehavior) + { + case ToggleBehaviors.Auto: + { + if (_isOwnerToggled is true || IsFilled is true) + { + VisualStateManager.GoToState(this, FilledTypeStateName, true); + return; + } + else if (IsHighContrast is true || _isOwnerEnabled is false || IsEnabled is false) + { + VisualStateManager.GoToState(this, OutlineTypeStateName, true); + VisualStateManager.GoToState(this, DisabledStateName, true); + return; + } + else + { + VisualStateManager.GoToState( + this, + IconType is ThemedIconTypes.Layered ? LayeredTypeStateName : OutlineTypeStateName, + true); + } + } + break; + case ToggleBehaviors.Off: + { + if (IsFilled is true) + { + VisualStateManager.GoToState(this, FilledTypeStateName, true); + return; + } + else if (IsHighContrast is true || _isOwnerEnabled is false || IsEnabled is false) + { + VisualStateManager.GoToState(this, OutlineTypeStateName, true); + VisualStateManager.GoToState(this, DisabledStateName, true); + return; + } + else + { + VisualStateManager.GoToState( + this, + IconType is ThemedIconTypes.Layered ? LayeredTypeStateName : OutlineTypeStateName, + true); + } + } + break; + case ToggleBehaviors.On: + { + VisualStateManager.GoToState(this, FilledTypeStateName, true); + } + break; + } + + VisualStateManager.GoToState(this, EnabledStateName, true); + } + + private void OnIconColorTypeChanged() + { + if (_isOwnerEnabled && IsEnabled) + { + if ((ToggleBehavior is ToggleBehaviors.Auto && _isOwnerToggled) || + ToggleBehavior is ToggleBehaviors.On) + { + // Toggle + VisualStateManager.GoToState(this, ToggleStateName, true); + } + else + { + // Use colorful ones + VisualStateManager.GoToState( + this, + IconColorType switch + { + ThemedIconColorType.Critical => CriticalStateName, + ThemedIconColorType.Caution => CautionStateName, + ThemedIconColorType.Success => SuccessStateName, + ThemedIconColorType.Neutral => NeutralStateName, + ThemedIconColorType.Accent => AccentStateName, + ThemedIconColorType.Custom => CustomColorStateName, + _ => NormalStateName, + }, + true); + } + + // Update layered icon color + if (_layeredCanvas != null) + foreach (var layer in _layeredCanvas.Children.Cast()) + layer.IconColorType = IconColorType; + } + else + { + // Disable + toggle + if ((ToggleBehavior is ToggleBehaviors.Auto && _isOwnerToggled is true) || + ToggleBehavior is ToggleBehaviors.On) + VisualStateManager.GoToState(this, DisabledToggleColorStateName, true); + // Disable + else + VisualStateManager.GoToState(this, DisabledColorStateName, true); + } + } + + // Misc + + private void UpdateVisualStates() + { + OnIconTypeChanged(); + OnIconColorTypeChanged(); + } + + private void SetPathData(string partName, string pathData, FrameworkElement element) + { + // Updates PathData + if (string.IsNullOrEmpty(pathData)) + return; + + var geometry = (Geometry)XamlReader.Load( + $"{pathData}"); + + if (GetTemplateChild(partName) is Path path) + { + path.Data = geometry; + path.Width = IconSize; + path.Height = IconSize; + } + } + + private void OnIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) + { + UpdateVisualStates(); + } + + private void OnIconColorChanged() + { + if (GetTemplateChild(OutlineIconPath) is Path outlinePath) + outlinePath.Fill = (Brush)this.GetValue(ColorProperty); + + if (GetTemplateChild(FilledIconPath) is Path fillPath) + fillPath.Fill = (Brush)this.GetValue(ColorProperty); + } + + private void OnIconSizeChanged() + { + Height = Width = IconSize; + } + } +} \ No newline at end of file diff --git a/src/Files.App.Controls/ThemedIcon/ThemedIcon.xaml b/src/Files.App.Controls/ThemedIcon/ThemedIcon.xaml new file mode 100644 index 000000000000..bfec9d735c78 --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/ThemedIcon.xaml @@ -0,0 +1,222 @@ + + + + + + + + + diff --git a/src/Files.App.Controls/ThemedIcon/ThemedIconLayer/ThemedIconLayer.Consts.cs b/src/Files.App.Controls/ThemedIcon/ThemedIconLayer/ThemedIconLayer.Consts.cs new file mode 100644 index 000000000000..137eed61a5ed --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/ThemedIconLayer/ThemedIconLayer.Consts.cs @@ -0,0 +1,58 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Shapes; + +namespace Files.App.Controls +{ + // Template Parts + [TemplatePart(Name = LayerCanvasPart, Type = typeof(Canvas))] + [TemplatePart(Name = LayerPathPart, Type = typeof(Path))] + // Icon Color Visual States + [TemplateVisualState(Name = BaseStateName, GroupName = IconLayerColorStateGroupName)] + [TemplateVisualState(Name = AltStateName, GroupName = IconLayerColorStateGroupName)] + [TemplateVisualState(Name = AccentStateName, GroupName = IconLayerColorStateGroupName)] + [TemplateVisualState(Name = AccentContrastStateName, GroupName = IconLayerColorStateGroupName)] + // Non Accent Color States + [TemplateVisualState(Name = CriticalStateName, GroupName = IconLayerColorStateGroupName)] + [TemplateVisualState(Name = CautionStateName, GroupName = IconLayerColorStateGroupName)] + [TemplateVisualState(Name = SuccessStateName, GroupName = IconLayerColorStateGroupName)] + [TemplateVisualState(Name = NeutralStateName, GroupName = IconLayerColorStateGroupName)] + [TemplateVisualState(Name = CustomColorStateName, GroupName = IconLayerColorStateGroupName)] + // Non Accent Contrasting Color States + [TemplateVisualState(Name = CriticalBGStateName, GroupName = IconLayerColorStateGroupName)] + [TemplateVisualState(Name = CautionBGStateName, GroupName = IconLayerColorStateGroupName)] + [TemplateVisualState(Name = SuccessBGStateName, GroupName = IconLayerColorStateGroupName)] + [TemplateVisualState(Name = NeutralBGStateName, GroupName = IconLayerColorStateGroupName)] + [TemplateVisualState(Name = CustomColorBGStateName, GroupName = IconLayerColorStateGroupName)] + /// + /// Displays a layer of for icon type. + /// + public partial class ThemedIconLayer + { + // Path Control Part + internal const string LayerCanvasPart = "PART_LayerCanvas"; + internal const string LayerPathPart = "PART_LayerPath"; + + // Visual State Group Names + internal const string IconLayerColorStateGroupName = "IconLayerColorStates"; + + // Icon Color Type Visual State Names + internal const string BaseStateName = "Base"; + internal const string AltStateName = "Alt"; + internal const string AccentStateName = "Accent"; + internal const string AccentContrastStateName = "AccentContrast"; + + internal const string CriticalStateName = "Critical"; + internal const string CautionStateName = "Caution"; + internal const string SuccessStateName = "Success"; + internal const string NeutralStateName = "Neutral"; + internal const string CustomColorStateName = "Custom"; + + internal const string CriticalBGStateName = "CriticalBG"; + internal const string CautionBGStateName = "CautionBG"; + internal const string SuccessBGStateName = "SuccessBG"; + internal const string NeutralBGStateName = "NeutralBG"; + internal const string CustomColorBGStateName = "CustomBG"; + } +} diff --git a/src/Files.App.Controls/ThemedIcon/ThemedIconLayer/ThemedIconLayer.Properties.cs b/src/Files.App.Controls/ThemedIcon/ThemedIconLayer/ThemedIconLayer.Properties.cs new file mode 100644 index 000000000000..0d031c627154 --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/ThemedIconLayer/ThemedIconLayer.Properties.cs @@ -0,0 +1,51 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using CommunityToolkit.WinUI; +using Microsoft.UI.Xaml.Media; + +namespace Files.App.Controls +{ + public partial class ThemedIconLayer + { + [GeneratedDependencyProperty(DefaultValue = ThemedIconLayerType.Base)] + public partial ThemedIconLayerType LayerType { get; set; } + + [GeneratedDependencyProperty(DefaultValue = "")] + public partial string PathData { get; set; } + + [GeneratedDependencyProperty(DefaultValue = 16.0d)] + public partial double LayerSize { get; set; } + + [GeneratedDependencyProperty] + public partial Brush LayerColor { get; set; } + + [GeneratedDependencyProperty(DefaultValue = ThemedIconColorType.Normal)] + public partial ThemedIconColorType IconColorType { get; set; } + + partial void OnLayerTypeChanged(ThemedIconLayerType newValue) + { + LayerTypeChanged(newValue); + } + + partial void OnPathDataChanged(string newValue) + { + LayerPathDataChanged(newValue); + } + + partial void OnLayerSizeChanged(double newValue) + { + LayerSizePropertyChanged(newValue); + } + + partial void OnLayerColorChanged(Brush newValue) + { + IconColorTypeChanged(); + } + + partial void OnIconColorTypeChanged(ThemedIconColorType newValue) + { + IconColorTypeChanged(); + } + } +} diff --git a/src/Files.App.Controls/ThemedIcon/ThemedIconLayer/ThemedIconLayer.cs b/src/Files.App.Controls/ThemedIcon/ThemedIconLayer/ThemedIconLayer.cs new file mode 100644 index 000000000000..5e1b52444b33 --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/ThemedIconLayer/ThemedIconLayer.cs @@ -0,0 +1,126 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Markup; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Shapes; + +namespace Files.App.Controls +{ + /// + /// A Layer for the ThemedIcon control's Layered IconType + /// + public partial class ThemedIconLayer : Control + { + private double _layerSize; + + public ThemedIconLayer() + { + DefaultStyleKey = typeof(ThemedIconLayer); + } + + protected override void OnApplyTemplate() + { + // Initialize with default values + + base.OnApplyTemplate(); + + UpdateIconLayerState(); + LayerPathDataChanged(PathData); + } + + private void LayerTypeChanged(ThemedIconLayerType layerType) + { + UpdateIconLayerState(); + } + + private void LayerPathDataChanged(string pathData) + { + if (GetTemplateChild(LayerCanvasPart) is not Canvas layerCanvas) + return; + + SetPathData(pathData ?? string.Empty, layerCanvas); + } + + private void LayerSizePropertyChanged(double value) + { + // Code to handle the design time Icon Size changing + + _layerSize = value; + + LayerPathDataChanged(PathData); + UpdateIconLayerState(); + } + + private void IconColorTypeChanged() + { + UpdateIconLayerState(); + } + + private void UpdateIconLayerState() + { + if (LayerType == ThemedIconLayerType.Accent) + { + VisualStateManager.GoToState( + this, + IconColorType switch + { + ThemedIconColorType.Critical => CriticalStateName, + ThemedIconColorType.Caution => CautionStateName, + ThemedIconColorType.Success => SuccessStateName, + ThemedIconColorType.Neutral => NeutralStateName, + ThemedIconColorType.Custom => CustomColorStateName, + _ => AccentStateName, + }, + true); + } + else if (LayerType == ThemedIconLayerType.AccentContrast) + { + VisualStateManager.GoToState( + this, + IconColorType switch + { + ThemedIconColorType.Critical => CriticalBGStateName, + ThemedIconColorType.Caution => CautionBGStateName, + ThemedIconColorType.Success => SuccessBGStateName, + ThemedIconColorType.Neutral => NeutralBGStateName, + ThemedIconColorType.Custom => CustomColorBGStateName, + _ => AccentContrastStateName, + }, + true); + } + else + { + VisualStateManager.GoToState( + this, + LayerType switch + { + ThemedIconLayerType.Alt => AltStateName, + _ => BaseStateName, + }, + true); + } + } + private void SetPathData(string pathData, FrameworkElement element) + { + // Code to take the PathData string, and convert it to an actual path + + // If there is no Path Data, we just exit + // If there is, we create a new Geometry and insert our Path Data string + // We check the Template Part exists, and is the right type, and assign it's data + + if (string.IsNullOrEmpty(pathData)) + return; + + var geometry = (Geometry)XamlReader.Load( + $"{pathData}"); + + if (GetTemplateChild(LayerPathPart) is Path path) + { + path.Data = geometry; + path.Width = element.Width; + path.Width = element.Height; + } + } + } +} diff --git a/src/Files.App.Controls/ThemedIcon/ThemedIconLayer/ThemedIconLayer.xaml b/src/Files.App.Controls/ThemedIcon/ThemedIconLayer/ThemedIconLayer.xaml new file mode 100644 index 000000000000..f491afc21bf0 --- /dev/null +++ b/src/Files.App.Controls/ThemedIcon/ThemedIconLayer/ThemedIconLayer.xaml @@ -0,0 +1,109 @@ + + + + + + + + diff --git a/src/Files.App.Controls/Toolbar/ToolbarButton/ToolbarButton.Constants.cs b/src/Files.App.Controls/Toolbar/ToolbarButton/ToolbarButton.Constants.cs new file mode 100644 index 000000000000..60a0a9606f76 --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarButton/ToolbarButton.Constants.cs @@ -0,0 +1,37 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + // TemplateParts + [TemplatePart(Name = ThemedIconPartName, Type = typeof(ThemedIcon))] + [TemplatePart(Name = ContentPresenterPartName, Type = typeof(ContentPresenter))] + + // VisualStates + [TemplateVisualState(Name = NormalStateName, GroupName = CommonStatesGroupName)] + [TemplateVisualState(Name = PointerOverStateName, GroupName = CommonStatesGroupName)] + [TemplateVisualState(Name = PressedStateName, GroupName = CommonStatesGroupName)] + [TemplateVisualState(Name = DisabledStateName, GroupName = CommonStatesGroupName)] + + [TemplateVisualState(Name = HasContentStateName, GroupName = ContentStatesGroupName)] + [TemplateVisualState(Name = HasNoContentStateName, GroupName = ContentStatesGroupName)] + public partial class ToolbarButton : Button, IToolbarItemSet + { + // TemplatePart Names + internal const string ThemedIconPartName = "PART_ThemedIcon"; + internal const string ContentPresenterPartName = "PART_ContentPresenter"; + + // VisualState Group Names + internal const string CommonStatesGroupName = "CommonStates"; + internal const string ContentStatesGroupName = "ContentStates"; + + // VisualState Names + internal const string NormalStateName = "Normal"; + internal const string PointerOverStateName = "PointerOver"; + internal const string PressedStateName = "Pressed"; + internal const string DisabledStateName = "Disabled"; + + internal const string HasContentStateName = "HasContent"; + internal const string HasNoContentStateName = "HasNoContent"; + } +} diff --git a/src/Files.App.Controls/Toolbar/ToolbarButton/ToolbarButton.Properties.cs b/src/Files.App.Controls/Toolbar/ToolbarButton/ToolbarButton.Properties.cs new file mode 100644 index 000000000000..676376686533 --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarButton/ToolbarButton.Properties.cs @@ -0,0 +1,124 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + public partial class ToolbarButton : Button, IToolbarItemSet + { + #region Label (string) + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty LabelProperty = + DependencyProperty.Register( + nameof(Label), + typeof(string), + typeof(ToolbarButton), + new PropertyMetadata(string.Empty, (d, e) => ((ToolbarButton)d).OnLabelPropertyChanged((string)e.OldValue, (string)e.NewValue))); + + + + /// + /// Gets or sets the Label as a String + /// + public string Label + { + get => (string)GetValue(LabelProperty); + set => SetValue(LabelProperty, value); + } + + + + protected virtual void OnLabelPropertyChanged(string oldValue, string newValue) + { + if (oldValue != newValue) + { + LabelChanged(newValue); + } + } + + #endregion + + #region ThemedIcon (Style) + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty ThemedIconProperty = + DependencyProperty.Register( + nameof(ThemedIcon), + typeof(Style), + typeof(ToolbarButton), + new PropertyMetadata(null, (d, e) => ((ToolbarButton)d).OnThemedIconPropertyChanged((Style)e.OldValue, (Style)e.NewValue))); + + + + /// + /// Gets or sets the Style value for the item's ThemedIcon + /// + public Style ThemedIcon + { + get => (Style)GetValue(ThemedIconProperty); + set => SetValue(ThemedIconProperty, value); + } + + + + protected virtual void OnThemedIconPropertyChanged(Style oldValue, Style newValue) + { + if (newValue != oldValue) + { + ThemedIconChanged(newValue); + } + } + + #endregion + + #region IconSize (double) + + public static readonly DependencyProperty IconSizeProperty = + DependencyProperty.Register( + nameof(IconSize), + typeof(double), + typeof(ToolbarButton), + new PropertyMetadata((double)16, (d, e) => ((ToolbarButton)d).OnIconSizePropertyChanged((double)e.OldValue, (double)e.NewValue))); + + + + /// + /// Gets or sets a value indicating the Icon's design size. + /// + public double IconSize + { + get => (double)GetValue(IconSizeProperty); + set => SetValue(IconSizeProperty, value); + } + + + + protected virtual void OnIconSizePropertyChanged(double oldValue, double newValue) + { + if (newValue != oldValue) + { + IconSizeChanged(newValue); + } + } + + #endregion + + #region ButtonBase Events + + /// + protected override void OnContentChanged(object oldContent, object newContent) + { + if (newContent != oldContent) + { + ContentChanged(newContent); + base.OnContentChanged(oldContent, newContent); + } + } + + #endregion + } +} diff --git a/src/Files.App.Controls/Toolbar/ToolbarButton/ToolbarButton.ThemeResources.xaml b/src/Files.App.Controls/Toolbar/ToolbarButton/ToolbarButton.ThemeResources.xaml new file mode 100644 index 000000000000..df64b3fd13ac --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarButton/ToolbarButton.ThemeResources.xaml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + 1 + + + + 4 + + 32 + 32 + + 32 + 40 + + 40 + 40 + + diff --git a/src/Files.App.Controls/Toolbar/ToolbarButton/ToolbarButton.cs b/src/Files.App.Controls/Toolbar/ToolbarButton/ToolbarButton.cs new file mode 100644 index 000000000000..5561c9ca41b7 --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarButton/ToolbarButton.cs @@ -0,0 +1,180 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + public partial class ToolbarButton : Button, IToolbarItemSet + { + // True when a button has its Content property assigned + private bool _hasContent = false; + + public ToolbarButton() + { + DefaultStyleKey = typeof(ToolbarButton); + } + + /// + protected override void OnApplyTemplate() + { + RegisterPropertyChangedCallback(ContentProperty, OnContentChanged); + + base.OnApplyTemplate(); + + UpdateContentStates(CheckHasContent()); + } + + #region Private Getters + + private bool CheckHasContent() + { + return _hasContent; + } + + #endregion + + #region Private Setters + + private void SetHasContent(bool newValue) + { + _hasContent = newValue; + } + + #endregion + + #region Update functions + + /// + /// Updates the ToolbarButton's Label string value as it changes. + /// + /// + private void UpdateLabel(string newLabel) + { + /// + /// Updates the internal item's Text or Label + /// property as it changes. + /// + } + + + + private void UpdateContent(object newContent) + { + if (CheckHasContent() == false) + { + // We clear the content + } + else + { + // We make sure the content displays + } + + UpdateContentStates(CheckHasContent()); + } + + + + /// + /// Sets the ToolbarButton's ContentState based on whether the + /// Content property has been assigned. + /// + /// + private void UpdateContentStates(bool hasContent) + { + if (hasContent) + { + VisualStateManager.GoToState(this, HasContentStateName, true); + } + else + { + VisualStateManager.GoToState(this, HasNoContentStateName, true); + } + } + + + + /// + /// Updates the ToolbarButton's ThemedIcon TemplatePart's Style value + /// + /// + private void UpdateThemedIcon(Style newStyle) + { + /// + /// Updates the internal item's ThemedIcon + /// Style as it changes. + /// + } + + + + /// + /// Updates the ToolbarButton's ThemedIcon.IconSize double value + /// + /// + private void UpdateIconSize(double newSize) + { + /// + /// Updates the internal item's ThemedIcon + /// IconSize as it changes. + /// + } + + #endregion + + #region Property Changed Events + + /// + /// Invoked when the Label string property has changed + /// + /// + private void LabelChanged(string newLabel) + { + UpdateLabel(newLabel); + } + + + + /// + /// Invoked when the ThemedIcon Style property has changed. + /// + /// + private void ThemedIconChanged(Style newStyle) + { + UpdateThemedIcon(newStyle); + } + + + /// + /// Invoked when the IconSize double property has changed. + /// + /// + private void IconSizeChanged(double newSize) + { + UpdateIconSize(newSize); + } + + #endregion + + #region ButtonBase Events + + /// + /// Invoked when the ToolbarButton's ButtonBase OnContentChanged event + /// is triggered. + /// + /// + private void ContentChanged(object newContent) + { + if (newContent != null) + { + SetHasContent(true); + } + else + { + SetHasContent(false); + } + + UpdateContent(newContent); + } + + #endregion + } +} diff --git a/src/Files.App.Controls/Toolbar/ToolbarButton/ToolbarButton.xaml b/src/Files.App.Controls/Toolbar/ToolbarButton/ToolbarButton.xaml new file mode 100644 index 000000000000..debdab5a3969 --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarButton/ToolbarButton.xaml @@ -0,0 +1,156 @@ + + + + + + diff --git a/src/Files.App.Controls/Toolbar/ToolbarFlyoutButton/ToolbarFlyoutButton.Constants.cs b/src/Files.App.Controls/Toolbar/ToolbarFlyoutButton/ToolbarFlyoutButton.Constants.cs new file mode 100644 index 000000000000..7812fdcb39db --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarFlyoutButton/ToolbarFlyoutButton.Constants.cs @@ -0,0 +1,9 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + public partial class ToolbarFlyoutButton : DropDownButton, IToolbarItemSet + { + } +} diff --git a/src/Files.App.Controls/Toolbar/ToolbarFlyoutButton/ToolbarFlyoutButton.Properties.cs b/src/Files.App.Controls/Toolbar/ToolbarFlyoutButton/ToolbarFlyoutButton.Properties.cs new file mode 100644 index 000000000000..2132bf587efc --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarFlyoutButton/ToolbarFlyoutButton.Properties.cs @@ -0,0 +1,9 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + public partial class ToolbarFlyoutButton : DropDownButton, IToolbarItemSet + { + } +} diff --git a/src/Files.App.Controls/Toolbar/ToolbarFlyoutButton/ToolbarFlyoutButton.ThemeResources.xaml b/src/Files.App.Controls/Toolbar/ToolbarFlyoutButton/ToolbarFlyoutButton.ThemeResources.xaml new file mode 100644 index 000000000000..ef0b32ed6bfa --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarFlyoutButton/ToolbarFlyoutButton.ThemeResources.xaml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + diff --git a/src/Files.App.Controls/Toolbar/ToolbarFlyoutButton/ToolbarFlyoutButton.cs b/src/Files.App.Controls/Toolbar/ToolbarFlyoutButton/ToolbarFlyoutButton.cs new file mode 100644 index 000000000000..d0d159887861 --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarFlyoutButton/ToolbarFlyoutButton.cs @@ -0,0 +1,18 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + public partial class ToolbarFlyoutButton : DropDownButton, IToolbarItemSet + { + public ToolbarFlyoutButton() + { + this.DefaultStyleKey = typeof(ToolbarFlyoutButton); + } + + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + } + } +} diff --git a/src/Files.App.Controls/Toolbar/ToolbarFlyoutButton/ToolbarFlyoutButton.xaml b/src/Files.App.Controls/Toolbar/ToolbarFlyoutButton/ToolbarFlyoutButton.xaml new file mode 100644 index 000000000000..532e84e0258d --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarFlyoutButton/ToolbarFlyoutButton.xaml @@ -0,0 +1,17 @@ + + + + + + diff --git a/src/Files.App.Controls/Toolbar/ToolbarItem/ToolbarItem.Properties.cs b/src/Files.App.Controls/Toolbar/ToolbarItem/ToolbarItem.Properties.cs new file mode 100644 index 000000000000..56a81de49cc3 --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarItem/ToolbarItem.Properties.cs @@ -0,0 +1,421 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Input; + +namespace Files.App.Controls +{ + public partial class ToolbarItem : DependencyObject + { + #region ItemType (enum ToolbarItemTypes) + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty ItemTypeProperty = + DependencyProperty.Register( + nameof(ItemType), + typeof(ToolbarItemTypes), + typeof(ToolbarItem), + new PropertyMetadata(ToolbarItemTypes.Content, (d, e) => ((ToolbarItem)d).OnItemTypePropertyChanged((ToolbarItemTypes)e.OldValue, (ToolbarItemTypes)e.NewValue))); + + + + /// + /// Gets or sets an Enum value to choose from our Toolbar ItemTypes. (Content, Button, FlyoutButton, SplitButton, ToggleButton) + /// + public ToolbarItemTypes ItemType + { + get => (ToolbarItemTypes)GetValue(ItemTypeProperty); + set => SetValue(ItemTypeProperty, value); + } + + + + protected virtual void OnItemTypePropertyChanged(ToolbarItemTypes oldValue, ToolbarItemTypes newValue) + { + if (oldValue != newValue) + { + ItemTypeChanged(newValue); + } + } + + #endregion + + #region OverflowBehavior (enum OverflowBehavior) + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty OverflowBehaviorProperty = + DependencyProperty.Register( + nameof(OverflowBehavior), + typeof(OverflowBehaviors), + typeof(ToolbarItem), + new PropertyMetadata(OverflowBehaviors.Auto, (d, e) => ((ToolbarItem)d).OnOverflowBehaviorPropertyChanged((OverflowBehaviors)e.OldValue, (OverflowBehaviors)e.NewValue))); + + + + /// + /// Gets or sets an Enum value to choose from our Toolbar OverflowBehaviors. (Auto, Always, Never) + /// + public OverflowBehaviors OverflowBehavior + { + get => (OverflowBehaviors)GetValue(OverflowBehaviorProperty); + set => SetValue(OverflowBehaviorProperty, value); + } + + + + protected virtual void OnOverflowBehaviorPropertyChanged(OverflowBehaviors oldValue, OverflowBehaviors newValue) + { + if (newValue != oldValue) + { + OverflowBehaviorChanged(newValue); + } + } + + #endregion + + #region Label (string) + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty LabelProperty = + DependencyProperty.Register( + nameof(Label), + typeof(string), + typeof(ToolbarItem), + new PropertyMetadata(string.Empty, (d, e) => ((ToolbarItem)d).OnLabelPropertyChanged((string)e.OldValue, (string)e.NewValue))); + + + + /// + /// Gets or sets the Label as a String + /// + public string Label + { + get => (string)GetValue(LabelProperty); + set => SetValue(LabelProperty, value); + } + + + + protected virtual void OnLabelPropertyChanged(string oldValue, string newValue) + { + if (newValue != oldValue) + { + LabelChanged(newValue); + } + } + + #endregion + + #region SubItems (IList) + + public static readonly DependencyProperty SubItemsProperty = + DependencyProperty.Register( + nameof(SubItems), + typeof(IList), + typeof(ToolbarItem), + new PropertyMetadata(new List(), (d, e) => ((ToolbarItem)d).OnSubItemsPropertyChanged((IList)e.OldValue, (IList)e.NewValue))); + + + + public IList SubItems + { + get => (IList)GetValue(SubItemsProperty); + set => SetValue(SubItemsProperty, value); + } + + + + protected virtual void OnSubItemsPropertyChanged(IList oldItems, IList newItems) + { + if (newItems != oldItems) + { + SubItemsChanged(newItems); + } + } + + #endregion + + #region Content (object) + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty ContentProperty = + DependencyProperty.Register( + nameof(Content), + typeof(object), + typeof(ToolbarItem), + new PropertyMetadata(null, (d, e) => ((ToolbarItem)d).OnContentPropertyChanged((object)e.OldValue, (object)e.NewValue))); + + + + /// + /// Gets or sets the objects we use as Content for the ToolbarItem. + /// + public object Content + { + get => (object)GetValue(ContentProperty); + set => SetValue(ContentProperty, value); + } + + + + protected virtual void OnContentPropertyChanged(object oldValue, object newValue) + { + if (newValue != oldValue) + { + ContentChanged(newValue); + } + } + + #endregion + + #region ThemedIcon (style) + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty ThemedIconProperty = + DependencyProperty.Register( + nameof(ThemedIcon), + typeof(Style), + typeof(ToolbarItem), + new PropertyMetadata(null, (d, e) => ((ToolbarItem)d).OnThemedIconPropertyChanged((Style)e.OldValue, (Style)e.NewValue))); + + + + /// + /// Gets or sets the Style value for the item's ThemedIcon + /// + public Style ThemedIcon + { + get => (Style)GetValue(ThemedIconProperty); + set => SetValue(ThemedIconProperty, value); + } + + + + protected virtual void OnThemedIconPropertyChanged(Style oldValue, Style newValue) + { + if (newValue != oldValue) + { + ThemedIconChanged(newValue); + } + } + + #endregion + + #region IconSize (double) + + public static readonly DependencyProperty IconSizeProperty = + DependencyProperty.Register( + nameof(IconSize), + typeof(double), + typeof(ToolbarItem), + new PropertyMetadata((double)16, (d, e) => ((ToolbarItem)d).OnIconSizePropertyChanged((double)e.OldValue, (double)e.NewValue))); + + + + /// + /// Gets or sets a value indicating the Icon's design size. + /// + public double IconSize + { + get => (double)GetValue(IconSizeProperty); + set => SetValue(IconSizeProperty, value); + } + + + + protected virtual void OnIconSizePropertyChanged(double oldValue, double newValue) + { + if (newValue != oldValue) + { + IconSizeChanged(newValue); + } + } + + #endregion + + #region IsChecked (bool) + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty IsCheckedProperty = + DependencyProperty.Register( + nameof(IsChecked), + typeof(bool), + typeof(ToolbarItem), + new PropertyMetadata(false, (d, e) => ((ToolbarItem)d).OnIsCheckedPropertyChanged((bool)e.OldValue, (bool)e.NewValue))); + + + + /// + /// Gets or sets the IsChecked as a bool + /// + public bool IsChecked + { + get => (bool)GetValue(IsCheckedProperty); + set => SetValue(IsCheckedProperty, value); + } + + + + protected virtual void OnIsCheckedPropertyChanged(bool oldValue, bool newValue) + { + if (newValue != oldValue) + { + IsCheckedChanged(newValue); + } + } + + #endregion + + #region KeyboardAcceleratorTextOverride (string) + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty KeyboardAcceleratorTextOverrideProperty = + DependencyProperty.Register( + nameof(KeyboardAcceleratorTextOverride), + typeof(string), + typeof(ToolbarItem), + new PropertyMetadata(string.Empty, (d, e) => ((ToolbarItem)d).OnKeyboardAcceleratorTextOverridePropertyChanged((string)e.OldValue, (string)e.NewValue))); + + + + /// + /// Gets or sets the KeyboardAcceleratorTextOverride as a String + /// + public string KeyboardAcceleratorTextOverride + { + get => (string)GetValue(KeyboardAcceleratorTextOverrideProperty); + set => SetValue(KeyboardAcceleratorTextOverrideProperty, value); + } + + + + protected virtual void OnKeyboardAcceleratorTextOverridePropertyChanged(string oldValue, string newValue) + { + if (newValue != oldValue) + { + KeyboardAcceleratorTextOverrideChanged(newValue); + } + } + + #endregion + + #region GroupName (string) + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty GroupNameProperty = + DependencyProperty.Register( + nameof(GroupName), + typeof(string), + typeof(ToolbarItem), + new PropertyMetadata(string.Empty, (d, e) => ((ToolbarItem)d).OnGroupNamePropertyChanged((string)e.OldValue, (string)e.NewValue))); + + + + /// + /// Gets or sets the GroupName as a String + /// + public string GroupName + { + get => (string)GetValue(GroupNameProperty); + set => SetValue(GroupNameProperty, value); + } + + + + protected virtual void OnGroupNamePropertyChanged(string oldValue, string newValue) + { + if (newValue != oldValue) + { + GroupNameChanged(newValue); + } + } + + #endregion + + #region Command (Command) + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty CommandProperty = + DependencyProperty.Register( + nameof(Command), + typeof(XamlUICommand), + typeof(ToolbarItem), + new PropertyMetadata(null, (d, e) => ((ToolbarItem)d).OnCommandPropertyChanged((XamlUICommand)e.OldValue, (XamlUICommand)e.NewValue))); + + + + /// + /// Gets or sets the Command associated with the ToolbarItem as a XamlUICommand + /// + public XamlUICommand Command + { + get => (XamlUICommand)GetValue(CommandProperty); + set => SetValue(CommandProperty, value); + } + + + + protected virtual void OnCommandPropertyChanged(XamlUICommand oldValue, XamlUICommand newValue) + { + if (newValue != oldValue) + { + CommandChanged(newValue); + } + } + + #endregion + + #region CommandParameter (CommandParameter) + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty CommandParameterProperty = + DependencyProperty.Register( + nameof(CommandParameter), + typeof(object), + typeof(ToolbarItem), + new PropertyMetadata(null, (d, e) => ((ToolbarItem)d).OnCommandParameterPropertyChanged((object)e.OldValue, (object)e.NewValue))); + + + + /// + /// Gets or sets the CommandParameter associated with the ToolbarItem as an Object + /// + public object CommandParameter + { + get => (object)GetValue(CommandParameterProperty); + set => SetValue(CommandParameterProperty, value); + } + + + + protected virtual void OnCommandParameterPropertyChanged(object oldValue, object newValue) + { + if (newValue != oldValue) + { + CommandParameterChanged(newValue); + } + } + + #endregion + } +} diff --git a/src/Files.App.Controls/Toolbar/ToolbarItem/ToolbarItem.cs b/src/Files.App.Controls/Toolbar/ToolbarItem/ToolbarItem.cs new file mode 100644 index 000000000000..6709a474c8eb --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarItem/ToolbarItem.cs @@ -0,0 +1,324 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Input; + +namespace Files.App.Controls +{ + /// + /// An Abstract control to simplify items added to the Toolbar, + /// and map them to other controls dependent on their overflow + /// behavior and state + /// + public partial class ToolbarItem : DependencyObject + { + #region Update Item Properties + + private void UpdateItemType(ToolbarItemTypes newItemType) + { + /// + /// We want to intercept the Item Type that is set + /// and then ensure we choose the correct control to + /// map it to internally. + /// + /// ToolbarItemTypes.Content + /// => ContentPresenter + /// + /// ToolbarItemTypes.Button + /// => ToolbarButton + /// => FlyoutMenuItemEx + /// + /// ToolbarItemTypes.Flyout + /// => ToolbarFlyoutButton + /// => MenuFlyoutSubItemEx + /// + /// ToolbarItemTypes.Radio + /// => ToolbarRadioButton + /// => MenuFlyoutRadioItemEx + /// + /// ToolbarItemTypes.Separator + /// => ToolbarSeparator + /// => MenuFlyoutSeparator + /// + /// ToolbarItemTypes.Split + /// => ToolbarSplitButton + /// => MenuFlyoutSubItem => MenuFlyoutItem (for the split buttons main command) + /// + /// ToolbarItemTypes.Toggle + /// => ToolbarToggleButton + /// => MenuFlyoutToggleItemEx + /// + } + + + + private void UpdateLabel(string newLabel) + { + /// + /// Updates the internal item's Text or Label + /// property as it changes. + /// + } + + + + /// + /// Updates the ToolbarItem's SubItems property + /// + /// + private void UpdateSubItems(IList newItems) + { + } + + + + private void UpdateContent(object newContent) + { + /// + /// Updates the internal item's Content + /// property + /// + } + + + + private void UpdateThemedIcon(Style newStyle) + { + /// + /// Updates the internal item's ThemedIcon + /// Style as it changes. + /// + } + + + + private void UpdateKeyboardAcceleratorTextOverride(string newKeyboardAcceleratorText) + { + /// + /// Updates the internal Overflow item's + /// KeyboardAcceleratorTextOverride string as it changes. + /// + } + + + + private void UpdateGroupName(string newGroupName) + { + /// + /// Updates the internal Radio item's + /// GroupName string as it changes. + /// + } + + + + private void UpdateCommand(XamlUICommand newCommand) + { + /// + /// Updates the internal item's + /// Command as it changes. + /// + /// If the internal item is a Button, this will + /// set the Click event handler, otherwise we pass + /// it onto the overflow menu item's Command property. + /// + } + + + + private void UpdateCommandParameter(object newCommandParameter) + { + /// + /// Updates the internal item's + /// CommandParameter as it changes. + /// + /// Not sure if this is relevent to the buttons, + /// but we pass this onto the MenuFlyoutItemEx's + /// CommandParameter property. + /// + } + + + + private void UpdateOverflowBehavior(OverflowBehaviors newOverflowBehavior) + { + /// + /// When we get our ToolbarItem collection, we need to read their + /// OverflowBehavior value and decide if that item belongs in the + /// ToolbarItemList list, or in the ToolbarItemOverflowList list. + /// + /// OverflowBehaviours.Auto + /// The ToolbarItem only moves to Overflow if + /// there is not enough space in the Toolbar. + /// + /// OverflowBehaviours.Always + /// The ToolbarItem is placed in the Overflow + /// menu even if there is enough room in the Toolbar. + /// + /// OverflowBehaviours.Never + /// The ToolbarItem is never placed in the Overflow + /// menu, even when there is insufficiant room, and so + /// does not display. + /// + } + + + + /// + /// Updates the ToolbarItem's IconSize double value + /// + /// + private void UpdateIconSize(double newSize) + { + /// + /// Updates the internal item's ThemedIcon + /// IconSize as it changes. + /// + } + + + + /// + /// Updates the ToolbarItem's IsChecked bool value + /// + /// + private void UpdateIsChecked(bool isChecked) + { + /// + /// Updates the internal item's IsChecked + /// property as it changes. + /// Primarily used for Toggle ItemTypes. + /// + } + + #endregion + + #region Property Changed Events + + private void ItemTypeChanged(ToolbarItemTypes newItemType) + { + UpdateItemType(newItemType); + } + + + + private void OverflowBehaviorChanged(OverflowBehaviors newOverflowBehavior) + { + UpdateOverflowBehavior(newOverflowBehavior); + } + + + + private void LabelChanged(string newLabel) + { + UpdateLabel(newLabel); + } + + + + + /// + /// Handles changes to the ToolbarItem's SubItems property + /// + /// + private void SubItemsChanged(IList newItems) + { + UpdateSubItems(newItems); + } + + + + private void ContentChanged(object newContent) + { + UpdateContent(newContent); + } + + + + private void ThemedIconChanged(Style newStyle) + { + UpdateThemedIcon(newStyle); + } + + + + private void KeyboardAcceleratorTextOverrideChanged(string newKeyboardAcceleratorText) + { + UpdateKeyboardAcceleratorTextOverride(newKeyboardAcceleratorText); + } + + + + private void GroupNameChanged(string newGroupName) + { + UpdateGroupName(newGroupName); + } + + + + private void CommandChanged(XamlUICommand newCommand) + { + UpdateCommand(newCommand); + } + + + + private void CommandParameterChanged(object newCommandParameter) + { + UpdateCommandParameter(newCommandParameter); + } + + + /// + /// Invoked when the IconSize double property has changed. + /// + /// + private void IconSizeChanged(double newSize) + { + UpdateIconSize(newSize); + } + + + /// + /// Invoked when the IsChecked bool property has changed. + /// + /// + private void IsCheckedChanged(bool isChecked) + { + UpdateIsChecked(isChecked); + } + + #endregion + + #region Internal methods + + /// + /// Properties on this ToolbarItem control will be mapped + /// onto the other controls we use to handle these items + /// + /// Label + /// => MenuItemEx.Text + /// => ToolbarButton.Label + /// + /// ThemedIcon + /// => MenuItemEx.ThemedIcon(Style) + /// => ToolbarButton.ThemedIcon(Style) + /// + /// GroupName + /// => RadioMenuFlyoutItemEx.GroupName + /// => ToolbarRadioButton.GroupName + /// + /// KeyboardAcceleratorTextOverride + /// => MenuItemEx.KeyboardAcceleratorTextOverride + /// => ToolbarButton => Tooltip = Label + KeyboardAcceleratorTextOverride + /// + /// Command + /// => MenuItemEx.Command + /// => ToolbarButton.Click event + /// => ToolbarSplitButton.Click event + /// => ToolbarToggleButton.OnToggle event + /// + + #endregion + } +} diff --git a/src/Files.App.Controls/Toolbar/ToolbarItem/ToolbarItemTypes.cs b/src/Files.App.Controls/Toolbar/ToolbarItem/ToolbarItemTypes.cs new file mode 100644 index 000000000000..4c320d2a4fa9 --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarItem/ToolbarItemTypes.cs @@ -0,0 +1,43 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + public enum ToolbarItemTypes + { + /// + /// + /// + Button, + + /// + /// Default type + /// + Content, + + /// + /// + /// + FlyoutButton, + + /// + /// Possibly add support for Radio Buttons using the GroupName + /// + RadioButton, + + /// + /// + /// + Separator, + + /// + /// + /// + SplitButton, + + /// + /// + /// + ToggleButton, + } +} diff --git a/src/Files.App.Controls/Toolbar/ToolbarItemList.cs b/src/Files.App.Controls/Toolbar/ToolbarItemList.cs new file mode 100644 index 000000000000..ab90a1294fa7 --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarItemList.cs @@ -0,0 +1,13 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + /// + /// A collection of Toolbar Items, + /// that are included in the IToolbarItemSet interface + /// + internal partial class ToolbarItemList : List + { + } +} diff --git a/src/Files.App.Controls/Toolbar/ToolbarItemOverflowList.cs b/src/Files.App.Controls/Toolbar/ToolbarItemOverflowList.cs new file mode 100644 index 000000000000..8eb81f37536f --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarItemOverflowList.cs @@ -0,0 +1,13 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + /// + /// A collection of Toolbar Items which are presented in the Overflow Menu, + /// that are included in the IToolbarItemSet interface + /// + internal partial class ToolbarItemOverflowList : List + { + } +} diff --git a/src/Files.App.Controls/Toolbar/ToolbarRadioButton/ToolbarRadioButton.Constants.cs b/src/Files.App.Controls/Toolbar/ToolbarRadioButton/ToolbarRadioButton.Constants.cs new file mode 100644 index 000000000000..a2ef066804fb --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarRadioButton/ToolbarRadioButton.Constants.cs @@ -0,0 +1,9 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + public partial class ToolbarRadioButton : RadioButton, IToolbarItemSet + { + } +} diff --git a/src/Files.App.Controls/Toolbar/ToolbarRadioButton/ToolbarRadioButton.Properties.cs b/src/Files.App.Controls/Toolbar/ToolbarRadioButton/ToolbarRadioButton.Properties.cs new file mode 100644 index 000000000000..3c694623c696 --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarRadioButton/ToolbarRadioButton.Properties.cs @@ -0,0 +1,9 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + public partial class ToolbarRadioButton : RadioButton, IToolbarItemSet + { + } +} diff --git a/src/Files.App.Controls/Toolbar/ToolbarRadioButton/ToolbarRadioButton.ThemeResources.xaml b/src/Files.App.Controls/Toolbar/ToolbarRadioButton/ToolbarRadioButton.ThemeResources.xaml new file mode 100644 index 000000000000..ef0b32ed6bfa --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarRadioButton/ToolbarRadioButton.ThemeResources.xaml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + diff --git a/src/Files.App.Controls/Toolbar/ToolbarRadioButton/ToolbarRadioButton.cs b/src/Files.App.Controls/Toolbar/ToolbarRadioButton/ToolbarRadioButton.cs new file mode 100644 index 000000000000..7760b0b869fc --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarRadioButton/ToolbarRadioButton.cs @@ -0,0 +1,18 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + public partial class ToolbarRadioButton : RadioButton, IToolbarItemSet + { + public ToolbarRadioButton() + { + DefaultStyleKey = typeof(ToolbarRadioButton); + } + + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + } + } +} diff --git a/src/Files.App.Controls/Toolbar/ToolbarRadioButton/ToolbarRadioButton.xaml b/src/Files.App.Controls/Toolbar/ToolbarRadioButton/ToolbarRadioButton.xaml new file mode 100644 index 000000000000..d3e15a43a1f0 --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarRadioButton/ToolbarRadioButton.xaml @@ -0,0 +1,17 @@ + + + + + + diff --git a/src/Files.App.Controls/Toolbar/ToolbarSeparator/ToolbarSeparator.ThemeResources.xaml b/src/Files.App.Controls/Toolbar/ToolbarSeparator/ToolbarSeparator.ThemeResources.xaml new file mode 100644 index 000000000000..ce7a5dc3a397 --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarSeparator/ToolbarSeparator.ThemeResources.xaml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + 4,4,4,4 + 1 + 0.5 + + diff --git a/src/Files.App.Controls/Toolbar/ToolbarSeparator/ToolbarSeparator.cs b/src/Files.App.Controls/Toolbar/ToolbarSeparator/ToolbarSeparator.cs new file mode 100644 index 000000000000..b8d7290522e3 --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarSeparator/ToolbarSeparator.cs @@ -0,0 +1,18 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + public partial class ToolbarSeparator : Control, IToolbarItemSet + { + public ToolbarSeparator() + { + DefaultStyleKey = typeof(ToolbarSeparator); + } + + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + } + } +} diff --git a/src/Files.App.Controls/Toolbar/ToolbarSeparator/ToolbarSeparator.xaml b/src/Files.App.Controls/Toolbar/ToolbarSeparator/ToolbarSeparator.xaml new file mode 100644 index 000000000000..2c14c47ddc6f --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarSeparator/ToolbarSeparator.xaml @@ -0,0 +1,32 @@ + + + + + + diff --git a/src/Files.App.Controls/Toolbar/ToolbarSplitButton/ToolbarSplitButton.Constants.cs b/src/Files.App.Controls/Toolbar/ToolbarSplitButton/ToolbarSplitButton.Constants.cs new file mode 100644 index 000000000000..11ff9378d178 --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarSplitButton/ToolbarSplitButton.Constants.cs @@ -0,0 +1,9 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + public partial class ToolbarSplitButton : SplitButton, IToolbarItemSet + { + } +} diff --git a/src/Files.App.Controls/Toolbar/ToolbarSplitButton/ToolbarSplitButton.Properties.cs b/src/Files.App.Controls/Toolbar/ToolbarSplitButton/ToolbarSplitButton.Properties.cs new file mode 100644 index 000000000000..d51b5cc4c0f3 --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarSplitButton/ToolbarSplitButton.Properties.cs @@ -0,0 +1,9 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + public partial class ToolbarSplitButton : SplitButton, IToolbarItemSet + { + } +} diff --git a/src/Files.App.Controls/Toolbar/ToolbarSplitButton/ToolbarSplitButton.ThemeResources.xaml b/src/Files.App.Controls/Toolbar/ToolbarSplitButton/ToolbarSplitButton.ThemeResources.xaml new file mode 100644 index 000000000000..ef0b32ed6bfa --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarSplitButton/ToolbarSplitButton.ThemeResources.xaml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + diff --git a/src/Files.App.Controls/Toolbar/ToolbarSplitButton/ToolbarSplitButton.cs b/src/Files.App.Controls/Toolbar/ToolbarSplitButton/ToolbarSplitButton.cs new file mode 100644 index 000000000000..11f1f5066556 --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarSplitButton/ToolbarSplitButton.cs @@ -0,0 +1,18 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + public partial class ToolbarSplitButton : SplitButton, IToolbarItemSet + { + public ToolbarSplitButton() + { + DefaultStyleKey = typeof(ToolbarSplitButton); + } + + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + } + } +} diff --git a/src/Files.App.Controls/Toolbar/ToolbarSplitButton/ToolbarSplitButton.xaml b/src/Files.App.Controls/Toolbar/ToolbarSplitButton/ToolbarSplitButton.xaml new file mode 100644 index 000000000000..4705a3f8d910 --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarSplitButton/ToolbarSplitButton.xaml @@ -0,0 +1,17 @@ + + + + + + diff --git a/src/Files.App.Controls/Toolbar/ToolbarToggleButton/ToolbarToggleButton.Constants.cs b/src/Files.App.Controls/Toolbar/ToolbarToggleButton/ToolbarToggleButton.Constants.cs new file mode 100644 index 000000000000..4e05ae5d6156 --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarToggleButton/ToolbarToggleButton.Constants.cs @@ -0,0 +1,57 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + // TemplateParts + [TemplatePart(Name = ThemedIconPartName, Type = typeof(ThemedIcon))] + [TemplatePart(Name = ContentPresenterPartName, Type = typeof(ContentPresenter))] + + // VisualStates + [TemplateVisualState(Name = NormalStateName, GroupName = CommonStatesGroupName)] + [TemplateVisualState(Name = PointerOverStateName, GroupName = CommonStatesGroupName)] + [TemplateVisualState(Name = PressedStateName, GroupName = CommonStatesGroupName)] + [TemplateVisualState(Name = DisabledStateName, GroupName = CommonStatesGroupName)] + + [TemplateVisualState(Name = CheckedStateName, GroupName = CommonStatesGroupName)] + [TemplateVisualState(Name = CheckedPointerOverStateName, GroupName = CommonStatesGroupName)] + [TemplateVisualState(Name = CheckedPressedStateName, GroupName = CommonStatesGroupName)] + [TemplateVisualState(Name = CheckedDisabledStateName, GroupName = CommonStatesGroupName)] + + [TemplateVisualState(Name = IndeterminateStateName, GroupName = CommonStatesGroupName)] + [TemplateVisualState(Name = IndeterminatePointerOverStateName, GroupName = CommonStatesGroupName)] + [TemplateVisualState(Name = IndeterminatePressedStateName, GroupName = CommonStatesGroupName)] + [TemplateVisualState(Name = IndeterminateDisabledStateName, GroupName = CommonStatesGroupName)] + + [TemplateVisualState(Name = HasContentStateName, GroupName = ContentStatesGroupName)] + [TemplateVisualState(Name = HasNoContentStateName, GroupName = ContentStatesGroupName)] + public partial class ToolbarToggleButton : ToggleButton, IToolbarItemSet + { + // TemplatePart Names + internal const string ThemedIconPartName = "PART_ThemedIcon"; + internal const string ContentPresenterPartName = "PART_ContentPresenter"; + + // VisualState Group Names + internal const string CommonStatesGroupName = "CommonStates"; + internal const string ContentStatesGroupName = "ContentStates"; + + // VisualState Names + internal const string NormalStateName = "Normal"; + internal const string PointerOverStateName = "PointerOver"; + internal const string PressedStateName = "Pressed"; + internal const string DisabledStateName = "Disabled"; + + internal const string CheckedStateName = "Checked"; + internal const string CheckedPointerOverStateName = "CheckedPointerOver"; + internal const string CheckedPressedStateName = "CheckedPressed"; + internal const string CheckedDisabledStateName = "CheckedDisabled"; + + internal const string IndeterminateStateName = "Indeterminate"; + internal const string IndeterminatePointerOverStateName = "IndeterminatePointerOver"; + internal const string IndeterminatePressedStateName = "IndeterminatePressed"; + internal const string IndeterminateDisabledStateName = "IndeterminateDisabled"; + + internal const string HasContentStateName = "HasContent"; + internal const string HasNoContentStateName = "HasNoContent"; + } +} diff --git a/src/Files.App.Controls/Toolbar/ToolbarToggleButton/ToolbarToggleButton.Properties.cs b/src/Files.App.Controls/Toolbar/ToolbarToggleButton/ToolbarToggleButton.Properties.cs new file mode 100644 index 000000000000..1cc13e6bfc52 --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarToggleButton/ToolbarToggleButton.Properties.cs @@ -0,0 +1,124 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + public partial class ToolbarToggleButton : ToggleButton, IToolbarItemSet + { + #region Label (string) + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty LabelProperty = + DependencyProperty.Register( + nameof(Label), + typeof(string), + typeof(ToolbarToggleButton), + new PropertyMetadata(string.Empty, (d, e) => ((ToolbarToggleButton)d).OnLabelPropertyChanged((string)e.OldValue, (string)e.NewValue))); + + + + /// + /// Gets or sets the Label as a String + /// + public string Label + { + get => (string)GetValue(LabelProperty); + set => SetValue(LabelProperty, value); + } + + + + protected virtual void OnLabelPropertyChanged(string oldValue, string newValue) + { + if (oldValue != newValue) + { + LabelChanged(newValue); + } + } + + #endregion + + #region ThemedIcon (Style) + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty ThemedIconProperty = + DependencyProperty.Register( + nameof(ThemedIcon), + typeof(Style), + typeof(ToolbarToggleButton), + new PropertyMetadata(null, (d, e) => ((ToolbarToggleButton)d).OnThemedIconPropertyChanged((Style)e.OldValue, (Style)e.NewValue))); + + + + /// + /// Gets or sets the Style value for the item's ThemedIcon + /// + public Style ThemedIcon + { + get => (Style)GetValue(ThemedIconProperty); + set => SetValue(ThemedIconProperty, value); + } + + + + protected virtual void OnThemedIconPropertyChanged(Style oldValue, Style newValue) + { + if (newValue != oldValue) + { + ThemedIconChanged(newValue); + } + } + + #endregion + + #region IconSize (double) + + public static readonly DependencyProperty IconSizeProperty = + DependencyProperty.Register( + nameof(IconSize), + typeof(double), + typeof(ToolbarToggleButton), + new PropertyMetadata((double)16, (d, e) => ((ToolbarToggleButton)d).OnIconSizePropertyChanged((double)e.OldValue, (double)e.NewValue))); + + + + /// + /// Gets or sets a value indicating the Icon's design size. + /// + public double IconSize + { + get => (double)GetValue(IconSizeProperty); + set => SetValue(IconSizeProperty, value); + } + + + + protected virtual void OnIconSizePropertyChanged(double oldValue, double newValue) + { + if (newValue != oldValue) + { + IconSizeChanged(newValue); + } + } + + #endregion + + #region ButtonBase Events + + /// + protected override void OnContentChanged(object oldContent, object newContent) + { + if (newContent != oldContent) + { + ContentChanged(newContent); + base.OnContentChanged(oldContent, newContent); + } + } + + #endregion + } +} diff --git a/src/Files.App.Controls/Toolbar/ToolbarToggleButton/ToolbarToggleButton.ThemeResources.xaml b/src/Files.App.Controls/Toolbar/ToolbarToggleButton/ToolbarToggleButton.ThemeResources.xaml new file mode 100644 index 000000000000..db900b19e0b3 --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarToggleButton/ToolbarToggleButton.ThemeResources.xaml @@ -0,0 +1,182 @@ + + + + + + + + + + 1 + 2 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 2 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 2 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 4 + 3 + + diff --git a/src/Files.App.Controls/Toolbar/ToolbarToggleButton/ToolbarToggleButton.cs b/src/Files.App.Controls/Toolbar/ToolbarToggleButton/ToolbarToggleButton.cs new file mode 100644 index 000000000000..d31356e8a6d6 --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarToggleButton/ToolbarToggleButton.cs @@ -0,0 +1,180 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Controls +{ + public partial class ToolbarToggleButton : ToggleButton, IToolbarItemSet + { + // True when a button has its Content property assigned + private bool _hasContent = false; + + public ToolbarToggleButton() + { + DefaultStyleKey = typeof(ToolbarToggleButton); + } + + protected override void OnApplyTemplate() + { + RegisterPropertyChangedCallback(ContentProperty, OnContentChanged); + + base.OnApplyTemplate(); + + UpdateContentStates(CheckHasContent()); + } + + #region Private Getters + + private bool CheckHasContent() + { + return _hasContent; + } + + #endregion + + #region Private Setters + + private void SetHasContent(bool newValue) + { + _hasContent = newValue; + } + + #endregion + + #region Update functions + + /// + /// Updates the ToolbarButton's Label string value as it changes. + /// + /// + private void UpdateLabel(string newLabel) + { + /// + /// Updates the internal item's Text or Label + /// property as it changes. + /// + } + + + + private void UpdateContent(object newContent) + { + if (CheckHasContent() == false) + { + // We clear the content + } + else + { + // We make sure the content displays + } + + UpdateContentStates(CheckHasContent()); + } + + + + /// + /// Sets the ToolbarButton's ContentState based on whether the + /// Content property has been assigned. + /// + /// + private void UpdateContentStates(bool hasContent) + { + if (hasContent) + { + VisualStateManager.GoToState(this, HasContentStateName, true); + } + else + { + VisualStateManager.GoToState(this, HasNoContentStateName, true); + } + } + + + + /// + /// Updates the ToolbarButton's ThemedIcon TemplatePart's Style value + /// + /// + private void UpdateThemedIcon(Style newStyle) + { + /// + /// Updates the internal item's ThemedIcon + /// Style as it changes. + /// + } + + + + /// + /// Updates the ToolbarButton's ThemedIcon.IconSize double value + /// + /// + private void UpdateIconSize(double newSize) + { + /// + /// Updates the internal item's ThemedIcon + /// IconSize as it changes. + /// + } + + #endregion + + #region Property Changed Events + + /// + /// Invoked when the Label string property has changed + /// + /// + private void LabelChanged(string newLabel) + { + UpdateLabel(newLabel); + } + + + + /// + /// Invoked when the ThemedIcon Style property has changed. + /// + /// + private void ThemedIconChanged(Style newStyle) + { + UpdateThemedIcon(newStyle); + } + + + + /// + /// Invoked when the IconSize double property has changed. + /// + /// + private void IconSizeChanged(double newSize) + { + UpdateIconSize(newSize); + } + + #endregion + + #region ButtonBase Events + + /// + /// Invoked when the ToolbarButton's ButtonBase OnContentChanged event + /// is triggered. + /// + /// + private void ContentChanged(object newContent) + { + if (newContent != null) + { + SetHasContent(true); + } + else + { + SetHasContent(false); + } + + UpdateContent(newContent); + } + + #endregion + } +} diff --git a/src/Files.App.Controls/Toolbar/ToolbarToggleButton/ToolbarToggleButton.xaml b/src/Files.App.Controls/Toolbar/ToolbarToggleButton/ToolbarToggleButton.xaml new file mode 100644 index 000000000000..c4336fae68d5 --- /dev/null +++ b/src/Files.App.Controls/Toolbar/ToolbarToggleButton/ToolbarToggleButton.xaml @@ -0,0 +1,343 @@ + + + + + + diff --git a/src/Files.App.CsWin32/ComHeapPtr`1.cs b/src/Files.App.CsWin32/ComHeapPtr`1.cs new file mode 100644 index 000000000000..2bf4fa41c642 --- /dev/null +++ b/src/Files.App.CsWin32/ComHeapPtr`1.cs @@ -0,0 +1,47 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using System; +using System.Runtime.CompilerServices; + +namespace Windows.Win32 +{ + /// + /// Contains a heap pointer allocated via CoTaskMemAlloc and a set of methods to work with the pointer safely. + /// + public unsafe struct ComHeapPtr : IDisposable where T : unmanaged + { + private T* _ptr; + + public bool IsNull + => _ptr == null; + + public ComHeapPtr(T* ptr) + { + _ptr = ptr; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly T* Get() + { + return _ptr; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly T** GetAddressOf() + { + return (T**)Unsafe.AsPointer(ref Unsafe.AsRef(in this)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() + { + T* ptr = _ptr; + if (ptr is not null) + { + _ptr = null; + PInvoke.CoTaskMemFree((void*)ptr); + } + } + } +} diff --git a/src/Files.App.CsWin32/ComPtr`1.cs b/src/Files.App.CsWin32/ComPtr`1.cs new file mode 100644 index 000000000000..2cceed532893 --- /dev/null +++ b/src/Files.App.CsWin32/ComPtr`1.cs @@ -0,0 +1,96 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using System; +using System.Runtime.CompilerServices; +using Windows.Win32.Foundation; +using Windows.Win32.System.Com; + +namespace Windows.Win32 +{ + /// + /// Contains a COM pointer and a set of methods to work with the pointer safely. + /// + public unsafe struct ComPtr : IDisposable where T : unmanaged, IComIID + { + private T* _ptr; + + public readonly bool IsNull + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _ptr is null; + } + + // Constructors + + public ComPtr(T* ptr) + { + _ptr = ptr; + + if (ptr is not null) + ((IUnknown*)ptr)->AddRef(); + } + + // Methods + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Attach(T* other) + { + if (_ptr is not null) + ((IUnknown*)_ptr)->Release(); + + _ptr = other; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T* Detach() + { + T* ptr = _ptr; + _ptr = null; + return ptr; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly T* Get() + { + return _ptr; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly T** GetAddressOf() + { + return (T**)Unsafe.AsPointer(ref Unsafe.AsRef(in this)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly HRESULT As(U** other) where U : unmanaged, IComIID + { + return ((IUnknown*)_ptr)->QueryInterface((Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in U.Guid)), (void**)other); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly HRESULT As(Guid* riid, IUnknown** other) where U : unmanaged, IComIID + { + return ((IUnknown*)_ptr)->QueryInterface(riid, (void**)other); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly HRESULT CoCreateInstance(Guid* rclsid, IUnknown* pUnkOuter = null, CLSCTX dwClsContext = CLSCTX.CLSCTX_LOCAL_SERVER) + { + return PInvoke.CoCreateInstance(rclsid, pUnkOuter, dwClsContext, (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in T.Guid)), (void**)this.GetAddressOf()); + } + + // Disposer + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() + { + T* ptr = _ptr; + if (ptr is not null) + { + _ptr = null; + ((IUnknown*)ptr)->Release(); + } + } + } +} diff --git a/src/Files.App.CsWin32/Extras.cs b/src/Files.App.CsWin32/Extras.cs new file mode 100644 index 000000000000..99fd57262dba --- /dev/null +++ b/src/Files.App.CsWin32/Extras.cs @@ -0,0 +1,56 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; +using Windows.Win32.Foundation; +using Windows.Win32.UI.WindowsAndMessaging; + +namespace Windows.Win32 +{ + namespace Graphics.Gdi + { + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + public unsafe delegate BOOL MONITORENUMPROC([In] HMONITOR param0, [In] HDC param1, [In][Out] RECT* param2, [In] LPARAM param3); + } + + namespace UI.WindowsAndMessaging + { + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + public delegate LRESULT WNDPROC(HWND hWnd, uint msg, WPARAM wParam, LPARAM lParam); + } + + public static partial class PInvoke + { + [DllImport("User32", EntryPoint = "SetWindowLongW", ExactSpelling = true)] + static extern int _SetWindowLong(HWND hWnd, int nIndex, int dwNewLong); + + [DllImport("User32", EntryPoint = "SetWindowLongPtrW", ExactSpelling = true)] + static extern nint _SetWindowLongPtr(HWND hWnd, int nIndex, nint dwNewLong); + + // NOTE: + // CsWin32 doesn't generate SetWindowLong on other than x86 and vice versa. + // For more info, visit https://github.com/microsoft/CsWin32/issues/882 + public static unsafe nint SetWindowLongPtr(HWND hWnd, WINDOW_LONG_PTR_INDEX nIndex, nint dwNewLong) + { + return sizeof(nint) is 4 + ? (nint)_SetWindowLong(hWnd, (int)nIndex, (int)dwNewLong) + : _SetWindowLongPtr(hWnd, (int)nIndex, dwNewLong); + } + + [LibraryImport("shell32.dll", EntryPoint = "SHUpdateRecycleBinIcon", SetLastError = true)] + public static partial void SHUpdateRecycleBinIcon(); + + public const int PixelFormat32bppARGB = 2498570; + } + + namespace Extras + { + [GeneratedComInterface, Guid("EACDD04C-117E-4E17-88F4-D1B12B0E3D89"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public partial interface IDCompositionTarget + { + [PreserveSig] + int SetRoot(nint visual); + } + } +} diff --git a/src/Files.App.CsWin32/Files.App.CsWin32.csproj b/src/Files.App.CsWin32/Files.App.CsWin32.csproj new file mode 100644 index 000000000000..d3a5a5198de2 --- /dev/null +++ b/src/Files.App.CsWin32/Files.App.CsWin32.csproj @@ -0,0 +1,26 @@ + + + + + $(WindowsTargetFramework) + $(MinimalWindowsVersion) + enable + true + Debug;Release + x86;x64;arm64 + win-x86;win-x64;win-arm64 + true + true + + + + + + + + + + + + + diff --git a/src/Files.App.CsWin32/HRESULT.cs b/src/Files.App.CsWin32/HRESULT.cs new file mode 100644 index 000000000000..b8415f666e0d --- /dev/null +++ b/src/Files.App.CsWin32/HRESULT.cs @@ -0,0 +1,25 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace Windows.Win32.Foundation +{ + [DebuggerDisplay("{" + nameof(Value) + ",h}")] + public readonly partial struct HRESULT + { + /// + /// Throws an exception if the indicates a failure in debug mode. Otherwise, it returns the original . + /// + /// Returns the original value regardless of the operation's success. + public readonly HRESULT ThrowIfFailedOnDebug() + { +#if DEBUG + if (Failed) Marshal.ThrowExceptionForHR(Value); +#endif + + return this; + } + } +} diff --git a/src/Files.App.CsWin32/IStorageProviderQuotaUI.cs b/src/Files.App.CsWin32/IStorageProviderQuotaUI.cs new file mode 100644 index 000000000000..c7cd816b77fc --- /dev/null +++ b/src/Files.App.CsWin32/IStorageProviderQuotaUI.cs @@ -0,0 +1,21 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Files.Shared.Attributes; +using System; +using Windows.Win32.Foundation; + +namespace Windows.Win32.System.WinRT +{ + public unsafe partial struct IStorageProviderQuotaUI : IComIID + { + [GeneratedVTableFunction(Index = 6)] + public partial HRESULT GetQuotaTotalInBytes(ulong* value); + + [GeneratedVTableFunction(Index = 8)] + public partial HRESULT GetQuotaUsedInBytes(ulong* value); + + [GuidRVAGen.Guid("BA6295C3-312E-544F-9FD5-1F81B21F3649")] + public static partial ref readonly Guid Guid { get; } + } +} diff --git a/src/Files.App.CsWin32/IStorageProviderStatusUI.cs b/src/Files.App.CsWin32/IStorageProviderStatusUI.cs new file mode 100644 index 000000000000..3aa60c4255f9 --- /dev/null +++ b/src/Files.App.CsWin32/IStorageProviderStatusUI.cs @@ -0,0 +1,18 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Files.Shared.Attributes; +using System; +using Windows.Win32.Foundation; + +namespace Windows.Win32.System.WinRT +{ + public unsafe partial struct IStorageProviderStatusUI : IComIID + { + [GeneratedVTableFunction(Index = 14)] + public partial HRESULT GetQuotaUI(IStorageProviderQuotaUI** result); + + [GuidRVAGen.Guid("D6B6A758-198D-5B80-977F-5FF73DA33118")] + public static partial ref readonly Guid Guid { get; } + } +} diff --git a/src/Files.App.CsWin32/IStorageProviderStatusUISource.cs b/src/Files.App.CsWin32/IStorageProviderStatusUISource.cs new file mode 100644 index 000000000000..9f4ede28194f --- /dev/null +++ b/src/Files.App.CsWin32/IStorageProviderStatusUISource.cs @@ -0,0 +1,18 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Files.Shared.Attributes; +using System; +using Windows.Win32.Foundation; + +namespace Windows.Win32.System.WinRT +{ + public unsafe partial struct IStorageProviderStatusUISource : IComIID + { + [GeneratedVTableFunction(Index = 6)] + public partial HRESULT GetStatusUI(IStorageProviderStatusUI** result); + + [GuidRVAGen.Guid("A306C249-3D66-5E70-9007-E43DF96051FF")] + public static partial ref readonly Guid Guid { get; } + } +} diff --git a/src/Files.App.CsWin32/IStorageProviderStatusUISourceFactory.cs b/src/Files.App.CsWin32/IStorageProviderStatusUISourceFactory.cs new file mode 100644 index 000000000000..4065c1800c10 --- /dev/null +++ b/src/Files.App.CsWin32/IStorageProviderStatusUISourceFactory.cs @@ -0,0 +1,18 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Files.Shared.Attributes; +using System; +using Windows.Win32.Foundation; + +namespace Windows.Win32.System.WinRT +{ + public unsafe partial struct IStorageProviderStatusUISourceFactory : IComIID + { + [GeneratedVTableFunction(Index = 6)] + public partial HRESULT GetStatusUISource(nint syncRootId, IStorageProviderStatusUISource** result); + + [GuidRVAGen.Guid("12E46B74-4E5A-58D1-A62F-0376E8EE7DD8")] + public static partial ref readonly Guid Guid { get; } + } +} diff --git a/src/Files.App.CsWin32/ManualGuid.cs b/src/Files.App.CsWin32/ManualGuid.cs new file mode 100644 index 000000000000..f95973c45048 --- /dev/null +++ b/src/Files.App.CsWin32/ManualGuid.cs @@ -0,0 +1,108 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using System; +using System.Runtime.CompilerServices; +using Windows.Win32.System.WinRT; + +namespace Windows.Win32 +{ + public static unsafe partial class IID + { + public static Guid* IID_IStorageProviderStatusUISourceFactory + => (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in IStorageProviderStatusUISourceFactory.Guid)); + + [GuidRVAGen.Guid("000214E4-0000-0000-C000-000000000046")] + public static partial Guid* IID_IContextMenu { get; } + + [GuidRVAGen.Guid("70629033-E363-4A28-A567-0DB78006E6D7")] + public static partial Guid* IID_IEnumShellItems { get; } + + [GuidRVAGen.Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE")] + public static partial Guid* IID_IShellItem { get; } + + [GuidRVAGen.Guid("7E9FB0D3-919F-4307-AB2E-9B1860310C93")] + public static partial Guid* IID_IShellItem2 { get; } + + [GuidRVAGen.Guid("947AAB5F-0A5C-4C13-B4D6-4BF7836FC9F8")] + public static partial Guid* IID_IFileOperation { get; } + + [GuidRVAGen.Guid("D57C7288-D4AD-4768-BE02-9D969532D960")] + public static partial Guid* IID_IFileOpenDialog { get; } + + [GuidRVAGen.Guid("84BCCD23-5FDE-4CDB-AEA4-AF64B83D78AB")] + public static partial Guid* IID_IFileSaveDialog { get; } + + [GuidRVAGen.Guid("B92B56A9-8B55-4E14-9A89-0199BBB6F93B")] + public static partial Guid* IID_IDesktopWallpaper { get; } + + [GuidRVAGen.Guid("2E941141-7F97-4756-BA1D-9DECDE894A3D")] + public static partial Guid* IID_IApplicationActivationManager { get; } + + [GuidRVAGen.Guid("00021500-0000-0000-C000-000000000046")] + public static partial Guid* IID_IQueryInfo { get; } + + [GuidRVAGen.Guid("BCC18B79-BA16-442F-80C4-8A59C30C463B")] + public static partial Guid* IID_IShellItemImageFactory { get; } + + [GuidRVAGen.Guid("000214F9-0000-0000-C000-000000000046")] + public static partial Guid* IID_IShellLinkW { get; } + + [GuidRVAGen.Guid("B63EA76D-1F85-456F-A19C-48159EFA858B")] + public static partial Guid* IID_IShellItemArray { get; } + + [GuidRVAGen.Guid("7F9185B0-CB92-43C5-80A9-92277A4F7B54")] + public static partial Guid* IID_IExecuteCommand { get; } + + [GuidRVAGen.Guid("1C9CD5BB-98E9-4491-A60F-31AACC72B83C")] + public static partial Guid* IID_IObjectWithSelection { get; } + + [GuidRVAGen.Guid("000214E8-0000-0000-C000-000000000046")] + public static partial Guid* IID_IShellExtInit { get; } + + [GuidRVAGen.Guid("000214F4-0000-0000-C000-000000000046")] + public static partial Guid* IID_IContextMenu2 { get; } + } + + public static unsafe partial class CLSID + { + [GuidRVAGen.Guid("3AD05575-8857-4850-9277-11B85BDB8E09")] + public static partial Guid* CLSID_FileOperation { get; } + + [GuidRVAGen.Guid("DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7")] + public static partial Guid* CLSID_FileOpenDialog { get; } + + [GuidRVAGen.Guid("C0B4E2F3-BA21-4773-8DBA-335EC946EB8B")] + public static partial Guid* CLSID_FileSaveDialog { get; } + + [GuidRVAGen.Guid("C2CF3110-460E-4FC1-B9D0-8A1C0C9CC4BD")] + public static partial Guid* CLSID_DesktopWallpaper { get; } + + [GuidRVAGen.Guid("45BA127D-10A8-46EA-8AB7-56EA9078943C")] + public static partial Guid* CLSID_ApplicationActivationManager { get; } + + [GuidRVAGen.Guid("B455F46E-E4AF-4035-B0A4-CF18D2F6F28E")] + public static partial Guid* CLSID_PinToFrequentExecute { get; } + + [GuidRVAGen.Guid("EE20EEBA-DF64-4A4E-B7BB-2D1C6B2DFCC1")] + public static partial Guid* CLSID_UnPinFromFrequentExecute { get; } + + [GuidRVAGen.Guid("D969A300-E7FF-11d0-A93B-00A0C90F2719")] + public static partial Guid* CLSID_NewMenu { get; } + } + + public static unsafe partial class BHID + { + [GuidRVAGen.Guid("3981E225-F559-11D3-8E3A-00C04F6837D5")] + public static partial Guid* BHID_SFUIObject { get; } + + [GuidRVAGen.Guid("94F60519-2850-4924-AA5A-D15E84868039")] + public static partial Guid* BHID_EnumItems { get; } + } + + public static unsafe partial class FOLDERID + { + [GuidRVAGen.Guid("B7534046-3ECB-4C18-BE4E-64CD4CB7D6AC")] + public static partial Guid* FOLDERID_RecycleBinFolder { get; } + } +} diff --git a/src/Files.App.CsWin32/NativeMethods.json b/src/Files.App.CsWin32/NativeMethods.json new file mode 100644 index 000000000000..2e32fa443533 --- /dev/null +++ b/src/Files.App.CsWin32/NativeMethods.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://aka.ms/CsWin32.schema.json", + "allowMarshaling": false, + "public": true, + "comInterop": { + "preserveSigMethods": [ + "*" + ] + } +} diff --git a/src/Files.App.CsWin32/NativeMethods.txt b/src/Files.App.CsWin32/NativeMethods.txt new file mode 100644 index 000000000000..9d900f53d0e9 --- /dev/null +++ b/src/Files.App.CsWin32/NativeMethods.txt @@ -0,0 +1,270 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +WNDPROC +WNDCLASSEXW +RegisterClassEx +CreateWindowEx +DestroyWindow +GetModuleHandle +RECT +NOTIFYICONIDENTIFIER +Shell_NotifyIconGetRect +RegisterWindowMessage +NOTIFYICONDATAW +Shell_NotifyIcon +GetCursorPos +DestroyMenu +AppendMenu +CreatePopupMenu +SetForegroundWindow +TrackPopupMenuEx +TRACK_POPUP_MENU_FLAGS +GetSystemMetricsForDpi +DefWindowProc +SYSTEM_METRICS_INDEX +GetDpiForWindow +HWND +LRESULT +WPARAM +LPARAM +WM_* +SetForegroundWindow +GetForegroundWindow +GetCurrentThreadId +GetWindowThreadProcessId +AttachThreadInput +SetWindowPos +SetFocus +SetActiveWindow +IsIconic +CopyFileFromApp +MoveFileFromApp +DeleteFileFromApp +RemoveDirectoryFromApp +GetKeyState +CreateDirectoryFromApp +WNetCancelConnection2 +NETRESOURCEW +WNetAddConnection3 +CREDENTIALW +CredWrite +WNetConnectionDialog1 +CONNECTDLGSTRUCTW +DwmSetWindowAttribute +WIN32_ERROR +CoCreateInstance +FileOpenDialog +IFileOpenDialog +SHCreateItemFromParsingName +FileSaveDialog +IFileSaveDialog +D3D_DRIVER_TYPE +D3D_FEATURE_LEVEL +ID3D11Device +ID3D11DeviceContext +D3D11CreateDevice +IDXGIDevice +DCompositionCreateDevice +IDCompositionDevice +GetNamedSecurityInfo +ConvertSidToStringSid +ConvertStringSidToSid +SetNamedSecurityInfo +GetAclInformation +IsValidAcl +GetAce +SetEntriesInAcl +ACL_SIZE_INFORMATION +DeleteAce +ACCESS_ALLOWED_ACE +LookupAccountSid +GetComputerName +AddAccessAllowedAceEx +LocalAlloc +InitializeAcl +AddAce +LocalFree +IDesktopWallpaper +DesktopWallpaper +SHCreateShellItemArrayFromIDLists +ILCreateFromPath +CLSIDFromString +FindWindow +SendMessage +IsWindowVisible +COPYDATASTRUCT +WINDOW_LONG_PTR_INDEX +CallWindowProc +MINMAXINFO +SUBCLASSPROC +SetWindowPlacement +GetWindowPlacement +WINDOWPLACEMENT +GetSystemMetrics +MONITORENUMPROC +EnumDisplayMonitors +MONITORINFOEXW +GetMonitorInfo +CLASS_E_CLASSNOTAVAILABLE +E_INVALIDARG +RoInitialize +RoRegisterActivationFactories +RoRevokeActivationFactories +WindowsCreateString +WindowsDeleteString +IPreviewHandler +AssocQueryString +GetModuleHandle +SHEmptyRecycleBin +SHFileOperation +SHGetFolderPath +SHGFP_TYPE +SHGetKnownFolderItem +SHQUERYRBINFO +SHQueryRecycleBin +FileOperation +IFileOperation +IShellItem2 +PSGetPropertyKeyFromName +ShellExecuteEx +CoTaskMemFree +QueryDosDevice +DeviceIoControl +CreateFile +GetVolumeInformation +COMPRESSION_FORMAT +FILE_ACCESS_RIGHTS +FindFirstFileEx +FindNextFile +CreateFile +GetFileSizeEx +WIN32_FIND_DATAW +FILE_ACCESS_RIGHTS +SHAddToRecentDocs +SHARD +BHID_EnumItems +FOLDERID_RecycleBinFolder +CoTaskMemFree +SHGetIDListFromObject +SHCreateItemFromIDList +BHID_SFUIObject +IContextMenu +CMF_NORMAL +CMF_OPTIMIZEFORINVOKE +CMF_EXTENDEDVERBS +CMF_DEFAULTONLY +IApplicationDestinations +ApplicationDestinations +IApplicationDocumentLists +ApplicationDocumentLists +IApplicationActivationManager +MENU_ITEM_TYPE +COMPRESSION_FORMAT +FSCTL_SET_COMPRESSION +FSCTL_DISMOUNT_VOLUME +FSCTL_LOCK_VOLUME +FILE_FILE_COMPRESSION +WM_WINDOWPOSCHANGING +WINDOWPOS +UnregisterClass +E_POINTER +E_NOINTERFACE +E_FAIL +IShellFolder +FILE_FLAGS_AND_ATTRIBUTES +GetLogicalDrives +IShellItemImageFactory +GdipCreateBitmapFromHBITMAP +GdipGetImageEncodersSize +GdipGetImageEncoders +ImageFormatPNG +ImageFormatJPEG +IStream +CreateStreamOnHGlobal +STATFLAG +STREAM_SEEK +STGM +GdipSaveImageToStream +GdipGetImageRawFormat +_TRANSFER_SOURCE_FLAGS +E_NOTIMPL +DeleteObject +GdipDisposeImage +DeleteObject +OleInitialize +OleUninitialize +IShellIconOverlayManager +SHGetFileInfo +GetFileAttributes +SetFileAttributes +INVALID_FILE_ATTRIBUTES +SHDefExtractIconW +GdipCreateBitmapFromHICON +SHGetSetFolderCustomSettings +FCSM_ICONFILE +FCS_FORCEWRITE +IShellLinkW +SHFormatDrive +ITaskbarList +ITaskbarList2 +ITaskbarList3 +ITaskbarList4 +TaskbarList +ICustomDestinationList +DestinationList +IObjectArray +GetCurrentProcessExplicitAppUserModelID +SetCurrentProcessExplicitAppUserModelID +GdipCreateBitmapFromScan0 +BITMAP +GetObject +_SICHINTF +RoGetAgileReference +IQueryInfo +QITIPF_FLAGS +GetKeyboardState +MapVirtualKey +GetKeyboardLayout +S_FALSE +IExecuteCommand +IObjectWithSelection +SHCreateShellItemArrayFromShellItem +IShellExtInit +IContextMenu2 +GetSubMenu +GetMenuItemCount +GetMenuItemInfo +IsWow64Process2 +GetCurrentProcess +CertFreeCertificateContext +CryptMsgGetParam +CryptMsgClose +CryptMsgOpenToDecode +CryptMsgUpdate +CertOpenStore +CryptDecodeObject +CertFindCertificateInStore +CertComparePublicKeyInfo +CryptQueryObject +CertCloseStore +WinVerifyTrust +FileTimeToSystemTime +FileTimeToLocalFileTime +SystemTimeToFileTime +CMSG_SIGNER_INFO +CRYPT_ATTRIBUTE +FILETIME +CRYPT_BIT_BLOB +CERT_ALT_NAME_INFO +CERT_CONTEXT +CERT_INFO +CRYPT_ALGORITHM_IDENTIFIER +CERT_PUBLIC_KEY_INFO +CATALOG_INFO +WINTRUST_FILE_INFO +WINTRUST_DATA +HCERTSTORE +CERT_QUERY_ENCODING_TYPE +CertGetNameString +MessageBox diff --git a/src/Files.App.Launcher/Files.App.Launcher.filters b/src/Files.App.Launcher/Files.App.Launcher.filters index 33c5209b1f1c..a33090deb425 100644 --- a/src/Files.App.Launcher/Files.App.Launcher.filters +++ b/src/Files.App.Launcher/Files.App.Launcher.filters @@ -1,5 +1,5 @@  - + diff --git a/src/Files.App.Launcher/Files.App.Launcher.vcxproj b/src/Files.App.Launcher/Files.App.Launcher.vcxproj index d4a97cd06ccb..9fc73ecd9115 100644 --- a/src/Files.App.Launcher/Files.App.Launcher.vcxproj +++ b/src/Files.App.Launcher/Files.App.Launcher.vcxproj @@ -1,7 +1,6 @@ - - + - + Debug @@ -11,18 +10,6 @@ Debug Win32 - - Preview - arm64 - - - Preview - Win32 - - - Preview - x64 - Release arm64 @@ -39,42 +26,6 @@ Release x64 - - Sideload - arm64 - - - Sideload - Win32 - - - Sideload - x64 - - - Stable - arm64 - - - Stable - Win32 - - - Stable - x64 - - - Store - arm64 - - - Store - Win32 - - - Store - x64 - 16.0 @@ -86,7 +37,7 @@ Application true - v143 + v145 Unicode true ..\..\artifacts\$(Configuration)\$(Platform)\$(MSBuildProjectName)\ @@ -115,8 +66,7 @@ - xcopy /s /y "$(ProjectDir)$(OutDir)*.exe" "$(ProjectDir)..\..\src\Files.App\Assets\FilesOpenDialog" - certutil -hashfile "$(ProjectDir)..\..\src\Files.App\Assets\FilesOpenDialog\$(TargetFileName)" SHA256|findstr /R /V "^SHA256 ^CertUtil">"$(ProjectDir)..\..\src\Files.App\Assets\FilesOpenDialog\$(TargetFileName).sha256" + xcopy /s /y "$(ProjectDir)$(OutDir)*.exe" "$(ProjectDir)..\..\src\Files.App\Assets\FilesOpenDialog" @@ -137,54 +87,6 @@ true - - - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreaded - - - true - true - - - - - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreaded - - - true - true - - - - - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreaded - - - true - true - - - - - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreaded - - - true - true - - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) @@ -209,54 +111,6 @@ true - - - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreaded - - - true - true - - - - - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreaded - - - true - true - - - - - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreaded - - - true - true - - - - - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreaded - - - true - true - - true @@ -269,54 +123,6 @@ true - - - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreaded - - - true - true - - - - - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreaded - - - true - true - - - - - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreaded - - - true - true - - - - - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreaded - - - true - true - - @@ -329,15 +135,15 @@ - - + + This project references a NuGet package that is not on this computer. To download those packages, use Restore NuGet Packages. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - + + + \ No newline at end of file diff --git a/src/Files.App.Launcher/FilesLauncher.cpp b/src/Files.App.Launcher/FilesLauncher.cpp index 4873f8ee563a..5b8108d7a482 100644 --- a/src/Files.App.Launcher/FilesLauncher.cpp +++ b/src/Files.App.Launcher/FilesLauncher.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. #include #include @@ -65,7 +65,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine LocalFree(szArglist); WCHAR szBuf[MAX_PATH]; - ExpandEnvironmentStringsW(L"%LOCALAPPDATA%\\Microsoft\\WindowsApps\\files.exe", szBuf, MAX_PATH - 1); + ExpandEnvironmentStringsW(L"%LOCALAPPDATA%\\Microsoft\\WindowsApps\\files-dev.exe", szBuf, MAX_PATH - 1); std::wcout << szBuf << std::endl; if (_waccess(szBuf, 0) == -1) { @@ -172,7 +172,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine swprintf(args, _countof(args) - 1, L"\"%s\" -select \"%s\"", szBuf, item.c_str()); } - std::wstring uriWithArgs = L"files-uwp:?cmd=" + str2wstr(wstring_to_utf8_hex(args)); + std::wstring uriWithArgs = L"files-dev:?cmd=" + str2wstr(wstring_to_utf8_hex(args)); std::wcout << L"Invoking: " << args << L" = " << uriWithArgs << std::endl; @@ -187,7 +187,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine { std::wcout << L"Protocol error: " << GetLastError() << std::endl; - //ShExecInfo.lpFile = L"files.exe"; + //ShExecInfo.lpFile = L"files-dev.exe"; //ShExecInfo.lpParameters = args; //if (!ShellExecuteEx(&ShExecInfo)) //{ @@ -202,13 +202,13 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine SHELLEXECUTEINFO ShExecInfo = { 0 }; ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); ShExecInfo.fMask = SEE_MASK_NOASYNC | SEE_MASK_FLAG_NO_UI; - ShExecInfo.lpFile = L"files-uwp:"; + ShExecInfo.lpFile = L"files-dev:"; ShExecInfo.nShow = SW_SHOW; if (!ShellExecuteEx(&ShExecInfo)) { std::wcout << L"Protocol error: " << GetLastError() << std::endl; - //ShExecInfo.lpFile = L"files.exe"; + //ShExecInfo.lpFile = L"files-dev.exe"; //if (!ShellExecuteEx(&ShExecInfo)) //{ //std::wcout << L"Command line error: " << GetLastError() << std::endl; @@ -343,10 +343,15 @@ bool OpenInExistingShellWindow(const TCHAR* folderPath) { std::wstring openDirectory(folderPath); bool mustOpenInExplorer = false; + constexpr auto godModeClsid = L"{ED7BA470-8E54-465E-825C-99712043E01C}"; if (strifind(openDirectory, L"::{") == 0) openDirectory = L"shell:" + openDirectory; + // Exclude this shell address so that it opens in File Explorer + if (strifind(openDirectory, godModeClsid) != std::wstring::npos) + mustOpenInExplorer = true; + if (strifind(openDirectory, L"shell:") == 0) { std::vector supportedShellFolders{ diff --git a/src/Files.App.Launcher/OpenInFolder.cpp b/src/Files.App.Launcher/OpenInFolder.cpp index c304fef0575d..f86aa6c5d0a5 100644 --- a/src/Files.App.Launcher/OpenInFolder.cpp +++ b/src/Files.App.Launcher/OpenInFolder.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. #include "OpenInFolder.h" diff --git a/src/Files.App.Launcher/OpenInFolder.h b/src/Files.App.Launcher/OpenInFolder.h index 4c1c4d8540c9..24e870902338 100644 --- a/src/Files.App.Launcher/OpenInFolder.h +++ b/src/Files.App.Launcher/OpenInFolder.h @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. #pragma once diff --git a/src/Files.App.Launcher/packages.config b/src/Files.App.Launcher/packages.config index 1b706ca9a10d..3a8e0698a3f8 100644 --- a/src/Files.App.Launcher/packages.config +++ b/src/Files.App.Launcher/packages.config @@ -1,5 +1,5 @@  - - + + \ No newline at end of file diff --git a/src/Files.App.OpenDialog/CustomOpenDialog.cpp b/src/Files.App.OpenDialog/CustomOpenDialog.cpp index bca30e1028c2..c2a17467526b 100644 --- a/src/Files.App.OpenDialog/CustomOpenDialog.cpp +++ b/src/Files.App.OpenDialog/CustomOpenDialog.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. // Abstract: // Implementation of DLL exports. diff --git a/src/Files.App.OpenDialog/CustomOpenDialog.def b/src/Files.App.OpenDialog/CustomOpenDialog.def index 9fc1d06ff641..7ae8bf3744eb 100644 --- a/src/Files.App.OpenDialog/CustomOpenDialog.def +++ b/src/Files.App.OpenDialog/CustomOpenDialog.def @@ -1,5 +1,5 @@ -; Copyright (c) 2024 Files Community -; Licensed under the MIT License. See the LICENSE. +; Copyright (c) Files Community +; Licensed under the MIT License. ; Abstract: ; Declaration the parameters of the module. diff --git a/src/Files.App.OpenDialog/CustomOpenDialog.idl b/src/Files.App.OpenDialog/CustomOpenDialog.idl index e3a41f6ed7ba..61b4cfa48fba 100644 --- a/src/Files.App.OpenDialog/CustomOpenDialog.idl +++ b/src/Files.App.OpenDialog/CustomOpenDialog.idl @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. // Abstract: // IDL source for CustomOpenDialog. diff --git a/src/Files.App.OpenDialog/CustomOpenDialog.rc b/src/Files.App.OpenDialog/CustomOpenDialog.rc index 71f5c163372a..290906c6d978 100644 Binary files a/src/Files.App.OpenDialog/CustomOpenDialog.rc and b/src/Files.App.OpenDialog/CustomOpenDialog.rc differ diff --git a/src/Files.App.OpenDialog/Files.App.OpenDialog.Win32.vcxproj b/src/Files.App.OpenDialog/Files.App.OpenDialog.Win32.vcxproj index ee88f5b2972c..d072282809f8 100644 --- a/src/Files.App.OpenDialog/Files.App.OpenDialog.Win32.vcxproj +++ b/src/Files.App.OpenDialog/Files.App.OpenDialog.Win32.vcxproj @@ -1,7 +1,5 @@ - - + - Debug @@ -11,18 +9,6 @@ Debug Win32 - - Preview - arm64 - - - Preview - Win32 - - - Preview - x64 - Release arm64 @@ -39,44 +25,7 @@ Release x64 - - Sideload - arm64 - - - Sideload - Win32 - - - Sideload - x64 - - - Stable - arm64 - - - Stable - Win32 - - - Stable - x64 - - - Store - arm64 - - - Store - Win32 - - - Store - x64 - - 16.0 {B3FE3F3B-CECC-4918-B72B-5488C3774125} @@ -84,14 +33,12 @@ 10.0 DynamicLibrary true - v143 + v145 Unicode - - true true @@ -99,7 +46,6 @@ ..\..\artifacts\intermediates\$(Platform)\$(MSBuildProjectName)\ Files.App.OpenDialog$(PlatformArchitecture) - true false @@ -108,7 +54,6 @@ ..\..\artifacts\intermediates\$(Platform)\$(MSBuildProjectName)\ Files.App.OpenDialog$(PlatformArchitecture) - Use @@ -140,7 +85,6 @@ xcopy /s /y "$(ProjectDir)$(OutDir)*.dll" "$(ProjectDir)..\..\src\Files.App\Assets\FilesOpenDialog" - Disabled @@ -154,7 +98,6 @@ _DEBUG;%(PreprocessorDefinitions) - Disabled @@ -167,7 +110,6 @@ _DEBUG;%(PreprocessorDefinitions) - Disabled @@ -180,7 +122,6 @@ _DEBUG;%(PreprocessorDefinitions) - MaxSpeed @@ -198,79 +139,6 @@ true - - - - MaxSpeed - WIN32;_WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - Win32 - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - WIN32;_WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - Win32 - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - WIN32;_WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - Win32 - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - WIN32;_WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - Win32 - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - MaxSpeed @@ -287,75 +155,6 @@ true - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - MaxSpeed @@ -372,75 +171,6 @@ true - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - @@ -453,11 +183,13 @@ false - + + false - + + @@ -469,7 +201,5 @@ - - - + \ No newline at end of file diff --git a/src/Files.App.OpenDialog/Files.App.OpenDialog.filters b/src/Files.App.OpenDialog/Files.App.OpenDialog.filters index f2a4f929e533..903ad78668dc 100644 --- a/src/Files.App.OpenDialog/Files.App.OpenDialog.filters +++ b/src/Files.App.OpenDialog/Files.App.OpenDialog.filters @@ -1,5 +1,5 @@  - + diff --git a/src/Files.App.OpenDialog/Files.App.OpenDialog.vcxproj b/src/Files.App.OpenDialog/Files.App.OpenDialog.vcxproj index 98aa6202db4a..d77c8863ef63 100644 --- a/src/Files.App.OpenDialog/Files.App.OpenDialog.vcxproj +++ b/src/Files.App.OpenDialog/Files.App.OpenDialog.vcxproj @@ -1,7 +1,5 @@ - - + - Debug @@ -11,18 +9,6 @@ Debug Win32 - - Preview - arm64 - - - Preview - Win32 - - - Preview - x64 - Release arm64 @@ -39,44 +25,7 @@ Release x64 - - Sideload - arm64 - - - Sideload - Win32 - - - Sideload - x64 - - - Stable - arm64 - - - Stable - Win32 - - - Stable - x64 - - - Store - arm64 - - - Store - Win32 - - - Store - x64 - - 16.0 {A2FF3F3B-8EBC-4108-B99D-1476B7876656} @@ -84,14 +33,12 @@ 10.0 DynamicLibrary true - v143 + v145 Unicode - - true true @@ -99,7 +46,6 @@ ..\..\artifacts\intermediates\$(Platform)\$(MSBuildProjectName)\ Files.App.OpenDialog$(PlatformArchitecture) - true false @@ -108,7 +54,6 @@ ..\..\artifacts\intermediates\$(Platform)\$(MSBuildProjectName)\ Files.App.OpenDialog$(PlatformArchitecture) - Use @@ -140,7 +85,6 @@ xcopy /s /y "$(ProjectDir)$(OutDir)*.dll" "$(ProjectDir)..\..\src\Files.App\Assets\FilesOpenDialog" - Disabled @@ -154,7 +98,6 @@ _DEBUG;%(PreprocessorDefinitions) - Disabled @@ -167,7 +110,6 @@ _DEBUG;%(PreprocessorDefinitions) - Disabled @@ -180,7 +122,6 @@ _DEBUG;%(PreprocessorDefinitions) - MaxSpeed @@ -198,79 +139,6 @@ true - - - - MaxSpeed - WIN32;_WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - Win32 - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - WIN32;_WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - Win32 - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - WIN32;_WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - Win32 - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - WIN32;_WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - Win32 - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - MaxSpeed @@ -287,75 +155,6 @@ true - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - MaxSpeed @@ -372,75 +171,6 @@ true - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - @@ -453,11 +183,13 @@ false - + + false - + + @@ -469,7 +201,5 @@ - - - + \ No newline at end of file diff --git a/src/Files.App.OpenDialog/FilesOpenDialog.cpp b/src/Files.App.OpenDialog/FilesOpenDialog.cpp index f1dec80b6568..d52814824490 100644 --- a/src/Files.App.OpenDialog/FilesOpenDialog.cpp +++ b/src/Files.App.OpenDialog/FilesOpenDialog.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. // Abstract: // Implementation of CFilesOpenDialog. @@ -162,7 +162,7 @@ STDAPICALL CFilesOpenDialog::Show(HWND hwndOwner) PWSTR pszPath = NULL; WCHAR szBuf[MAX_PATH]; TCHAR args[1024] = { 0 }; - ExpandEnvironmentStringsW(L"%LOCALAPPDATA%\\Microsoft\\WindowsApps\\files.exe", szBuf, MAX_PATH - 1); + ExpandEnvironmentStringsW(L"%LOCALAPPDATA%\\Microsoft\\WindowsApps\\files-dev.exe", szBuf, MAX_PATH - 1); HANDLE closeEvent = CreateEvent(NULL, FALSE, FALSE, TEXT("FILEDIALOG")); @@ -177,7 +177,7 @@ STDAPICALL CFilesOpenDialog::Show(HWND hwndOwner) swprintf(args, _countof(args) - 1, L"\"%s\" -outputpath \"%s\"", szBuf, _outputPath.c_str()); } - std::wstring uriWithArgs = L"files-uwp:?cmd=" + str2wstr(wstring_to_utf8_hex(args)); + std::wstring uriWithArgs = L"files-dev:?cmd=" + str2wstr(wstring_to_utf8_hex(args)); ShExecInfo.lpFile = uriWithArgs.c_str(); ShExecInfo.nShow = SW_SHOW; ShellExecuteEx(&ShExecInfo); diff --git a/src/Files.App.OpenDialog/FilesOpenDialog.h b/src/Files.App.OpenDialog/FilesOpenDialog.h index 52843c7e6f2d..01c0c460be91 100644 --- a/src/Files.App.OpenDialog/FilesOpenDialog.h +++ b/src/Files.App.OpenDialog/FilesOpenDialog.h @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. // Abstract: // Declaration of CFilesOpenDialog. diff --git a/src/Files.App.OpenDialog/UndefInterfaces.h b/src/Files.App.OpenDialog/UndefInterfaces.h index 2d07259f97c9..c6346261b4e6 100644 --- a/src/Files.App.OpenDialog/UndefInterfaces.h +++ b/src/Files.App.OpenDialog/UndefInterfaces.h @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. // Abstract: // Declaration of Undocumented interfaces and helpers. diff --git a/src/Files.App.OpenDialog/dllmain.cpp b/src/Files.App.OpenDialog/dllmain.cpp index 807f894f581e..111e4f1b153d 100644 --- a/src/Files.App.OpenDialog/dllmain.cpp +++ b/src/Files.App.OpenDialog/dllmain.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. // Abstract: // Implementation of module class. diff --git a/src/Files.App.OpenDialog/dllmain.h b/src/Files.App.OpenDialog/dllmain.h index 851ba39964c1..e5d52517b234 100644 --- a/src/Files.App.OpenDialog/dllmain.h +++ b/src/Files.App.OpenDialog/dllmain.h @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. // Abstract: // declaration of module class. diff --git a/src/Files.App.OpenDialog/framework.h b/src/Files.App.OpenDialog/framework.h index c1ee9ee89bd1..608961a075fd 100644 --- a/src/Files.App.OpenDialog/framework.h +++ b/src/Files.App.OpenDialog/framework.h @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. #pragma once diff --git a/src/Files.App.OpenDialog/pch.cpp b/src/Files.App.OpenDialog/pch.cpp index 6c53f0baaad4..597acce46dfd 100644 --- a/src/Files.App.OpenDialog/pch.cpp +++ b/src/Files.App.OpenDialog/pch.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. // Abstract: // Source file pch.h corresponding to pch.h precompiled header. diff --git a/src/Files.App.OpenDialog/pch.h b/src/Files.App.OpenDialog/pch.h index 0ef504ce7bfa..11839799b4d9 100644 --- a/src/Files.App.OpenDialog/pch.h +++ b/src/Files.App.OpenDialog/pch.h @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. // Abstract: // pch.h is the precompiled header file named pch.h. diff --git a/src/Files.App.OpenDialog/targetver.h b/src/Files.App.OpenDialog/targetver.h index 5db2b4a04b4d..64b2576e7cea 100644 --- a/src/Files.App.OpenDialog/targetver.h +++ b/src/Files.App.OpenDialog/targetver.h @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. // Note: // Including SDKDDKVer.h defines the latest Windows platform available. diff --git a/src/Files.App.SaveDialog/Files.App.SaveDialog.Win32.vcxproj b/src/Files.App.SaveDialog/Files.App.SaveDialog.Win32.vcxproj index 81db2ee5f38c..3cb9077c6b9e 100644 --- a/src/Files.App.SaveDialog/Files.App.SaveDialog.Win32.vcxproj +++ b/src/Files.App.SaveDialog/Files.App.SaveDialog.Win32.vcxproj @@ -1,4 +1,4 @@ - + @@ -9,18 +9,6 @@ Debug Win32 - - Preview - arm64 - - - Preview - Win32 - - - Preview - x64 - Release arm64 @@ -37,44 +25,7 @@ Release x64 - - Sideload - arm64 - - - Sideload - Win32 - - - Sideload - x64 - - - Stable - arm64 - - - Stable - Win32 - - - Stable - x64 - - - Store - arm64 - - - Store - Win32 - - - Store - x64 - - 16.0 {7756A1A4-17B5-4E6B-9B12-F19AA868A225} @@ -82,14 +33,12 @@ 10.0 DynamicLibrary true - v143 + v145 Unicode - - true true @@ -97,7 +46,6 @@ ..\..\artifacts\intermediates\$(Platform)\$(MSBuildProjectName)\ Files.App.SaveDialog$(PlatformArchitecture) - true false @@ -106,7 +54,6 @@ ..\..\artifacts\intermediates\$(Platform)\$(MSBuildProjectName)\ Files.App.SaveDialog$(PlatformArchitecture) - Use @@ -138,7 +85,6 @@ xcopy /s /y "$(ProjectDir)$(OutDir)*.dll" "$(ProjectDir)..\..\src\Files.App\Assets\FilesOpenDialog" - Disabled @@ -152,7 +98,6 @@ _DEBUG;%(PreprocessorDefinitions) - Disabled @@ -165,7 +110,6 @@ _DEBUG;%(PreprocessorDefinitions) - Disabled @@ -178,7 +122,6 @@ _DEBUG;%(PreprocessorDefinitions) - MaxSpeed @@ -196,79 +139,6 @@ true - - - - MaxSpeed - WIN32;_WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - Win32 - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - WIN32;_WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - Win32 - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - WIN32;_WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - Win32 - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - WIN32;_WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - Win32 - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - MaxSpeed @@ -285,75 +155,6 @@ true - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - MaxSpeed @@ -370,75 +171,6 @@ true - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - @@ -452,11 +184,13 @@ false - + + false - + + @@ -469,7 +203,5 @@ - - \ No newline at end of file diff --git a/src/Files.App.SaveDialog/Files.App.SaveDialog.vcxproj b/src/Files.App.SaveDialog/Files.App.SaveDialog.vcxproj index 9e0232aa2b88..098d63c7cbd8 100644 --- a/src/Files.App.SaveDialog/Files.App.SaveDialog.vcxproj +++ b/src/Files.App.SaveDialog/Files.App.SaveDialog.vcxproj @@ -1,5 +1,4 @@ - - + Debug @@ -9,18 +8,6 @@ Debug Win32 - - Preview - arm64 - - - Preview - Win32 - - - Preview - x64 - Release arm64 @@ -37,44 +24,7 @@ Release x64 - - Sideload - arm64 - - - Sideload - Win32 - - - Sideload - x64 - - - Stable - arm64 - - - Stable - Win32 - - - Stable - x64 - - - Store - arm64 - - - Store - Win32 - - - Store - x64 - - 16.0 {EBFA367F-CBDB-4CD0-B838-D6B95F61D1F6} @@ -82,14 +32,12 @@ 10.0 DynamicLibrary true - v143 + v145 Unicode - - true true @@ -97,7 +45,6 @@ ..\..\artifacts\intermediates\$(Platform)\$(MSBuildProjectName)\ Files.App.SaveDialog$(PlatformArchitecture) - true false @@ -106,7 +53,6 @@ ..\..\artifacts\intermediates\$(Platform)\$(MSBuildProjectName)\ Files.App.SaveDialog$(PlatformArchitecture) - Use @@ -138,7 +84,6 @@ xcopy /s /y "$(ProjectDir)$(OutDir)*.dll" "$(ProjectDir)..\..\src\Files.App\Assets\FilesOpenDialog" - Disabled @@ -152,7 +97,6 @@ _DEBUG;%(PreprocessorDefinitions) - Disabled @@ -165,7 +109,6 @@ _DEBUG;%(PreprocessorDefinitions) - Disabled @@ -178,7 +121,6 @@ _DEBUG;%(PreprocessorDefinitions) - MaxSpeed @@ -196,79 +138,6 @@ true - - - - MaxSpeed - WIN32;_WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - Win32 - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - WIN32;_WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - Win32 - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - WIN32;_WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - Win32 - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - WIN32;_WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - Win32 - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - MaxSpeed @@ -285,75 +154,6 @@ true - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - MaxSpeed @@ -370,75 +170,6 @@ true - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - MaxSpeed - _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - @@ -452,11 +183,13 @@ false - + + false - + + @@ -469,7 +202,5 @@ - - \ No newline at end of file diff --git a/src/Files.App.SaveDialog/FilesSaveDialog.cpp b/src/Files.App.SaveDialog/FilesSaveDialog.cpp index b17d018e78f9..49dddc8fcfb7 100644 --- a/src/Files.App.SaveDialog/FilesSaveDialog.cpp +++ b/src/Files.App.SaveDialog/FilesSaveDialog.cpp @@ -438,7 +438,7 @@ HRESULT __stdcall CFilesSaveDialog::Show(HWND hwndOwner) PWSTR pszPath = NULL; WCHAR szBuf[MAX_PATH]; TCHAR args[1024] = { 0 }; - ExpandEnvironmentStringsW(L"%LOCALAPPDATA%\\Microsoft\\WindowsApps\\files.exe", szBuf, MAX_PATH - 1); + ExpandEnvironmentStringsW(L"%LOCALAPPDATA%\\Microsoft\\WindowsApps\\files-dev.exe", szBuf, MAX_PATH - 1); HANDLE closeEvent = CreateEvent(NULL, FALSE, FALSE, TEXT("FILEDIALOG")); @@ -460,7 +460,7 @@ HRESULT __stdcall CFilesSaveDialog::Show(HWND hwndOwner) swprintf(args, _countof(args) - 1, L"\"%s\" -outputpath \"%s\"", szBuf, _outputPath.c_str()); } - std::wstring uriWithArgs = L"files-uwp:?cmd=" + str2wstr(wstring_to_utf8_hex(args)); + std::wstring uriWithArgs = L"files-dev:?cmd=" + str2wstr(wstring_to_utf8_hex(args)); ShExecInfo.lpFile = uriWithArgs.c_str(); ShExecInfo.nShow = SW_SHOW; ShellExecuteEx(&ShExecInfo); diff --git a/src/Files.App.Server/AppInstanceMonitor.cs b/src/Files.App.Server/AppInstanceMonitor.cs index c1b08fe6f1e8..9d3688e65826 100644 --- a/src/Files.App.Server/AppInstanceMonitor.cs +++ b/src/Files.App.Server/AppInstanceMonitor.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Diagnostics; diff --git a/src/Files.App.Server/Files.App.Server.csproj b/src/Files.App.Server/Files.App.Server.csproj index 3a5bcc6e9271..58bb19550d42 100644 --- a/src/Files.App.Server/Files.App.Server.csproj +++ b/src/Files.App.Server/Files.App.Server.csproj @@ -1,24 +1,25 @@ - + + WinExe en-US Scale|DXFeatureLevel - Language=en-US;af;ar;be-BY;bg;ca;cs-CZ;da;de-DE;el;en-GB;es-ES;es-419;fa-IR;fi-FI;fil-PH;fr-FR;he-IL;hi-IN;hr-HR;hu-HU;id-ID;it-IT;ja-JP;ka;km-KH;ko-KR;ku-Arab;lt-LT;lv-LV;ms-MY;nb-NO;nl-NL;or-IN;pl-PL;pt-BR;pt-PT;ro-RO;ru-RU;sk-SK;sq-AL;sr-Cyrl;sv-SE;ta;th-TH;tr-TR;uk-UA;vi;zh-Hans;zh-Hant - net8.0-windows10.0.22621.0 - 10.0.19041.0 + Language=en-US;af;ar;be-BY;bg;ca;cs-CZ;da;de-DE;el;en-GB;es-ES;es-419;fa-IR;fi-FI;fil-PH;fr-FR;he-IL;hi-IN;hr-HR;hu-HU;hy-AM;id-ID;it-IT;ja-JP;ka;km-KH;ko-KR;lt-LT;lv-LV;ms-MY;nb-NO;nl-NL;pl-PL;pt-BR;pt-PT;ro-RO;ru-RU;sk-SK;sq-AL;sr-Cyrl;sv-SE;ta;th-TH;tr-TR;uk-UA;vi;zh-Hans;zh-Hant + $(WindowsTargetFramework) + $(MinimalWindowsVersion) enable enable - Debug;Release;Stable;Preview;Store + Debug;Release x86;x64;arm64 - true + false win-x86;win-x64;win-arm64 win-x86 win-x64 win-arm64 true true - 10.0.22621.0 + $(TargetWindowsVersion) app.manifest False True @@ -26,21 +27,14 @@ False True true - - - - TRACE;DEBUG;NETFX_CORE - - - TRACE;RELEASE;NETFX_CORE - true + true + true - - + @@ -52,8 +46,10 @@ + + diff --git a/src/Files.App.Server/Helpers.cs b/src/Files.App.Server/Helpers.cs index 94f01b1a44ea..8ab3253540d5 100644 --- a/src/Files.App.Server/Helpers.cs +++ b/src/Files.App.Server/Helpers.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/Files.App.Server/NativeMethods.json b/src/Files.App.Server/NativeMethods.json deleted file mode 100644 index 3ca2cc1e7e4d..000000000000 --- a/src/Files.App.Server/NativeMethods.json +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. -{ - "$schema": "https://aka.ms/CsWin32.schema.json", - - // Emit COM interfaces instead of structs, and allow generation of non-blittable structs for the sake of an easier to use API. - "allowMarshaling": false, - - // A value indicating whether to generate APIs judged to be unnecessary or redundant given the target framework. - // This is useful for multi-targeting projects that need a consistent set of APIs across target frameworks - // to avoid too many conditional compilation regions. - "multiTargetingFriendlyAPIs": false, - - // A value indicating whether friendly overloads should use safe handles. - "useSafeHandles": true, - - // Omit ANSI functions and remove `W` suffix from UTF-16 functions. - "wideCharOnly": true, - - // A value indicating whether to emit a single source file as opposed to types spread across many files. - "emitSingleFile": false, - - // The name of a single class under which all p/invoke methods and constants are generated, regardless of imported module. - "className": "PInvoke", - - // A value indicating whether to expose the generated APIs publicly (as opposed to internally). - "public": false -} diff --git a/src/Files.App.Server/NativeMethods.txt b/src/Files.App.Server/NativeMethods.txt deleted file mode 100644 index 661dfca64d2a..000000000000 --- a/src/Files.App.Server/NativeMethods.txt +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -CLASS_E_CLASSNOTAVAILABLE -E_INVALIDARG -RoInitialize -RoRegisterActivationFactories -RoRevokeActivationFactories -WindowsCreateString -WindowsDeleteString diff --git a/src/Files.App.Server/Program.cs b/src/Files.App.Server/Program.cs index c5e99b80457c..b00061eaba85 100644 --- a/src/Files.App.Server/Program.cs +++ b/src/Files.App.Server/Program.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.Shared.Helpers; using System.Runtime.ExceptionServices; diff --git a/src/Files.App.Server/Properties/PublishProfiles/win-arm64.pubxml b/src/Files.App.Server/Properties/PublishProfiles/win-arm64.pubxml index fa22017606e1..c94e10787961 100644 --- a/src/Files.App.Server/Properties/PublishProfiles/win-arm64.pubxml +++ b/src/Files.App.Server/Properties/PublishProfiles/win-arm64.pubxml @@ -7,7 +7,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem arm64 win-arm64 - true + false False + diff --git a/src/Files.App.Storage/Files.App.Storage.csproj b/src/Files.App.Storage/Files.App.Storage.csproj index c08d08cbe3f0..5d9495110e52 100644 --- a/src/Files.App.Storage/Files.App.Storage.csproj +++ b/src/Files.App.Storage/Files.App.Storage.csproj @@ -1,29 +1,25 @@ - + - net8.0-windows10.0.22621.0 - 10.0.19041.0 + $(WindowsTargetFramework) + $(MinimalWindowsVersion) enable true - Debug;Release;Stable;Preview;Store + Debug;Release x86;x64;arm64 win-x86;win-x64;win-arm64 + true + true + true - + - - TRACE;DEBUG;NETFX_CORE - - - TRACE;RELEASE;NETFX_CORE - true - - + diff --git a/src/Files.App.Storage/Storables/FtpStorage/FtpHelpers.cs b/src/Files.App.Storage/Ftp/FtpHelpers.cs similarity index 79% rename from src/Files.App.Storage/Storables/FtpStorage/FtpHelpers.cs rename to src/Files.App.Storage/Ftp/FtpHelpers.cs index 11354cf30f15..9a0f630e2502 100644 --- a/src/Files.App.Storage/Storables/FtpStorage/FtpHelpers.cs +++ b/src/Files.App.Storage/Ftp/FtpHelpers.cs @@ -1,19 +1,19 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.Shared.Extensions; using FluentFTP; -namespace Files.App.Storage.Storables +namespace Files.App.Storage { internal static class FtpHelpers { public static string GetFtpPath(string path) { - path = path.Replace("\\", "/", StringComparison.Ordinal); + path = path.Replace('\\', '/'); var schemaIndex = path.IndexOf("://", StringComparison.Ordinal) + 3; - var hostIndex = path.IndexOf("/", schemaIndex, StringComparison.Ordinal); + var hostIndex = path.IndexOf('/', schemaIndex); return hostIndex == -1 ? "/" : path.Substring(hostIndex); } @@ -44,9 +44,9 @@ public static ushort GetFtpPort(string path) public static string GetFtpAuthority(string path) { - path = path.Replace("\\", "/", StringComparison.Ordinal); + path = path.Replace('\\', '/'); var schemaIndex = path.IndexOf("://", StringComparison.Ordinal) + 3; - var hostIndex = path.IndexOf("/", schemaIndex, StringComparison.Ordinal); + var hostIndex = path.IndexOf('/', schemaIndex); if (hostIndex == -1) hostIndex = path.Length; diff --git a/src/Files.App.Storage/Ftp/FtpManager.cs b/src/Files.App.Storage/Ftp/FtpManager.cs new file mode 100644 index 000000000000..db737c199771 --- /dev/null +++ b/src/Files.App.Storage/Ftp/FtpManager.cs @@ -0,0 +1,14 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using System.Net; + +namespace Files.App.Storage +{ + public static class FtpManager + { + public static readonly Dictionary Credentials = []; + + public static readonly NetworkCredential Anonymous = new("anonymous", "anonymous"); + } +} diff --git a/src/Files.App.Storage/Ftp/FtpStorable.cs b/src/Files.App.Storage/Ftp/FtpStorable.cs new file mode 100644 index 000000000000..9cf6f855b2ee --- /dev/null +++ b/src/Files.App.Storage/Ftp/FtpStorable.cs @@ -0,0 +1,39 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using FluentFTP; + +namespace Files.App.Storage +{ + public abstract class FtpStorable : IStorableChild + { + /// + public virtual string Name { get; protected set; } + + /// + public virtual string Id { get; } + + /// + /// Gets the parent folder of the storable, if any. + /// + protected virtual IFolder? Parent { get; } + + protected internal FtpStorable(string path, string name, IFolder? parent) + { + Id = FtpHelpers.GetFtpPath(path); + Name = name; + Parent = parent; + } + + /// + public Task GetParentAsync(CancellationToken cancellationToken = default) + { + return Task.FromResult(Parent); + } + + protected AsyncFtpClient GetFtpClient() + { + return FtpHelpers.GetFtpClient(Id); + } + } +} diff --git a/src/Files.App.Storage/Ftp/FtpStorageFile.cs b/src/Files.App.Storage/Ftp/FtpStorageFile.cs new file mode 100644 index 000000000000..decfdcaabfd6 --- /dev/null +++ b/src/Files.App.Storage/Ftp/FtpStorageFile.cs @@ -0,0 +1,29 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using System.IO; + +namespace Files.App.Storage +{ + public sealed class FtpStorageFile : FtpStorable, IChildFile + { + public FtpStorageFile(string path, string name, IFolder? parent) + : base(path, name, parent) + { + } + + /// + public async Task OpenStreamAsync(FileAccess access, CancellationToken cancellationToken = default) + { + using var ftpClient = GetFtpClient(); + await ftpClient.EnsureConnectedAsync(cancellationToken); + + if (access.HasFlag(FileAccess.Write)) + return await ftpClient.OpenWrite(Id, token: cancellationToken); + else if (access.HasFlag(FileAccess.Read)) + return await ftpClient.OpenRead(Id, token: cancellationToken); + else + throw new ArgumentException($"Invalid {nameof(access)} flag."); + } + } +} diff --git a/src/Files.App.Storage/Ftp/FtpStorageFolder.cs b/src/Files.App.Storage/Ftp/FtpStorageFolder.cs new file mode 100644 index 000000000000..83b01dccaa88 --- /dev/null +++ b/src/Files.App.Storage/Ftp/FtpStorageFolder.cs @@ -0,0 +1,173 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Files.Shared.Helpers; +using FluentFTP; +using System.IO; +using System.Runtime.CompilerServices; + +namespace Files.App.Storage +{ + public sealed class FtpStorageFolder : FtpStorable, IModifiableFolder, IChildFolder, IDirectCopy, IDirectMove, IGetFirstByName + { + public FtpStorageFolder(string path, string name, IFolder? parent) + : base(path, name, parent) + { + } + + /// + public async Task GetFirstByNameAsync(string folderName, CancellationToken cancellationToken = default) + { + using var ftpClient = GetFtpClient(); + await ftpClient.EnsureConnectedAsync(cancellationToken); + + var path = FtpHelpers.GetFtpPath(PathHelpers.Combine(Id, folderName)); + var item = await ftpClient.GetObjectInfo(path, token: cancellationToken); + + if (item is null) + throw new FileNotFoundException(); + + if (item.Type == FtpObjectType.Directory) + return new FtpStorageFolder(path, item.Name, this); + else + return new FtpStorageFile(path, item.Name, this); + + } + + /// + public async IAsyncEnumerable GetItemsAsync(StorableType kind = StorableType.All, [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + using var ftpClient = GetFtpClient(); + await ftpClient.EnsureConnectedAsync(cancellationToken); + + if (kind == StorableType.File) + { + foreach (var item in await ftpClient.GetListing(Id, cancellationToken)) + { + if (item.Type == FtpObjectType.File) + yield return new FtpStorageFile(item.FullName, item.Name, this); + } + } + else if (kind == StorableType.Folder) + { + foreach (var item in await ftpClient.GetListing(Id, cancellationToken)) + { + if (item.Type == FtpObjectType.Directory) + yield return new FtpStorageFolder(item.FullName, item.Name, this); + } + } + else + { + foreach (var item in await ftpClient.GetListing(Id, cancellationToken)) + { + if (item.Type == FtpObjectType.File) + yield return new FtpStorageFile(item.FullName, item.Name, this); + + if (item.Type == FtpObjectType.Directory) + yield return new FtpStorageFolder(item.FullName, item.Name, this); + } + } + } + + /// + public Task GetFolderWatcherAsync(CancellationToken cancellationToken = default) + { + return Task.FromException(new NotSupportedException()); + } + + /// + public async Task DeleteAsync(IStorableChild item, CancellationToken cancellationToken = default) + { + using var ftpClient = GetFtpClient(); + await ftpClient.EnsureConnectedAsync(cancellationToken); + + if (item is IFile locatableFile) + { + await ftpClient.DeleteFile(locatableFile.Id, cancellationToken); + } + else if (item is IFolder locatableFolder) + { + await ftpClient.DeleteDirectory(locatableFolder.Id, cancellationToken); + } + else + { + throw new ArgumentException($"Could not delete {item}."); + } + } + + /// + public async Task CreateCopyOfAsync(IStorableChild itemToCopy, bool overwrite = default, CancellationToken cancellationToken = default) + { + if (itemToCopy is IFile sourceFile) + { + var copiedFile = await CreateFileAsync(itemToCopy.Name, overwrite, cancellationToken); + await sourceFile.CopyContentsToAsync(copiedFile, cancellationToken); + + return copiedFile; + } + else + { + throw new NotSupportedException("Copying folders is not supported."); + } + } + + /// + public async Task MoveFromAsync(IStorableChild itemToMove, IModifiableFolder source, bool overwrite = default, CancellationToken cancellationToken = default) + { + using var ftpClient = GetFtpClient(); + await ftpClient.EnsureConnectedAsync(cancellationToken); + + var newItem = await CreateCopyOfAsync(itemToMove, overwrite, cancellationToken); + await source.DeleteAsync(itemToMove, cancellationToken); + + return newItem; + } + + /// + public async Task CreateFileAsync(string desiredName, bool overwrite = default, CancellationToken cancellationToken = default) + { + using var ftpClient = GetFtpClient(); + await ftpClient.EnsureConnectedAsync(cancellationToken); + + var newPath = $"{Id}/{desiredName}"; + if (overwrite && await ftpClient.FileExists(newPath, cancellationToken)) + throw new IOException("File already exists."); + + using var stream = new MemoryStream(); + var result = await ftpClient.UploadStream(stream, newPath, overwrite ? FtpRemoteExists.Overwrite : FtpRemoteExists.Skip, token: cancellationToken); + + if (result == FtpStatus.Success) + { + // Success + return new FtpStorageFile(newPath, desiredName, this); + } + else if (result == FtpStatus.Skipped) + { + // Throw exception since flag CreationCollisionOption.GenerateUniqueName was not satisfied + throw new IOException("Couldn't generate unique name. File skipped."); + } + else + { + // File creation failed + throw new IOException("File creation failed."); + } + } + + /// + public async Task CreateFolderAsync(string desiredName, bool overwrite = default, CancellationToken cancellationToken = default) + { + using var ftpClient = GetFtpClient(); + await ftpClient.EnsureConnectedAsync(cancellationToken); + + var newPath = $"{Id}/{desiredName}"; + if (overwrite && await ftpClient.DirectoryExists(newPath, cancellationToken)) + throw new IOException("Directory already exists."); + + var isSuccessful = await ftpClient.CreateDirectory(newPath, overwrite, cancellationToken); + if (!isSuccessful) + throw new IOException("Directory was not successfully created."); + + return new FtpStorageFolder(newPath, desiredName, this); + } + } +} diff --git a/src/Files.App.Storage/Storables/FtpStorage/FtpStorageService.cs b/src/Files.App.Storage/Ftp/FtpStorageService.cs similarity index 90% rename from src/Files.App.Storage/Storables/FtpStorage/FtpStorageService.cs rename to src/Files.App.Storage/Ftp/FtpStorageService.cs index c299353542d0..20df207f45f1 100644 --- a/src/Files.App.Storage/Storables/FtpStorage/FtpStorageService.cs +++ b/src/Files.App.Storage/Ftp/FtpStorageService.cs @@ -1,10 +1,10 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using FluentFTP; using System.IO; -namespace Files.App.Storage.Storables +namespace Files.App.Storage { /// public sealed class FtpStorageService : IFtpStorageService diff --git a/src/Files.App.Storage/GlobalUsings.cs b/src/Files.App.Storage/GlobalUsings.cs index 3b6f668bd3fb..8fa89979a935 100644 --- a/src/Files.App.Storage/GlobalUsings.cs +++ b/src/Files.App.Storage/GlobalUsings.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. // System global using global::System; @@ -23,7 +23,7 @@ global using global::Files.Core.Storage.Enums; global using global::Files.Core.Storage.EventArguments; global using global::Files.Core.Storage.Extensions; -global using global::Files.Core.Storage.StorageEnumeration; +global using global::OwlCore.Storage; // Files.App.Storage diff --git a/src/Files.App.Storage/Legacy/HomeFolder/HomeFolder.cs b/src/Files.App.Storage/Legacy/HomeFolder/HomeFolder.cs new file mode 100644 index 000000000000..ae976667d582 --- /dev/null +++ b/src/Files.App.Storage/Legacy/HomeFolder/HomeFolder.cs @@ -0,0 +1,99 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using System.Numerics; +using System.Runtime.CompilerServices; +using Windows.Win32; + +namespace Files.App.Storage.Storables +{ + public unsafe partial class HomeFolder : IHomeFolder + { + public string Id => "Home"; // Will be "files://Home" in the future. + + public string Name => "Home"; + + /// + public async IAsyncEnumerable GetItemsAsync(StorableType type = StorableType.Folder, [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + await foreach (var folder in GetQuickAccessFolderAsync(cancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + yield return folder; + } + + await foreach (var drive in GetLogicalDrivesAsync(cancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + yield return drive; + } + + await foreach (var location in GetNetworkLocationsAsync(cancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + yield return location; + } + } + + /// + public IAsyncEnumerable GetQuickAccessFolderAsync(CancellationToken cancellationToken = default) + { + IFolder folder = new WindowsFolder(new Guid("3936e9e4-d92c-4eee-a85a-bc16d5ea0819")); + return folder.GetItemsAsync(StorableType.Folder, cancellationToken); + } + + /// + public IAsyncEnumerable GetLogicalDrivesAsync(CancellationToken cancellationToken = default) + { + var availableDrives = PInvoke.GetLogicalDrives(); + if (availableDrives is 0) + return Enumerable.Empty().ToAsyncEnumerable(); + + int count = BitOperations.PopCount(availableDrives); + var driveLetters = new char[count]; + + count = 0; + char driveLetter = 'A'; + while (availableDrives is not 0) + { + if ((availableDrives & 1) is not 0) + driveLetters[count++] = driveLetter; + + availableDrives >>= 1; + driveLetter++; + } + + List driveItems = []; + foreach (char letter in driveLetters) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (WindowsStorable.TryParse($"{letter}:\\") is not IWindowsStorable driveRoot) + throw new InvalidOperationException(); + + driveItems.Add(new WindowsFolder(driveRoot.ThisPtr)); + } + + return driveItems.ToAsyncEnumerable(); + } + + /// + public IAsyncEnumerable GetNetworkLocationsAsync(CancellationToken cancellationToken = default) + { + Guid FOLDERID_NetHood = new("{C5ABBF53-E17F-4121-8900-86626FC2C973}"); + IFolder folder = new WindowsFolder(FOLDERID_NetHood); + return folder.GetItemsAsync(StorableType.Folder, cancellationToken); + } + + /// + public IAsyncEnumerable GetRecentFilesAsync(CancellationToken cancellationToken = default) + { + Guid FOLDERID_NetHood = new("{AE50C081-EBD2-438A-8655-8A092E34987A}"); + IFolder folder = new WindowsFolder(FOLDERID_NetHood); + return folder.GetItemsAsync(StorableType.Folder, cancellationToken); + } + } +} diff --git a/src/Files.App.Storage/Legacy/HomeFolder/IHomeFolder.cs b/src/Files.App.Storage/Legacy/HomeFolder/IHomeFolder.cs new file mode 100644 index 000000000000..e7db124aa168 --- /dev/null +++ b/src/Files.App.Storage/Legacy/HomeFolder/IHomeFolder.cs @@ -0,0 +1,36 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Storage.Storables +{ + public partial interface IHomeFolder : IFolder + { + /// + /// Gets quick access folders. + /// + /// The cancellation token. + /// A list of the collection. + public IAsyncEnumerable GetQuickAccessFolderAsync(CancellationToken cancellationToken = default); + + /// + /// Gets available logical drives. + /// + /// The cancellation token. + /// A list of the collection. + public IAsyncEnumerable GetLogicalDrivesAsync(CancellationToken cancellationToken = default); + + /// + /// Gets network locations(shortcuts). + /// + /// The cancellation token. + /// A list of the collection. + public IAsyncEnumerable GetNetworkLocationsAsync(CancellationToken cancellationToken = default); + + /// + /// Gets recent files. + /// + /// The cancellation token. + /// A list of the collection. + public IAsyncEnumerable GetRecentFilesAsync(CancellationToken cancellationToken = default); + } +} diff --git a/src/Files.App.Storage/Legacy/NativeStorageLegacy/NativeStorageService.cs b/src/Files.App.Storage/Legacy/NativeStorageLegacy/NativeStorageService.cs new file mode 100644 index 000000000000..b97271818f42 --- /dev/null +++ b/src/Files.App.Storage/Legacy/NativeStorageLegacy/NativeStorageService.cs @@ -0,0 +1,26 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using OwlCore.Storage.System.IO; + +namespace Files.App.Storage.Storables +{ + /// + [Obsolete("Use the new WindowsStorable")] + public sealed class NativeStorageLegacyService : IStorageService + { + /// + public async Task GetFileAsync(string id, CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + return new SystemFile(id); + } + + /// + public async Task GetFolderAsync(string id, CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + return new SystemFolder(id); + } + } +} diff --git a/src/Files.App.Storage/Legacy/RecycleBinWatcher.cs b/src/Files.App.Storage/Legacy/RecycleBinWatcher.cs new file mode 100644 index 000000000000..7b1782fac4ac --- /dev/null +++ b/src/Files.App.Storage/Legacy/RecycleBinWatcher.cs @@ -0,0 +1,111 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Files.Shared.Extensions; +using System.Security.Principal; + +namespace Files.App.Storage.Watchers +{ + [Obsolete] + public class RecycleBinWatcher : ITrashWatcher + { + private readonly List _watchers = []; + + /// + public event EventHandler? ItemAdded; + + /// + public event EventHandler? ItemDeleted; + + /// + public event EventHandler? ItemChanged; + + /// + public event EventHandler? ItemRenamed; + + /// + public event EventHandler? RefreshRequested; + + /// + /// Initializes an instance of class. + /// + public RecycleBinWatcher() + { + StartWatcher(); + } + + /// + public void StartWatcher() + { + // NOTE: SHChangeNotifyRegister only works if recycle bin is open in File Explorer. + + // Listen changes only on the Recycle Bin that the current logon user has + var sid = WindowsIdentity.GetCurrent().User?.ToString() ?? string.Empty; + if (string.IsNullOrEmpty(sid)) + return; + + foreach (var drive in SystemIO.DriveInfo.GetDrives()) + { + var recyclePath = SystemIO.Path.Combine(drive.Name, "$RECYCLE.BIN", sid); + + if (drive.DriveType is SystemIO.DriveType.Network || + !SystemIO.Directory.Exists(recyclePath)) + continue; + + // NOTE: Suppressed NullReferenceException caused by EnableRaisingEvents in #15808 + SafetyExtensions.IgnoreExceptions(() => + { + SystemIO.FileSystemWatcher watcher = new() + { + Path = recyclePath, + Filter = "*.*", + NotifyFilter = SystemIO.NotifyFilters.LastWrite | SystemIO.NotifyFilters.FileName | SystemIO.NotifyFilters.DirectoryName + }; + + watcher.Created += Watcher_Changed; + watcher.Deleted += Watcher_Changed; + watcher.EnableRaisingEvents = true; + + _watchers.Add(watcher); + }); + } + } + + /// + public void StopWatcher() + { + foreach (var watcher in _watchers) + watcher.Dispose(); + } + + private void Watcher_Changed(object sender, SystemIO.FileSystemEventArgs e) + { + // Don't listen changes on files starting with '$I' + if (string.IsNullOrEmpty(e.Name) || + e.Name.StartsWith("$I", StringComparison.Ordinal)) + return; + + switch (e.ChangeType) + { + case SystemIO.WatcherChangeTypes.Created: + ItemAdded?.Invoke(this, e); + break; + case SystemIO.WatcherChangeTypes.Deleted: + ItemDeleted?.Invoke(this, e); + break; + case SystemIO.WatcherChangeTypes.Renamed: + ItemRenamed?.Invoke(this, e); + break; + default: + RefreshRequested?.Invoke(this, e); + break; + } + } + + /// + public void Dispose() + { + StopWatcher(); + } + } +} diff --git a/src/Files.App.Storage/Storables/FtpStorage/FtpManager.cs b/src/Files.App.Storage/Storables/FtpStorage/FtpManager.cs deleted file mode 100644 index 78b89bfe0473..000000000000 --- a/src/Files.App.Storage/Storables/FtpStorage/FtpManager.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System.Net; - -namespace Files.App.Storage.Storables -{ - public static class FtpManager - { - public static readonly Dictionary Credentials = []; - - public static readonly NetworkCredential Anonymous = new("anonymous", "anonymous"); - } -} diff --git a/src/Files.App.Storage/Storables/FtpStorage/FtpStorable.cs b/src/Files.App.Storage/Storables/FtpStorage/FtpStorable.cs deleted file mode 100644 index a132baea1e20..000000000000 --- a/src/Files.App.Storage/Storables/FtpStorage/FtpStorable.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using FluentFTP; - -namespace Files.App.Storage.Storables -{ - public abstract class FtpStorable : ILocatableStorable, INestedStorable - { - /// - public virtual string Path { get; protected set; } - - /// - public virtual string Name { get; protected set; } - - /// - public virtual string Id { get; } - - /// - /// Gets the parent folder of the storable, if any. - /// - protected virtual IFolder? Parent { get; } - - protected internal FtpStorable(string path, string name, IFolder? parent) - { - Path = FtpHelpers.GetFtpPath(path); - Name = name; - Id = Path; - Parent = parent; - } - - /// - public Task GetParentAsync(CancellationToken cancellationToken = default) - { - return Task.FromResult(Parent); - } - - protected AsyncFtpClient GetFtpClient() - { - return FtpHelpers.GetFtpClient(Path); - } - } -} diff --git a/src/Files.App.Storage/Storables/FtpStorage/FtpStorageFile.cs b/src/Files.App.Storage/Storables/FtpStorage/FtpStorageFile.cs deleted file mode 100644 index 9dc06b60d087..000000000000 --- a/src/Files.App.Storage/Storables/FtpStorage/FtpStorageFile.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System.IO; - -namespace Files.App.Storage.Storables -{ - public sealed class FtpStorageFile : FtpStorable, IModifiableFile, ILocatableFile, INestedFile - { - public FtpStorageFile(string path, string name, IFolder? parent) - : base(path, name, parent) - { - } - - /// - public async Task OpenStreamAsync(FileAccess access, CancellationToken cancellationToken = default) - { - using var ftpClient = GetFtpClient(); - await ftpClient.EnsureConnectedAsync(cancellationToken); - - if (access.HasFlag(FileAccess.Write)) - return await ftpClient.OpenWrite(Path, token: cancellationToken); - else if (access.HasFlag(FileAccess.Read)) - return await ftpClient.OpenRead(Path, token: cancellationToken); - else - throw new ArgumentException($"Invalid {nameof(access)} flag."); - } - } -} diff --git a/src/Files.App.Storage/Storables/FtpStorage/FtpStorageFolder.cs b/src/Files.App.Storage/Storables/FtpStorage/FtpStorageFolder.cs deleted file mode 100644 index 9a79730056bd..000000000000 --- a/src/Files.App.Storage/Storables/FtpStorage/FtpStorageFolder.cs +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.Shared.Helpers; -using FluentFTP; -using System.IO; -using System.Runtime.CompilerServices; - -namespace Files.App.Storage.Storables -{ - public sealed class FtpStorageFolder : FtpStorable, ILocatableFolder, IModifiableFolder, IFolderExtended, INestedFolder, IDirectCopy, IDirectMove - { - public FtpStorageFolder(string path, string name, IFolder? parent) - : base(path, name, parent) - { - } - - /// - public async Task GetFileAsync(string fileName, CancellationToken cancellationToken = default) - { - using var ftpClient = GetFtpClient(); - await ftpClient.EnsureConnectedAsync(cancellationToken); - - var path = FtpHelpers.GetFtpPath(PathHelpers.Combine(Path, fileName)); - var item = await ftpClient.GetObjectInfo(path, token: cancellationToken); - - if (item is null || item.Type != FtpObjectType.File) - throw new FileNotFoundException(); - - return new FtpStorageFile(path, item.Name, this); - } - - /// - public async Task GetFolderAsync(string folderName, CancellationToken cancellationToken = default) - { - using var ftpClient = GetFtpClient(); - await ftpClient.EnsureConnectedAsync(cancellationToken); - - var path = FtpHelpers.GetFtpPath(PathHelpers.Combine(Path, folderName)); - var item = await ftpClient.GetObjectInfo(path, token: cancellationToken); - - if (item is null || item.Type != FtpObjectType.Directory) - throw new DirectoryNotFoundException(); - - return new FtpStorageFolder(path, item.Name, this); - } - - /// - public async IAsyncEnumerable GetItemsAsync(StorableKind kind = StorableKind.All, [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - using var ftpClient = GetFtpClient(); - await ftpClient.EnsureConnectedAsync(cancellationToken); - - if (kind == StorableKind.Files) - { - foreach (var item in await ftpClient.GetListing(Path, cancellationToken)) - { - if (item.Type == FtpObjectType.File) - yield return new FtpStorageFile(item.FullName, item.Name, this); - } - } - else if (kind == StorableKind.Folders) - { - foreach (var item in await ftpClient.GetListing(Path, cancellationToken)) - { - if (item.Type == FtpObjectType.Directory) - yield return new FtpStorageFolder(item.FullName, item.Name, this); - } - } - else - { - foreach (var item in await ftpClient.GetListing(Path, cancellationToken)) - { - if (item.Type == FtpObjectType.File) - yield return new FtpStorageFile(item.FullName, item.Name, this); - - if (item.Type == FtpObjectType.Directory) - yield return new FtpStorageFolder(item.FullName, item.Name, this); - } - } - } - - /// - public async Task DeleteAsync(INestedStorable item, bool permanently = false, CancellationToken cancellationToken = default) - { - using var ftpClient = GetFtpClient(); - await ftpClient.EnsureConnectedAsync(cancellationToken); - - if (item is ILocatableFile locatableFile) - { - await ftpClient.DeleteFile(locatableFile.Path, cancellationToken); - } - else if (item is ILocatableFolder locatableFolder) - { - await ftpClient.DeleteDirectory(locatableFolder.Path, cancellationToken); - } - else - { - throw new ArgumentException($"Could not delete {item}."); - } - } - - /// - public async Task CreateCopyOfAsync(INestedStorable itemToCopy, bool overwrite = default, CancellationToken cancellationToken = default) - { - if (itemToCopy is IFile sourceFile) - { - var copiedFile = await CreateFileAsync(itemToCopy.Name, overwrite, cancellationToken); - await sourceFile.CopyContentsToAsync(copiedFile, cancellationToken); - - return copiedFile; - } - else - { - throw new NotSupportedException("Copying folders is not supported."); - } - } - - /// - public async Task MoveFromAsync(INestedStorable itemToMove, IModifiableFolder source, bool overwrite = default, CancellationToken cancellationToken = default) - { - using var ftpClient = GetFtpClient(); - await ftpClient.EnsureConnectedAsync(cancellationToken); - - var newItem = await CreateCopyOfAsync(itemToMove, overwrite, cancellationToken); - await source.DeleteAsync(itemToMove, true, cancellationToken); - - return newItem; - } - - /// - public async Task CreateFileAsync(string desiredName, bool overwrite = default, CancellationToken cancellationToken = default) - { - using var ftpClient = GetFtpClient(); - await ftpClient.EnsureConnectedAsync(cancellationToken); - - var newPath = $"{Path}/{desiredName}"; - if (overwrite && await ftpClient.FileExists(newPath, cancellationToken)) - throw new IOException("File already exists."); - - using var stream = new MemoryStream(); - var result = await ftpClient.UploadStream(stream, newPath, overwrite ? FtpRemoteExists.Overwrite : FtpRemoteExists.Skip, token: cancellationToken); - - if (result == FtpStatus.Success) - { - // Success - return new FtpStorageFile(newPath, desiredName, this); - } - else if (result == FtpStatus.Skipped) - { - // Throw exception since flag CreationCollisionOption.GenerateUniqueName was not satisfied - throw new IOException("Couldn't generate unique name. File skipped."); - } - else - { - // File creation failed - throw new IOException("File creation failed."); - } - } - - /// - public async Task CreateFolderAsync(string desiredName, bool overwrite = default, CancellationToken cancellationToken = default) - { - using var ftpClient = GetFtpClient(); - await ftpClient.EnsureConnectedAsync(cancellationToken); - - var newPath = $"{Path}/{desiredName}"; - if (overwrite && await ftpClient.DirectoryExists(newPath, cancellationToken)) - throw new IOException("Directory already exists."); - - var isSuccessful = await ftpClient.CreateDirectory(newPath, overwrite, cancellationToken); - if (!isSuccessful) - throw new IOException("Directory was not successfully created."); - - return new FtpStorageFolder(newPath, desiredName, this); - } - } -} diff --git a/src/Files.App.Storage/Storables/NativeStorage/NativeFile.cs b/src/Files.App.Storage/Storables/NativeStorage/NativeFile.cs deleted file mode 100644 index d930426f77b7..000000000000 --- a/src/Files.App.Storage/Storables/NativeStorage/NativeFile.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System.IO; - -namespace Files.App.Storage.Storables -{ - /// - public class NativeFile : NativeStorable, ILocatableFile, IModifiableFile, IFileExtended, INestedFile - { - public NativeFile(FileInfo fileInfo, string? name = null) - : base(fileInfo, name) - { - } - - public NativeFile(string path, string? name = null) - : this(new FileInfo(path), name) - { - } - - /// - public virtual Task OpenStreamAsync(FileAccess access, CancellationToken cancellationToken = default) - { - return OpenStreamAsync(access, FileShare.None, cancellationToken); - } - - /// - public virtual Task OpenStreamAsync(FileAccess access, FileShare share = FileShare.None, CancellationToken cancellationToken = default) - { - var stream = File.Open(Path, FileMode.Open, access, share); - return Task.FromResult(stream); - } - } -} diff --git a/src/Files.App.Storage/Storables/NativeStorage/NativeFolder.cs b/src/Files.App.Storage/Storables/NativeStorage/NativeFolder.cs deleted file mode 100644 index e364cfb3b8c2..000000000000 --- a/src/Files.App.Storage/Storables/NativeStorage/NativeFolder.cs +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System.IO; -using System.Runtime.CompilerServices; - -namespace Files.App.Storage.Storables -{ - /// - public class NativeFolder : NativeStorable, ILocatableFolder, IModifiableFolder, IMutableFolder, IFolderExtended, INestedFolder, IDirectCopy, IDirectMove - { - public NativeFolder(DirectoryInfo directoryInfo, string? name = null) - : base(directoryInfo, name) - { - } - - public NativeFolder(string path, string? name = null) - : this(new DirectoryInfo(path), name) - { - } - - /// - public virtual Task GetFileAsync(string fileName, CancellationToken cancellationToken = default) - { - var path = System.IO.Path.Combine(Path, fileName); - - if (!File.Exists(path)) - throw new FileNotFoundException(); - - return Task.FromResult(new NativeFile(path)); - } - - /// - public virtual Task GetFolderAsync(string folderName, CancellationToken cancellationToken = default) - { - var path = System.IO.Path.Combine(Path, folderName); - if (!Directory.Exists(path)) - throw new FileNotFoundException(); - - return Task.FromResult(new NativeFolder(path)); - } - - /// - public virtual async IAsyncEnumerable GetItemsAsync(StorableKind kind = StorableKind.All, [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - if (kind == StorableKind.Files) - { - foreach (var item in Directory.EnumerateFiles(Path)) - yield return new NativeFile(item); - } - else if (kind == StorableKind.Folders) - { - foreach (var item in Directory.EnumerateDirectories(Path)) - yield return new NativeFolder(item); - } - else - { - foreach (var item in Directory.EnumerateFileSystemEntries(Path)) - { - if (File.Exists(item)) - yield return new NativeFile(item); - else - yield return new NativeFolder(item); - } - } - - await Task.CompletedTask; - } - - /// - public virtual Task DeleteAsync(INestedStorable item, bool permanently = false, CancellationToken cancellationToken = default) - { - _ = permanently; - - if (item is ILocatableFile locatableFile) - { - File.Delete(locatableFile.Path); - } - else if (item is ILocatableFolder locatableFolder) - { - Directory.Delete(locatableFolder.Path, true); - } - else - throw new ArgumentException($"Could not delete {item}."); - - return Task.CompletedTask; - } - - /// - public virtual async Task CreateCopyOfAsync(INestedStorable itemToCopy, bool overwrite = default, CancellationToken cancellationToken = default) - { - if (itemToCopy is IFile sourceFile) - { - if (itemToCopy is ILocatableFile sourceLocatableFile) - { - var newPath = System.IO.Path.Combine(Path, itemToCopy.Name); - File.Copy(sourceLocatableFile.Path, newPath, overwrite); - - return new NativeFile(newPath); - } - - var copiedFile = await CreateFileAsync(itemToCopy.Name, overwrite, cancellationToken); - await sourceFile.CopyContentsToAsync(copiedFile, cancellationToken); - - return copiedFile; - } - else if (itemToCopy is IFolder sourceFolder) - { - // TODO: Implement folder copy - _ = sourceFolder; - throw new NotSupportedException(); - } - - throw new ArgumentException($"Could not copy type {itemToCopy.GetType()}"); - } - - /// - public virtual async Task MoveFromAsync(INestedStorable itemToMove, IModifiableFolder source, bool overwrite = default, CancellationToken cancellationToken = default) - { - if (itemToMove is IFile sourceFile) - { - if (itemToMove is ILocatableFile sourceLocatableFile) - { - var newPath = System.IO.Path.Combine(Path, itemToMove.Name); - File.Move(sourceLocatableFile.Path, newPath, overwrite); - - return new NativeFile(newPath); - } - else - { - var copiedFile = await CreateFileAsync(itemToMove.Name, overwrite, cancellationToken); - await sourceFile.CopyContentsToAsync(copiedFile, cancellationToken); - await source.DeleteAsync(itemToMove, true, cancellationToken); - - return copiedFile; - } - } - else if (itemToMove is IFolder sourceFolder) - { - throw new NotImplementedException(); - } - - throw new ArgumentException($"Could not move type {itemToMove.GetType()}"); - } - - /// - public virtual async Task CreateFileAsync(string desiredName, bool overwrite = default, CancellationToken cancellationToken = default) - { - var path = System.IO.Path.Combine(Path, desiredName); - if (overwrite || !File.Exists(path)) - await File.Create(path).DisposeAsync(); - - return new NativeFile(path); - } - - /// - public virtual Task CreateFolderAsync(string desiredName, bool overwrite = default, CancellationToken cancellationToken = default) - { - var path = System.IO.Path.Combine(Path, desiredName); - if (overwrite) - Directory.Delete(path, true); - - _ = Directory.CreateDirectory(path); - return Task.FromResult(new NativeFolder(path)); - } - } -} diff --git a/src/Files.App.Storage/Storables/NativeStorage/NativeStorable.cs b/src/Files.App.Storage/Storables/NativeStorage/NativeStorable.cs deleted file mode 100644 index 1aab43b33a5a..000000000000 --- a/src/Files.App.Storage/Storables/NativeStorage/NativeStorable.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System.IO; - -namespace Files.App.Storage.Storables -{ - /// - public abstract class NativeStorable : ILocatableStorable, INestedStorable - where TStorage : FileSystemInfo - { - protected readonly TStorage storage; - - /// - public string Path { get; protected set; } - - /// - public string Name { get; protected set; } - - /// - public virtual string Id { get; } - - protected NativeStorable(TStorage storage, string? name = null) - { - this.storage = storage; - Path = storage.FullName; - Name = name ?? storage.Name; - Id = storage.FullName; - } - - /// - public virtual Task GetParentAsync(CancellationToken cancellationToken = default) - { - var parent = Directory.GetParent(Path); - if (parent is null) - return Task.FromResult(null); - - return Task.FromResult(new NativeFolder(parent)); - } - - /// - /// Formats a given . - /// - /// The path to format. - /// A formatted path. - protected static string FormatPath(string path) - { - path = path.Replace("file:///", string.Empty); - - if ('/' != System.IO.Path.DirectorySeparatorChar) - return path.Replace('/', System.IO.Path.DirectorySeparatorChar); - - if ('\\' != System.IO.Path.DirectorySeparatorChar) - return path.Replace('\\', System.IO.Path.DirectorySeparatorChar); - - return path; - } - } -} diff --git a/src/Files.App.Storage/Storables/NativeStorage/NativeStorageService.cs b/src/Files.App.Storage/Storables/NativeStorage/NativeStorageService.cs deleted file mode 100644 index a94d4f2b801a..000000000000 --- a/src/Files.App.Storage/Storables/NativeStorage/NativeStorageService.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.Shared.Helpers; -using System.IO; -using Windows.Storage; - -namespace Files.App.Storage.Storables -{ - /// - public sealed class NativeStorageService : IStorageService - { - /// - public Task GetFileAsync(string id, CancellationToken cancellationToken = default) - { - if (!File.Exists(id)) - throw new FileNotFoundException(); - - return Task.FromResult(new NativeFile(id)); - } - - /// - public async Task GetFolderAsync(string id, CancellationToken cancellationToken = default) - { - if (!Directory.Exists(id)) - throw new DirectoryNotFoundException(); - - // A special folder should use the localized name - if (PathHelpers.IsSpecialFolder(id)) - { - var storageFolder = await TryGetStorageFolderAsync(id); - return new NativeFolder(id, storageFolder?.DisplayName); - } - - return new NativeFolder(id); - - async Task TryGetStorageFolderAsync(string path) - { - try - { - return await StorageFolder.GetFolderFromPathAsync(path); - } - catch (Exception) - { - return null; - } - } - } - } -} diff --git a/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorable.cs b/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorable.cs deleted file mode 100644 index f730db243c85..000000000000 --- a/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorable.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.Shared.Helpers; -using Windows.Storage; - -namespace Files.App.Storage.Storables -{ - /// - public abstract class WindowsStorable : ILocatableStorable, INestedStorable - where TStorage : class, IStorageItem - { - private string? _computedId; - internal readonly TStorage storage; - - /// - public string Path { get; protected internal set; } - - /// - public string Name { get; protected internal set; } - - /// - public virtual string Id => _computedId ??= ChecksumHelpers.CalculateChecksumForPath(Path); - - protected internal WindowsStorable(TStorage storage) - { - this.storage = storage; - Path = storage.Path; - Name = storage.Name; - } - - /// - public abstract Task GetParentAsync(CancellationToken cancellationToken = default); - } -} diff --git a/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorageFile.cs b/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorageFile.cs deleted file mode 100644 index 15d856a34fe3..000000000000 --- a/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorageFile.cs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System.IO; -using Windows.Storage; - -namespace Files.App.Storage.Storables -{ - /// - public sealed class WindowsStorageFile : WindowsStorable, ILocatableFile, IModifiableFile, IFileExtended, INestedFile - { - public WindowsStorageFile(StorageFile storage) - : base(storage) - { - } - - /// - public Task OpenStreamAsync(FileAccess access, CancellationToken cancellationToken = default) - { - return OpenStreamAsync(access, FileShare.None, cancellationToken); - } - - /// - public async Task OpenStreamAsync(FileAccess access, FileShare share = FileShare.None, CancellationToken cancellationToken = default) - { - var fileAccessMode = GetFileAccessMode(access); - var storageOpenOptions = GetStorageOpenOptions(share); - - var winrtStreamTask = storage.OpenAsync(fileAccessMode, storageOpenOptions).AsTask(cancellationToken); - var winrtStream = await winrtStreamTask; - - return winrtStream.AsStream(); - } - - /// - public override async Task GetParentAsync(CancellationToken cancellationToken = default) - { - var parentFolderTask = storage.GetParentAsync().AsTask(cancellationToken); - var parentFolder = await parentFolderTask; - - return new WindowsStorageFolder(parentFolder); - } - - private static FileAccessMode GetFileAccessMode(FileAccess access) - { - return access switch - { - FileAccess.Read => FileAccessMode.Read, - FileAccess.Write => FileAccessMode.ReadWrite, - FileAccess.ReadWrite => FileAccessMode.ReadWrite, - _ => throw new ArgumentOutOfRangeException(nameof(access)) - }; - } - - private static StorageOpenOptions GetStorageOpenOptions(FileShare share) - { - return share switch - { - FileShare.Read => StorageOpenOptions.AllowOnlyReaders, - FileShare.Write => StorageOpenOptions.AllowReadersAndWriters, - FileShare.ReadWrite => StorageOpenOptions.AllowReadersAndWriters, - FileShare.Inheritable => StorageOpenOptions.None, - FileShare.Delete => StorageOpenOptions.None, - FileShare.None => StorageOpenOptions.None, - _ => throw new ArgumentOutOfRangeException(nameof(share)) - }; - } - } -} diff --git a/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorageFolder.cs b/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorageFolder.cs deleted file mode 100644 index e2467acc9ae9..000000000000 --- a/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorageFolder.cs +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System.Runtime.CompilerServices; -using Windows.Storage; - -namespace Files.App.Storage.Storables -{ - /// - public sealed class WindowsStorageFolder : WindowsStorable, ILocatableFolder, IFolderExtended, INestedFolder, IDirectCopy, IDirectMove - { - // TODO: Implement IMutableFolder - - public WindowsStorageFolder(StorageFolder storage) - : base(storage) - { - } - - /// - public async Task GetFileAsync(string fileName, CancellationToken cancellationToken = default) - { - var file = await storage.GetFileAsync(fileName).AsTask(cancellationToken); - return new WindowsStorageFile(file); - } - - /// - public async Task GetFolderAsync(string folderName, CancellationToken cancellationToken = default) - { - var folder = await storage.GetFolderAsync(folderName).AsTask(cancellationToken); - return new WindowsStorageFolder(folder); - } - - /// - public async IAsyncEnumerable GetItemsAsync(StorableKind kind = StorableKind.All, [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - switch (kind) - { - case StorableKind.Files: - { - var files = await storage.GetFilesAsync().AsTask(cancellationToken); - foreach (var item in files) - { - yield return new WindowsStorageFile(item); - } - - break; - } - - case StorableKind.Folders: - { - var folders = await storage.GetFoldersAsync().AsTask(cancellationToken); - foreach (var item in folders) - { - yield return new WindowsStorageFolder(item); - } - - break; - } - - case StorableKind.All: - { - var items = await storage.GetItemsAsync().AsTask(cancellationToken); - foreach (var item in items) - { - if (item is StorageFile storageFile) - yield return new WindowsStorageFile(storageFile); - - if (item is StorageFolder storageFolder) - yield return new WindowsStorageFolder(storageFolder); - } - - break; - } - - default: - yield break; - } - } - - /// - public Task DeleteAsync(INestedStorable item, bool permanently = default, CancellationToken cancellationToken = default) - { - return item switch - { - WindowsStorable storageFile => storageFile.storage - .DeleteAsync(GetWindowsStorageDeleteOption(permanently)) - .AsTask(cancellationToken), - - WindowsStorable storageFolder => storageFolder.storage - .DeleteAsync(GetWindowsStorageDeleteOption(permanently)) - .AsTask(cancellationToken), - - _ => throw new NotImplementedException() - }; - } - - /// - public async Task CreateCopyOfAsync(INestedStorable itemToCopy, bool overwrite = default, CancellationToken cancellationToken = default) - { - if (itemToCopy is WindowsStorable sourceFile) - { - var copiedFile = await sourceFile.storage.CopyAsync(storage, itemToCopy.Name, GetWindowsNameCollisionOption(overwrite)).AsTask(cancellationToken); - return new WindowsStorageFile(copiedFile); - } - - throw new ArgumentException($"Could not copy type {itemToCopy.GetType()}"); - } - - /// - public async Task MoveFromAsync(INestedStorable itemToMove, IModifiableFolder source, bool overwrite = default, CancellationToken cancellationToken = default) - { - if (itemToMove is WindowsStorable sourceFile) - { - await sourceFile.storage.MoveAsync(storage, itemToMove.Name, GetWindowsNameCollisionOption(overwrite)).AsTask(cancellationToken); - return new WindowsStorageFile(sourceFile.storage); - } - - throw new ArgumentException($"Could not copy type {itemToMove.GetType()}"); - } - - /// - public async Task CreateFileAsync(string desiredName, bool overwrite = default, CancellationToken cancellationToken = default) - { - var file = await storage.CreateFileAsync(desiredName, GetWindowsCreationCollisionOption(overwrite)).AsTask(cancellationToken); - return new WindowsStorageFile(file); - } - - /// - public async Task CreateFolderAsync(string desiredName, bool overwrite = default, CancellationToken cancellationToken = default) - { - var folder = await storage.CreateFolderAsync(desiredName, GetWindowsCreationCollisionOption(overwrite)).AsTask(cancellationToken); - return new WindowsStorageFolder(folder); - } - - /// - public override async Task GetParentAsync(CancellationToken cancellationToken = default) - { - var parentFolder = await storage.GetParentAsync().AsTask(cancellationToken); - return new WindowsStorageFolder(parentFolder); - } - - private static StorageDeleteOption GetWindowsStorageDeleteOption(bool permanently) - { - return permanently ? StorageDeleteOption.PermanentDelete : StorageDeleteOption.Default; - } - - private static NameCollisionOption GetWindowsNameCollisionOption(bool overwrite) - { - return overwrite ? NameCollisionOption.ReplaceExisting : NameCollisionOption.GenerateUniqueName; - } - - private static CreationCollisionOption GetWindowsCreationCollisionOption(bool overwrite) - { - return overwrite ? CreationCollisionOption.ReplaceExisting : CreationCollisionOption.OpenIfExists; - } - } -} diff --git a/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorageService.cs b/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorageService.cs deleted file mode 100644 index f642a7aed968..000000000000 --- a/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorageService.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Windows.Storage; - -namespace Files.App.Storage.Storables -{ - /// - internal sealed class WindowsStorageService : IStorageService - { - /// - public async Task GetFileAsync(string id, CancellationToken cancellationToken = default) - { - var file = await StorageFile.GetFileFromPathAsync(id).AsTask(cancellationToken); - return new WindowsStorageFile(file); - } - - /// - public async Task GetFolderAsync(string id, CancellationToken cancellationToken = default) - { - var folder = await StorageFolder.GetFolderFromPathAsync(id).AsTask(cancellationToken); - return new WindowsStorageFolder(folder); - } - } -} diff --git a/src/Files.App.Storage/Windows/Helpers/WindowsStorableHelpers.Icon.cs b/src/Files.App.Storage/Windows/Helpers/WindowsStorableHelpers.Icon.cs new file mode 100644 index 000000000000..01df95e5a916 --- /dev/null +++ b/src/Files.App.Storage/Windows/Helpers/WindowsStorableHelpers.Icon.cs @@ -0,0 +1,283 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using System.Collections.Concurrent; +using System.Runtime.InteropServices; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.Graphics.Gdi; +using Windows.Win32.Graphics.GdiPlus; +using Windows.Win32.System.Com; +using Windows.Win32.UI.Shell; +using Windows.Win32.UI.WindowsAndMessaging; + +namespace Files.App.Storage +{ + public static partial class WindowsStorableHelpers + { + // Fields + + private static (Guid Format, Guid Encorder)[]? GdiEncoders; + private static ConcurrentDictionary<(string, int, int), byte[]>? DllIconCache; + + // Methods + + /// + public static async Task GetThumbnailAsync(this IWindowsStorable storable, int size, SIIGBF options) + { + return await STATask.Run(() => + { + HRESULT hr = storable.TryGetThumbnail(size, options, out var thumbnailData).ThrowIfFailedOnDebug(); + return thumbnailData; + }, null); + } + + /// + /// Retrieves a thumbnail image data for the specified using . + /// + /// An object that implements and represents a shell item on Windows. + /// The desired size (in pixels) of the thumbnail (width and height are equal). + /// A combination of flags that specify how the thumbnail should be retrieved. + /// A byte array containing the thumbnail image in its native format (e.g., PNG, JPEG). + /// If the thumbnail is JPEG, this tries to decoded as a PNG instead because JPEG loses data. + public unsafe static HRESULT TryGetThumbnail(this IWindowsStorable storable, int size, SIIGBF options, out byte[]? thumbnailData) + { + thumbnailData = null; + + using ComPtr pShellItemImageFactory = default; + storable.ThisPtr->QueryInterface(IID.IID_IShellItemImageFactory, (void**)pShellItemImageFactory.GetAddressOf()); + if (pShellItemImageFactory.IsNull) + return HRESULT.E_NOINTERFACE; + + // Get HBITMAP + HBITMAP hBitmap = default; + HRESULT hr = pShellItemImageFactory.Get()->GetImage(new(size, size), options, &hBitmap); + if (hr.ThrowIfFailedOnDebug().Failed) + { + if (!hBitmap.IsNull) PInvoke.DeleteObject(hBitmap); + return hr; + } + + // Retrieve BITMAP data + BITMAP bmp = default; + if (PInvoke.GetObject(hBitmap, sizeof(BITMAP), &bmp) is 0) + { + if (!hBitmap.IsNull) PInvoke.DeleteObject(hBitmap); + return HRESULT.E_FAIL; + } + + // Allocate buffer for flipped pixel data + byte* flippedBits = (byte*)NativeMemory.AllocZeroed((nuint)(bmp.bmWidthBytes * bmp.bmHeight)); + + // Flip the image manually row by row + for (int y = 0; y < bmp.bmHeight; y++) + { + Buffer.MemoryCopy( + (byte*)bmp.bmBits + y * bmp.bmWidthBytes, + flippedBits + (bmp.bmHeight - y - 1) * bmp.bmWidthBytes, + bmp.bmWidthBytes, + bmp.bmWidthBytes + ); + } + + // Create GpBitmap from the flipped pixel data + GpBitmap* gpBitmap = default; + if (PInvoke.GdipCreateBitmapFromScan0(bmp.bmWidth, bmp.bmHeight, bmp.bmWidthBytes, PInvoke.PixelFormat32bppARGB, flippedBits, &gpBitmap) != Status.Ok) + { + if (flippedBits is not null) NativeMemory.Free(flippedBits); + if (!hBitmap.IsNull) PInvoke.DeleteObject(hBitmap); + return HRESULT.E_FAIL; + } + + if (!TryConvertGpBitmapToByteArray(gpBitmap, out thumbnailData)) + { + if (!hBitmap.IsNull) PInvoke.DeleteObject(hBitmap); + return HRESULT.E_FAIL; + } + + if (flippedBits is not null) NativeMemory.Free(flippedBits); + if (!hBitmap.IsNull) PInvoke.DeleteObject(hBitmap); + + return HRESULT.S_OK; + } + + public unsafe static HRESULT TryExtractImageFromDll(this IWindowsStorable storable, int size, int index, out byte[]? imageData) + { + DllIconCache ??= []; + imageData = null; + + if (storable.ToString() is not { } path) + return HRESULT.E_INVALIDARG; + + if (DllIconCache.TryGetValue((path, index, size), out var cachedImageData)) + { + imageData = cachedImageData; + return HRESULT.S_OK; + } + else + { + HICON hIcon = default; + HRESULT hr = default; + + fixed (char* pszPath = path) + hr = PInvoke.SHDefExtractIcon(pszPath, -1 * index, 0, &hIcon, null, (uint)size); + + if (hr.ThrowIfFailedOnDebug().Failed) + { + if (!hIcon.IsNull) PInvoke.DestroyIcon(hIcon); + return hr; + } + + // Convert to GpBitmap of GDI+ + GpBitmap* gpBitmap = default; + if (PInvoke.GdipCreateBitmapFromHICON(hIcon, &gpBitmap) is not Status.Ok) + { + if (!hIcon.IsNull) PInvoke.DestroyIcon(hIcon); + return HRESULT.E_FAIL; + } + + if (!TryConvertGpBitmapToByteArray(gpBitmap, out imageData) || imageData is null) + { + if (!hIcon.IsNull) PInvoke.DestroyIcon(hIcon); + return HRESULT.E_FAIL; + } + + DllIconCache[(path, index, size)] = imageData; + if (!hIcon.IsNull) PInvoke.DestroyIcon(hIcon); + + return HRESULT.S_OK; + } + } + + public unsafe static bool TryConvertGpBitmapToByteArray(GpBitmap* gpBitmap, out byte[]? imageData) + { + imageData = null; + + // Get an encoder for PNG + Guid format = Guid.Empty; + if (PInvoke.GdipGetImageRawFormat((GpImage*)gpBitmap, &format) is not Status.Ok) + { + if (gpBitmap is not null) PInvoke.GdipDisposeImage((GpImage*)gpBitmap); + return false; + } + + Guid encoder = GetEncoderClsid(format); + if (format == PInvoke.ImageFormatJPEG || encoder == Guid.Empty) + { + format = PInvoke.ImageFormatPNG; + encoder = GetEncoderClsid(format); + } + + using ComPtr pStream = default; + HRESULT hr = PInvoke.CreateStreamOnHGlobal(HGLOBAL.Null, true, pStream.GetAddressOf()); + if (hr.ThrowIfFailedOnDebug().Failed) + { + if (gpBitmap is not null) PInvoke.GdipDisposeImage((GpImage*)gpBitmap); + return false; + } + + if (PInvoke.GdipSaveImageToStream((GpImage*)gpBitmap, pStream.Get(), &encoder, (EncoderParameters*)null) is not Status.Ok) + { + if (gpBitmap is not null) PInvoke.GdipDisposeImage((GpImage*)gpBitmap); + return false; + } + + STATSTG stat = default; + hr = pStream.Get()->Stat(&stat, (uint)STATFLAG.STATFLAG_NONAME); + if (hr.ThrowIfFailedOnDebug().Failed) + { + if (gpBitmap is not null) PInvoke.GdipDisposeImage((GpImage*)gpBitmap); + return false; + } + + ulong statSize = stat.cbSize & 0xFFFFFFFF; + byte* RawThumbnailData = (byte*)NativeMemory.Alloc((nuint)statSize); + + pStream.Get()->Seek(0L, (SystemIO.SeekOrigin)STREAM_SEEK.STREAM_SEEK_SET, null); + hr = pStream.Get()->Read(RawThumbnailData, (uint)statSize); + if (hr.ThrowIfFailedOnDebug().Failed) + { + if (gpBitmap is not null) PInvoke.GdipDisposeImage((GpImage*)gpBitmap); + if (RawThumbnailData is not null) NativeMemory.Free(RawThumbnailData); + return false; + } + + imageData = new ReadOnlySpan(RawThumbnailData, (int)statSize / sizeof(byte)).ToArray(); + NativeMemory.Free(RawThumbnailData); + + return true; + + Guid GetEncoderClsid(Guid format) + { + foreach ((Guid Format, Guid Encoder) in GetGdiEncoders()) + if (Format == format) + return Encoder; + + return Guid.Empty; + } + + (Guid Format, Guid Encorder)[] GetGdiEncoders() + { + if (GdiEncoders is not null) + return GdiEncoders; + + if (PInvoke.GdipGetImageEncodersSize(out var numEncoders, out var size) is not Status.Ok) + return []; + + ImageCodecInfo* pImageCodecInfo = (ImageCodecInfo*)NativeMemory.Alloc(size); + + if (PInvoke.GdipGetImageEncoders(numEncoders, size, pImageCodecInfo) is not Status.Ok) + return []; + + ReadOnlySpan codecs = new(pImageCodecInfo, (int)numEncoders); + GdiEncoders = new (Guid Format, Guid Encoder)[codecs.Length]; + for (int index = 0; index < codecs.Length; index++) + GdiEncoders[index] = (codecs[index].FormatID, codecs[index].Clsid); + + return GdiEncoders; + } + } + + public unsafe static HRESULT TrySetFolderIcon(this IWindowsStorable storable, IWindowsStorable iconFile, int index) + { + if (storable.GetDisplayName() is not { } folderPath || + iconFile.GetDisplayName() is not { } filePath) + return HRESULT.E_INVALIDARG; + + fixed (char* pszFolderPath = folderPath, pszIconFile = filePath) + { + SHFOLDERCUSTOMSETTINGS settings = default; + settings.dwSize = (uint)sizeof(SHFOLDERCUSTOMSETTINGS); + settings.dwMask = PInvoke.FCSM_ICONFILE; + settings.pszIconFile = pszIconFile; + settings.cchIconFile = 0; + settings.iIconIndex = index; + + HRESULT hr = PInvoke.SHGetSetFolderCustomSettings(&settings, pszFolderPath, PInvoke.FCS_FORCEWRITE); + if (hr.ThrowIfFailedOnDebug().Failed) + return hr; + } + + return HRESULT.S_OK; + } + + public unsafe static HRESULT TrySetShortcutIcon(this IWindowsStorable storable, IWindowsStorable iconFile, int index) + { + if (iconFile.ToString() is not { } iconFilePath) + return HRESULT.E_INVALIDARG; + + using ComPtr pShellLink = default; + + HRESULT hr = storable.ThisPtr->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IShellLinkW, (void**)pShellLink.GetAddressOf()); + if (hr.ThrowIfFailedOnDebug().Failed) + return hr; + + fixed (char* pszIconFilePath = iconFilePath) + hr = pShellLink.Get()->SetIconLocation(iconFilePath, index); + if (hr.ThrowIfFailedOnDebug().Failed) + return hr; + + return HRESULT.S_OK; + } + } +} diff --git a/src/Files.App.Storage/Windows/Helpers/WindowsStorableHelpers.PowerShell.cs b/src/Files.App.Storage/Windows/Helpers/WindowsStorableHelpers.PowerShell.cs new file mode 100644 index 000000000000..ec3204a12141 --- /dev/null +++ b/src/Files.App.Storage/Windows/Helpers/WindowsStorableHelpers.PowerShell.cs @@ -0,0 +1,39 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Storage +{ + public static partial class WindowsStorableHelpers + { + public static async Task TrySetShortcutIconOnPowerShellAsElevatedAsync(this IWindowsStorable storable, IWindowsStorable iconFile, int index) + { + string psScript = + $@"$FilePath = '{storable}' + $IconFile = '{iconFile}' + $IconIndex = '{index}' + + $Shell = New-Object -ComObject WScript.Shell + $Shortcut = $Shell.CreateShortcut($FilePath) + $Shortcut.IconLocation = ""$IconFile, $IconIndex"" + $Shortcut.Save()"; + + var process = new Process() + { + StartInfo = new ProcessStartInfo() + { + FileName = "PowerShell.exe", + Arguments = $"-NoProfile -EncodedCommand {Convert.ToBase64String(System.Text.Encoding.Unicode.GetBytes(psScript))}", + Verb = "RunAs", + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden, + UseShellExecute = true + }, + }; + + process.Start(); + await process.WaitForExitAsync(); + + return true; + } + } +} diff --git a/src/Files.App.Storage/Windows/Helpers/WindowsStorableHelpers.Process.cs b/src/Files.App.Storage/Windows/Helpers/WindowsStorableHelpers.Process.cs new file mode 100644 index 000000000000..aa3948a27e7d --- /dev/null +++ b/src/Files.App.Storage/Windows/Helpers/WindowsStorableHelpers.Process.cs @@ -0,0 +1,27 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Windows.Win32; +using Windows.Win32.System.SystemInformation; + +namespace Files.App.Storage +{ + public unsafe static partial class WindowsStorableHelpers + { + public static bool IsOnArmProcessor() + { + IMAGE_FILE_MACHINE dwMachineType = default; + + // Assumes the current process token has "PROCESS_QUERY_INFORMATION" or "PROCESS_QUERY_LIMITED_INFORMATION" access right + bool fResult = PInvoke.IsWow64Process2(PInvoke.GetCurrentProcess(), null, &dwMachineType); + if (!fResult) + Debug.WriteLine($"{nameof(PInvoke.IsWow64Process2)} has failed."); + + return dwMachineType is + IMAGE_FILE_MACHINE.IMAGE_FILE_MACHINE_THUMB or + IMAGE_FILE_MACHINE.IMAGE_FILE_MACHINE_ARMNT or + IMAGE_FILE_MACHINE.IMAGE_FILE_MACHINE_ARM64 or + IMAGE_FILE_MACHINE.IMAGE_FILE_MACHINE_ARM; + } + } +} diff --git a/src/Files.App.Storage/Windows/Helpers/WindowsStorableHelpers.Shell.cs b/src/Files.App.Storage/Windows/Helpers/WindowsStorableHelpers.Shell.cs new file mode 100644 index 000000000000..41eb10877af7 --- /dev/null +++ b/src/Files.App.Storage/Windows/Helpers/WindowsStorableHelpers.Shell.cs @@ -0,0 +1,304 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.System.Com; +using Windows.Win32.System.SystemServices; +using Windows.Win32.UI.Shell; +using Windows.Win32.UI.Shell.Common; +using Windows.Win32.UI.Shell.PropertiesSystem; +using Windows.Win32.UI.WindowsAndMessaging; + +namespace Files.App.Storage +{ + public unsafe static partial class WindowsStorableHelpers + { + public static HRESULT GetPropertyValue(this IWindowsStorable storable, string propKey, out TValue value) + { + using ComPtr pShellItem2 = default; + HRESULT hr = storable.ThisPtr->QueryInterface(IID.IID_IShellItem2, (void**)pShellItem2.GetAddressOf()); + + PROPERTYKEY propertyKey = default; + fixed (char* pszPropertyKey = propKey) + hr = PInvoke.PSGetPropertyKeyFromName(pszPropertyKey, &propertyKey); + + if (typeof(TValue) == typeof(string)) + { + ComHeapPtr szPropertyValue = default; + hr = pShellItem2.Get()->GetString(&propertyKey, szPropertyValue.Get()); + value = (TValue)(object)szPropertyValue.Get()->ToString(); + + return hr; + } + if (typeof(TValue) == typeof(bool)) + { + bool fPropertyValue = false; + hr = pShellItem2.Get()->GetBool(&propertyKey, (BOOL*)&fPropertyValue); + value = Unsafe.As(ref fPropertyValue); + + return hr; + } + else + { + value = default!; + return HRESULT.E_FAIL; + } + } + + public static bool HasShellAttributes(this IWindowsStorable storable, SFGAO_FLAGS attributes) + { + return storable.ThisPtr->GetAttributes(attributes, out var dwRetAttributes).Succeeded && dwRetAttributes == attributes; + } + + public static string GetDisplayName(this IWindowsStorable storable, SIGDN options = SIGDN.SIGDN_FILESYSPATH) + { + using ComHeapPtr pszName = default; + HRESULT hr = storable.ThisPtr->GetDisplayName(options, (PWSTR*)pszName.GetAddressOf()); + + return hr.ThrowIfFailedOnDebug().Succeeded + ? new string((char*)pszName.Get()) // this is safe as it gets memcpy'd internally + : string.Empty; + } + + public static HRESULT TryInvokeContextMenuVerb(this IWindowsStorable storable, string verbName) + { + Debug.Assert(Thread.CurrentThread.GetApartmentState() is ApartmentState.STA); + + using ComPtr pContextMenu = default; + HRESULT hr = storable.ThisPtr->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IContextMenu, (void**)pContextMenu.GetAddressOf()); + HMENU hMenu = PInvoke.CreatePopupMenu(); + hr = pContextMenu.Get()->QueryContextMenu(hMenu, 0, 1, 0x7FFF, PInvoke.CMF_OPTIMIZEFORINVOKE); + + CMINVOKECOMMANDINFO cmici = default; + cmici.cbSize = (uint)sizeof(CMINVOKECOMMANDINFO); + cmici.nShow = (int)SHOW_WINDOW_CMD.SW_HIDE; + + fixed (byte* pszVerbName = Encoding.ASCII.GetBytes(verbName)) + { + cmici.lpVerb = new(pszVerbName); + hr = pContextMenu.Get()->InvokeCommand(cmici); + + if (!PInvoke.DestroyMenu(hMenu)) + return HRESULT.E_FAIL; + + return hr; + } + } + + public static HRESULT TryInvokeContextMenuVerbs(this IWindowsStorable storable, string[] verbNames, bool earlyReturnOnSuccess) + { + Debug.Assert(Thread.CurrentThread.GetApartmentState() is ApartmentState.STA); + + using ComPtr pContextMenu = default; + HRESULT hr = storable.ThisPtr->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IContextMenu, (void**)pContextMenu.GetAddressOf()); + HMENU hMenu = PInvoke.CreatePopupMenu(); + hr = pContextMenu.Get()->QueryContextMenu(hMenu, 0, 1, 0x7FFF, PInvoke.CMF_OPTIMIZEFORINVOKE); + + CMINVOKECOMMANDINFO cmici = default; + cmici.cbSize = (uint)sizeof(CMINVOKECOMMANDINFO); + cmici.nShow = (int)SHOW_WINDOW_CMD.SW_HIDE; + + foreach (var verbName in verbNames) + { + fixed (byte* pszVerbName = Encoding.ASCII.GetBytes(verbName)) + { + cmici.lpVerb = new(pszVerbName); + hr = pContextMenu.Get()->InvokeCommand(cmici); + + if (!PInvoke.DestroyMenu(hMenu)) + return HRESULT.E_FAIL; + + if (hr.Succeeded && earlyReturnOnSuccess) + return hr; + } + } + + return hr; + } + + public static HRESULT TryGetShellTooltip(this IWindowsStorable storable, out string? tooltip) + { + tooltip = null; + + using ComPtr pQueryInfo = default; + HRESULT hr = storable.ThisPtr->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IQueryInfo, (void**)pQueryInfo.GetAddressOf()); + if (hr.ThrowIfFailedOnDebug().Failed) + return hr; + + pQueryInfo.Get()->GetInfoTip((uint)QITIPF_FLAGS.QITIPF_DEFAULT, out var pszTip); + if (hr.ThrowIfFailedOnDebug().Failed) + return hr; + + tooltip = pszTip.ToString(); + PInvoke.CoTaskMemFree(pszTip); + + return HRESULT.S_OK; + } + + public static HRESULT TryPinFolderToQuickAccess(this IWindowsFolder @this) + { + HRESULT hr = default; + + using ComPtr pExecuteCommand = default; + using ComPtr pObjectWithSelection = default; + + hr = PInvoke.CoCreateInstance(CLSID.CLSID_PinToFrequentExecute, null, CLSCTX.CLSCTX_INPROC_SERVER, IID.IID_IExecuteCommand, (void**)pExecuteCommand.GetAddressOf()); + if (hr.ThrowIfFailedOnDebug().Failed) + return hr; + + using ComPtr pShellItemArray = default; + hr = PInvoke.SHCreateShellItemArrayFromShellItem(@this.ThisPtr, IID.IID_IShellItemArray, (void**)pShellItemArray.GetAddressOf()); + if (hr.ThrowIfFailedOnDebug().Failed) + return hr; + + hr = pExecuteCommand.Get()->QueryInterface(IID.IID_IObjectWithSelection, (void**)pObjectWithSelection.GetAddressOf()); + if (hr.ThrowIfFailedOnDebug().Failed) + return hr; + + hr = pObjectWithSelection.Get()->SetSelection(pShellItemArray.Get()); + if (hr.ThrowIfFailedOnDebug().Failed) + return hr; + + hr = pExecuteCommand.Get()->Execute(); + if (hr.ThrowIfFailedOnDebug().Failed) + return hr; + + return HRESULT.S_OK; + } + + public static HRESULT TryUnpinFolderFromQuickAccess(this IWindowsFolder @this) + { + HRESULT hr = default; + + using ComPtr pExecuteCommand = default; + using ComPtr pObjectWithSelection = default; + + hr = PInvoke.CoCreateInstance(CLSID.CLSID_UnPinFromFrequentExecute, null, CLSCTX.CLSCTX_INPROC_SERVER, IID.IID_IExecuteCommand, (void**)pExecuteCommand.GetAddressOf()); + if (hr.ThrowIfFailedOnDebug().Failed) + return hr; + + using ComPtr pShellItemArray = default; + hr = PInvoke.SHCreateShellItemArrayFromShellItem(@this.ThisPtr, IID.IID_IShellItemArray, (void**)pShellItemArray.GetAddressOf()); + if (hr.ThrowIfFailedOnDebug().Failed) + return hr; + + hr = pExecuteCommand.Get()->QueryInterface(IID.IID_IObjectWithSelection, (void**)pObjectWithSelection.GetAddressOf()); + if (hr.ThrowIfFailedOnDebug().Failed) + return hr; + + hr = pObjectWithSelection.Get()->SetSelection(pShellItemArray.Get()); + if (hr.ThrowIfFailedOnDebug().Failed) + return hr; + + hr = pExecuteCommand.Get()->Execute(); + if (hr.ThrowIfFailedOnDebug().Failed) + return hr; + + return HRESULT.S_OK; + } + + public static IEnumerable GetShellNewItems(this IWindowsFolder @this) + { + HRESULT hr = default; + + IContextMenu* pNewMenu = default; + using ComPtr pShellExtInit = default; + using ComPtr pContextMenu2 = default; + + hr = PInvoke.CoCreateInstance(CLSID.CLSID_NewMenu, null, CLSCTX.CLSCTX_INPROC_SERVER, IID.IID_IContextMenu, (void**)&pNewMenu); + if (hr.ThrowIfFailedOnDebug().Failed) + return []; + + hr = pNewMenu->QueryInterface(IID.IID_IContextMenu2, (void**)pContextMenu2.GetAddressOf()); + if (hr.ThrowIfFailedOnDebug().Failed) + return []; + + hr = pNewMenu->QueryInterface(IID.IID_IShellExtInit, (void**)pShellExtInit.GetAddressOf()); + if (hr.ThrowIfFailedOnDebug().Failed) + return []; + + @this.ShellNewMenu = pNewMenu; + + ITEMIDLIST* pFolderPidl = default; + hr = PInvoke.SHGetIDListFromObject((IUnknown*)@this.ThisPtr, &pFolderPidl); + if (hr.ThrowIfFailedOnDebug().Failed) + return []; + + hr = pShellExtInit.Get()->Initialize(pFolderPidl, null, default); + if (hr.ThrowIfFailedOnDebug().Failed) + return []; + + // Inserts "New (&W)" + HMENU hMenu = PInvoke.CreatePopupMenu(); + hr = pNewMenu->QueryContextMenu(hMenu, 0, 1, 256, 0); + if (hr.ThrowIfFailedOnDebug().Failed) + return []; + + // Invokes CNewMenu::_InitMenuPopup(), which populates the hSubMenu + HMENU hSubMenu = PInvoke.GetSubMenu(hMenu, 0); + hr = pContextMenu2.Get()->HandleMenuMsg(PInvoke.WM_INITMENUPOPUP, (WPARAM)(nuint)hSubMenu.Value, 0); + if (hr.ThrowIfFailedOnDebug().Failed) + return []; + + uint dwCount = unchecked((uint)PInvoke.GetMenuItemCount(hSubMenu)); + if (dwCount is unchecked((uint)-1)) + return []; + + // Enumerates and populates the list + List shellNewItems = []; + for (uint dwIndex = 0; dwIndex < dwCount; dwIndex++) + { + MENUITEMINFOW mii = default; + mii.cbSize = (uint)sizeof(MENUITEMINFOW); + mii.fMask = MENU_ITEM_MASK.MIIM_STRING | MENU_ITEM_MASK.MIIM_ID | MENU_ITEM_MASK.MIIM_STATE; + mii.dwTypeData = (char*)NativeMemory.Alloc(256U); + mii.cch = 256; + + if (PInvoke.GetMenuItemInfo(hSubMenu, dwIndex, true, &mii)) + { + shellNewItems.Add(new() + { + Id = mii.wID, + Name = mii.dwTypeData.ToString(), + Type = (WindowsContextMenuType)mii.fState, + }); + } + + NativeMemory.Free(mii.dwTypeData); + } + + return shellNewItems; + } + + public static bool InvokeShellNewItem(this IWindowsFolder @this, WindowsContextMenuItem item) + { + HRESULT hr = default; + + if (@this.ShellNewMenu is null) + { + IContextMenu* pNewMenu = default; + + hr = PInvoke.CoCreateInstance(CLSID.CLSID_NewMenu, null, CLSCTX.CLSCTX_INPROC_SERVER, IID.IID_IContextMenu, (void**)&pNewMenu); + if (hr.ThrowIfFailedOnDebug().Failed) + return false; + + @this.ShellNewMenu = pNewMenu; + } + + CMINVOKECOMMANDINFO cmici = default; + cmici.cbSize = (uint)sizeof(CMINVOKECOMMANDINFO); + cmici.lpVerb = (PCSTR)(byte*)item.Id; + cmici.nShow = (int)SHOW_WINDOW_CMD.SW_SHOWNORMAL; + + hr = @this.ShellNewMenu->InvokeCommand(&cmici); + if (hr.ThrowIfFailedOnDebug().Failed) + return false; + + return false; + } + } +} diff --git a/src/Files.App.Storage/Windows/Helpers/WindowsStorableHelpers.Storage.cs b/src/Files.App.Storage/Windows/Helpers/WindowsStorableHelpers.Storage.cs new file mode 100644 index 000000000000..d3ad8823351a --- /dev/null +++ b/src/Files.App.Storage/Windows/Helpers/WindowsStorableHelpers.Storage.cs @@ -0,0 +1,95 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.Storage.FileSystem; +using Windows.Win32.UI.Shell; + +namespace Files.App.Storage +{ + public unsafe static partial class WindowsStorableHelpers + { + public static bool TryGetFileAttributes(this IWindowsStorable storable, out FILE_FLAGS_AND_ATTRIBUTES attributes) + { + attributes = (FILE_FLAGS_AND_ATTRIBUTES)PInvoke.GetFileAttributes(storable.GetDisplayName()); + + if ((uint)attributes is PInvoke.INVALID_FILE_ATTRIBUTES) + { + attributes = 0; + return false; + } + else + { + return true; + } + } + + public static bool TrySetFileAttributes(this IWindowsStorable storable, FILE_FLAGS_AND_ATTRIBUTES attributes) + { + if (attributes is FILE_FLAGS_AND_ATTRIBUTES.FILE_ATTRIBUTE_COMPRESSED) + return storable.TryToggleFileCompressedAttribute(true); + + if (!storable.TryGetFileAttributes(out var previousAttributes)) + return false; + return PInvoke.SetFileAttributes(storable.GetDisplayName(), previousAttributes | attributes); + } + + public static bool TryUnsetFileAttributes(this IWindowsStorable storable, FILE_FLAGS_AND_ATTRIBUTES attributes) + { + if (attributes is FILE_FLAGS_AND_ATTRIBUTES.FILE_ATTRIBUTE_COMPRESSED) + return storable.TryToggleFileCompressedAttribute(false); + + if (!storable.TryGetFileAttributes(out var previousAttributes)) + return false; + return PInvoke.SetFileAttributes(storable.GetDisplayName(), previousAttributes & ~attributes); + } + + public static bool TryToggleFileCompressedAttribute(this IWindowsStorable storable, bool value) + { + // GENERIC_READ | GENERIC_WRITE flags are needed here + // FILE_FLAG_BACKUP_SEMANTICS is used to open directories + using var hFile = PInvoke.CreateFile( + storable.GetDisplayName(), + (uint)(FILE_ACCESS_RIGHTS.FILE_GENERIC_READ | FILE_ACCESS_RIGHTS.FILE_GENERIC_WRITE | FILE_ACCESS_RIGHTS.FILE_WRITE_ATTRIBUTES), + FILE_SHARE_MODE.FILE_SHARE_READ | FILE_SHARE_MODE.FILE_SHARE_WRITE, + lpSecurityAttributes: null, + FILE_CREATION_DISPOSITION.OPEN_EXISTING, + FILE_FLAGS_AND_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL | FILE_FLAGS_AND_ATTRIBUTES.FILE_FLAG_BACKUP_SEMANTICS, + hTemplateFile: null); + + if (hFile.IsInvalid) + return false; + + var bytesReturned = 0u; + var compressionFormat = value + ? COMPRESSION_FORMAT.COMPRESSION_FORMAT_DEFAULT + : COMPRESSION_FORMAT.COMPRESSION_FORMAT_NONE; + + var result = PInvoke.DeviceIoControl( + new(hFile.DangerousGetHandle()), + PInvoke.FSCTL_SET_COMPRESSION, + &compressionFormat, + sizeof(ushort), + null, + 0u, + &bytesReturned); + + return result; + } + + public static bool TryShowFormatDriveDialog(HWND hWnd, uint driveLetterIndex, SHFMT_ID id, SHFMT_OPT options) + { + // NOTE: This calls an undocumented elevatable COM class, shell32.dll!CFormatEngine so this doesn't need to be elevated beforehand. + var result = PInvoke.SHFormatDrive(hWnd, driveLetterIndex, id, options); + return result is 0xFFFF; + } + + public static bool TryRenameVolumeLabel(string path, string newLabel) + { + // TODO: Use shell32.dll!CMountPointRename (CLSID: 60173D16-A550-47f0-A14B-C6F9E4DA0831, IID: 92F8D886-AB61-4113-BD4F-2E894397386F) + + return false; + } + } +} diff --git a/src/Files.App.Storage/Windows/IWindowsFile.cs b/src/Files.App.Storage/Windows/IWindowsFile.cs new file mode 100644 index 000000000000..43f30155f907 --- /dev/null +++ b/src/Files.App.Storage/Windows/IWindowsFile.cs @@ -0,0 +1,9 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Storage +{ + public interface IWindowsFile : IWindowsStorable, IChildFile + { + } +} diff --git a/src/Files.App.Storage/Windows/IWindowsFolder.cs b/src/Files.App.Storage/Windows/IWindowsFolder.cs new file mode 100644 index 000000000000..92160da97f48 --- /dev/null +++ b/src/Files.App.Storage/Windows/IWindowsFolder.cs @@ -0,0 +1,15 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Windows.Win32.UI.Shell; + +namespace Files.App.Storage +{ + public unsafe interface IWindowsFolder : IWindowsStorable, IChildFolder + { + /// + /// Gets or sets the cached for the ShellNew context menu. + /// + public IContextMenu* ShellNewMenu { get; set; } + } +} diff --git a/src/Files.App.Storage/Windows/IWindowsStorable.cs b/src/Files.App.Storage/Windows/IWindowsStorable.cs new file mode 100644 index 000000000000..c79ef3ba66cb --- /dev/null +++ b/src/Files.App.Storage/Windows/IWindowsStorable.cs @@ -0,0 +1,14 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Windows.Win32.UI.Shell; + +namespace Files.App.Storage +{ + public unsafe interface IWindowsStorable : IStorableChild, IEquatable, IDisposable + { + IShellItem* ThisPtr { get; set; } + + IContextMenu* ContextMenu { get; set; } + } +} diff --git a/src/Files.App.Storage/Windows/Managers/JumpListDestinationType.cs b/src/Files.App.Storage/Windows/Managers/JumpListDestinationType.cs new file mode 100644 index 000000000000..b811e47d0625 --- /dev/null +++ b/src/Files.App.Storage/Windows/Managers/JumpListDestinationType.cs @@ -0,0 +1,11 @@ +namespace Files.App.Storage +{ + public enum JumpListDestinationType + { + Pinned, + + Recent, + + Frequent, + } +} diff --git a/src/Files.App.Storage/Windows/Managers/JumpListItem.cs b/src/Files.App.Storage/Windows/Managers/JumpListItem.cs new file mode 100644 index 000000000000..ff20f2229405 --- /dev/null +++ b/src/Files.App.Storage/Windows/Managers/JumpListItem.cs @@ -0,0 +1,10 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Storage +{ + public partial class JumpListItem + { + + } +} diff --git a/src/Files.App.Storage/Windows/Managers/JumpListItemType.cs b/src/Files.App.Storage/Windows/Managers/JumpListItemType.cs new file mode 100644 index 000000000000..4fa801b68154 --- /dev/null +++ b/src/Files.App.Storage/Windows/Managers/JumpListItemType.cs @@ -0,0 +1,12 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Storage +{ + public enum JumpListItemType + { + Item, + + Separator, + } +} diff --git a/src/Files.App.Storage/Windows/Managers/JumpListManager.cs b/src/Files.App.Storage/Windows/Managers/JumpListManager.cs new file mode 100644 index 000000000000..ddee40eb69d4 --- /dev/null +++ b/src/Files.App.Storage/Windows/Managers/JumpListManager.cs @@ -0,0 +1,33 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Storage +{ + public unsafe class JumpListManager : IDisposable + { + public string AppId { get; } + + public JumpListManager(string appId) + { + if (string.IsNullOrEmpty(appId)) + throw new ArgumentException("App ID cannot be null or empty.", nameof(appId)); + + AppId = appId; + //_jumpList = new ConcurrentDictionary(); + } + + public IEnumerable GetAutomaticDestinations() + { + return []; + } + + public IEnumerable GetCustomDestinations() + { + return []; + } + + public void Dispose() + { + } + } +} diff --git a/src/Files.App.Storage/Windows/Managers/STATask.cs b/src/Files.App.Storage/Windows/Managers/STATask.cs new file mode 100644 index 000000000000..3d968fe6beb3 --- /dev/null +++ b/src/Files.App.Storage/Windows/Managers/STATask.cs @@ -0,0 +1,166 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.Extensions.Logging; +using Windows.Win32; + +namespace Files.App.Storage +{ + /// + /// Represents a work scheduled to execute on a STA thread. + /// + public partial class STATask + { + /// + /// Schedules the specified work to execute in a new background thread initialized with STA state. + /// + /// The work to execute in the STA thread. + /// A logger to capture any exception that occurs during execution. + /// A that represents the work scheduled to execute in the STA thread. + public static Task Run(Action action, ILogger? logger) + { + var tcs = new TaskCompletionSource(); + + Thread thread = + new(() => + { + PInvoke.OleInitialize(); + + try + { + action(); + tcs.SetResult(); + } + catch (Exception ex) + { + tcs.SetResult(); + logger?.LogWarning(ex, "An exception was occurred during the execution within STA."); + } + finally + { + PInvoke.OleUninitialize(); + } + }); + + thread.IsBackground = true; + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + + return tcs.Task; + } + + /// + /// Schedules the specified work to execute in a new background thread initialized with STA state. + /// + /// The type of the result returned by the function. + /// The work to execute in the STA thread. + /// A logger to capture any exception that occurs during execution. + /// A that represents the work scheduled to execute in the STA thread. + public static Task Run(Func func, ILogger? logger) + { + var tcs = new TaskCompletionSource(); + + Thread thread = + new(() => + { + PInvoke.OleInitialize(); + + try + { + tcs.SetResult(func()); + } + catch (Exception ex) + { + tcs.SetResult(default!); + logger?.LogWarning(ex, "An exception was occurred during the execution within STA."); + } + finally + { + PInvoke.OleUninitialize(); + } + }); + + thread.IsBackground = true; + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + + return tcs.Task; + } + + /// + /// Schedules the specified work to execute in a new background thread initialized with STA state. + /// + /// The work to execute in the STA thread. + /// A logger to capture any exception that occurs during execution. + /// A that represents the work scheduled to execute in the STA thread. + public static Task Run(Func func, ILogger? logger) + { + var tcs = new TaskCompletionSource(); + + Thread thread = + new(async () => + { + PInvoke.OleInitialize(); + + try + { + await func(); + tcs.SetResult(); + } + catch (Exception ex) + { + tcs.SetResult(); + logger?.LogWarning(ex, "An exception was occurred during the execution within STA."); + } + finally + { + PInvoke.OleUninitialize(); + } + }); + + thread.IsBackground = true; + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + + return tcs.Task; + } + + /// + /// Schedules the specified work to execute in a new background thread initialized with STA state. + /// + /// The type of the result returned by the function. + /// The work to execute in the STA thread. + /// A logger to capture any exception that occurs during execution. + /// A that represents the work scheduled to execute in the STA thread. + public static Task Run(Func> func, ILogger? logger) + { + var tcs = new TaskCompletionSource(); + + Thread thread = + new(async () => + { + PInvoke.OleInitialize(); + + try + { + tcs.SetResult(await func()); + } + catch (Exception ex) + { + tcs.SetResult(default); + logger?.LogWarning(ex, "An exception was occurred during the execution within STA."); + } + finally + { + PInvoke.OleUninitialize(); + } + }); + + thread.IsBackground = true; + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + + return tcs.Task; + } + } +} diff --git a/src/Files.App.Storage/Windows/Managers/SystemTrayManager.cs b/src/Files.App.Storage/Windows/Managers/SystemTrayManager.cs new file mode 100644 index 000000000000..6c861a097f74 --- /dev/null +++ b/src/Files.App.Storage/Windows/Managers/SystemTrayManager.cs @@ -0,0 +1,154 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using System.Runtime.InteropServices; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.UI.Shell; +using Windows.Win32.UI.WindowsAndMessaging; + +namespace Files.App.Storage +{ + /// + /// Exposes a manager to create or delete an system tray icon you provide. + /// + public unsafe partial class SystemTrayManager : IDisposable + { + string _szWndClassName = null!; + string _szToolTip = null!; + HICON _hIcon = default; + private Guid _id; + private uint _dwCallbackMsgId; + Action _callback = null!; + + private HWND _hWnd = default; + private WNDPROC? _wndProc; + private uint _dwTaskbarRestartMsgId; + private bool _isShown; + + public static SystemTrayManager CreateIcon(string szWndClassName, string szToolTip, HICON hIcon, Guid id, uint dwCallbackMsgId, Action callback) + { + return new() + { + _szWndClassName = szWndClassName, + _szToolTip = szToolTip, + _hIcon = hIcon, + _id = id, + _dwCallbackMsgId = dwCallbackMsgId, + _callback = callback, + }; + } + + private bool CreateIcon() + { + if (_hWnd.IsNull) + _hWnd = CreateIconWindow(_szWndClassName); + + NOTIFYICONDATAW data = default; + data.cbSize = (uint)sizeof(NOTIFYICONDATAW); + data.hWnd = _hWnd; + data.uCallbackMessage = _dwCallbackMsgId; + data.guidItem = _id; + data.hIcon = _hIcon; + data.uFlags = NOTIFY_ICON_DATA_FLAGS.NIF_MESSAGE | NOTIFY_ICON_DATA_FLAGS.NIF_ICON | NOTIFY_ICON_DATA_FLAGS.NIF_TIP | NOTIFY_ICON_DATA_FLAGS.NIF_GUID | NOTIFY_ICON_DATA_FLAGS.NIF_SHOWTIP; + data.szTip = _szToolTip; + data.Anonymous.uVersion = 4u; + + if (_isShown) + { + return PInvoke.Shell_NotifyIcon(NOTIFY_ICON_MESSAGE.NIM_DELETE, &data); + } + else + { + bool fRes = PInvoke.Shell_NotifyIcon(NOTIFY_ICON_MESSAGE.NIM_DELETE, &data); + if (!fRes) return false; + + fRes = PInvoke.Shell_NotifyIcon(NOTIFY_ICON_MESSAGE.NIM_ADD, &data); + if (!fRes) return false; + + fRes = PInvoke.Shell_NotifyIcon(NOTIFY_ICON_MESSAGE.NIM_SETVERSION, &data); + if (!fRes) return false; + + _isShown = true; + + return true; + } + } + + public bool DeleteIcon() + { + if (_isShown) + { + NOTIFYICONDATAW data = default; + data.cbSize = (uint)sizeof(NOTIFYICONDATAW); + data.hWnd = _hWnd; + data.guidItem = _id; + data.uFlags = NOTIFY_ICON_DATA_FLAGS.NIF_GUID; + data.Anonymous.uVersion = 4u; + + return PInvoke.Shell_NotifyIcon(NOTIFY_ICON_MESSAGE.NIM_DELETE, &data); + } + + return true; + } + + private HWND CreateIconWindow(string szWndClassName) + { + fixed (char* pszWndClassName = szWndClassName) + { + _wndProc ??= new(WndProc); + + WNDCLASSEXW wndClass = default; + + wndClass.cbSize = (uint)sizeof(WNDCLASSEXW); + wndClass.style = WNDCLASS_STYLES.CS_DBLCLKS; + wndClass.hInstance = PInvoke.GetModuleHandle(default(PCWSTR)); + wndClass.lpszClassName = pszWndClassName; + wndClass.lpfnWndProc = (delegate* unmanaged[Stdcall]) + Marshal.GetFunctionPointerForDelegate(_wndProc); + + PInvoke.RegisterClassEx(&wndClass); + + _dwTaskbarRestartMsgId = PInvoke.RegisterWindowMessage("TaskbarCreated"); + + return PInvoke.CreateWindowEx( + WINDOW_EX_STYLE.WS_EX_LEFT, pszWndClassName, default, + WINDOW_STYLE.WS_OVERLAPPED, 0, 0, 1, 1, HWND.Null, HMENU.Null, HINSTANCE.Null, null); + } + } + + private LRESULT WndProc(HWND hWnd, uint uMsg, WPARAM wParam, LPARAM lParam) + { + if (uMsg == _dwCallbackMsgId) + { + _callback((uint)(lParam.Value & 0xFFFF)); + + return default; + } + else if (uMsg is PInvoke.WM_DESTROY) + { + DeleteIcon(); + + return default; + } + else if (uMsg == _dwTaskbarRestartMsgId) + { + DeleteIcon(); + CreateIcon(); + } + + return PInvoke.DefWindowProc(hWnd, uMsg, wParam, lParam); + } + + public void Dispose() + { + if (!_hWnd.IsNull) + PInvoke.DestroyWindow(_hWnd); + + if (!_hIcon.IsNull) + PInvoke.DestroyIcon(_hIcon); + + _wndProc = null; + } + } +} diff --git a/src/Files.App.Storage/Windows/Managers/TaskbarManager.cs b/src/Files.App.Storage/Windows/Managers/TaskbarManager.cs new file mode 100644 index 000000000000..1d8772239428 --- /dev/null +++ b/src/Files.App.Storage/Windows/Managers/TaskbarManager.cs @@ -0,0 +1,48 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.System.Com; +using Windows.Win32.UI.Shell; + +namespace Files.App.Storage +{ + public unsafe class TaskbarManager : IDisposable + { + private ComPtr pTaskbarList = default; + + private static TaskbarManager? _Default = null; + public static TaskbarManager Default { get; } = _Default ??= new TaskbarManager(); + + public TaskbarManager() + { + Guid CLSID_TaskbarList = typeof(TaskbarList).GUID; + Guid IID_ITaskbarList3 = ITaskbarList3.IID_Guid; + HRESULT hr = PInvoke.CoCreateInstance( + &CLSID_TaskbarList, + null, + CLSCTX.CLSCTX_INPROC_SERVER, + &IID_ITaskbarList3, + (void**)pTaskbarList.GetAddressOf()); + + if (hr.ThrowIfFailedOnDebug().Succeeded) + hr = pTaskbarList.Get()->HrInit().ThrowIfFailedOnDebug(); + } + + public HRESULT SetProgressValue(HWND hwnd, ulong ullCompleted, ulong ullTotal) + { + return pTaskbarList.Get()->SetProgressValue(hwnd, ullCompleted, ullTotal); + } + + public HRESULT SetProgressState(HWND hwnd, TBPFLAG tbpFlags) + { + return pTaskbarList.Get()->SetProgressState(hwnd, tbpFlags); + } + + public void Dispose() + { + pTaskbarList.Dispose(); + } + } +} diff --git a/src/Files.App.Storage/Windows/Managers/WindowsContextMenuItem.cs b/src/Files.App.Storage/Windows/Managers/WindowsContextMenuItem.cs new file mode 100644 index 000000000000..03af85e6a03e --- /dev/null +++ b/src/Files.App.Storage/Windows/Managers/WindowsContextMenuItem.cs @@ -0,0 +1,19 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Storage +{ + /// + /// Represents a Windows Shell ContextMenu item. + /// + public partial class WindowsContextMenuItem + { + public WindowsContextMenuType Type { get; set; } + + public uint Id { get; set; } + + public byte[]? Icon { get; set; } + + public string? Name { get; set; } + } +} diff --git a/src/Files.App.Storage/Windows/Managers/WindowsContextMenuType.cs b/src/Files.App.Storage/Windows/Managers/WindowsContextMenuType.cs new file mode 100644 index 000000000000..904a089fac19 --- /dev/null +++ b/src/Files.App.Storage/Windows/Managers/WindowsContextMenuType.cs @@ -0,0 +1,18 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Storage +{ + public enum WindowsContextMenuType + { + Normal = 0x00000000, + + Disabled = 0x00000003, + + Checked = 0x00000008, + + Highlighted = 0x00000080, + + Default = 0x00001000, + } +} diff --git a/src/Files.App.Storage/Windows/WindowsBulkOperations.cs b/src/Files.App.Storage/Windows/WindowsBulkOperations.cs new file mode 100644 index 000000000000..d995d20ea71b --- /dev/null +++ b/src/Files.App.Storage/Windows/WindowsBulkOperations.cs @@ -0,0 +1,173 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.Storage.FileSystem; +using Windows.Win32.System.Com; +using Windows.Win32.UI.Shell; + +namespace Files.App.Storage +{ + /// + /// Handles bulk file operations in Windows, such as copy, move, delete, create, and rename, supporting progress tracking and event notifications. + /// + public unsafe partial class WindowsBulkOperations : IDisposable + { + // Fields + + private readonly IFileOperation* _pFileOperation; + private readonly IFileOperationProgressSink* _pProgressSink; + private readonly uint _progressSinkCookie; + + // Events + + /// An event that is triggered when bulk operations are completed. + public event EventHandler? OperationsFinished; + + /// An event that is triggered when an item is copied during bulk operations. + public event EventHandler? ItemCopied; + + /// An event that is triggered when an item is deleted during bulk operations. + public event EventHandler? ItemDeleted; + + /// An event that is triggered when an item is moved during bulk operations. + public event EventHandler? ItemMoved; + + /// An event that is triggered when a new item is created. + public event EventHandler? ItemCreated; + + /// An event that is triggered when an item is renamed. + public event EventHandler? ItemRenamed; + + /// An event that occurs when an item is being copied during bulk operations. + public event EventHandler? ItemCopying; + + /// An event that is triggered when an item is being deleted. + public event EventHandler? ItemDeleting; + + /// An event that is triggered when an item is being moved in bulk operations. + public event EventHandler? ItemMoving; + + /// An event that is triggered when an item is being created in bulk operations. + public event EventHandler? ItemCreating; + + /// An event that is triggered when an item is being renamed. + public event EventHandler? ItemRenaming; + + /// An event that is triggered when operations start. + public event EventHandler? OperationsStarted; + + /// An event that is triggered to indicate progress updates. + public event ProgressChangedEventHandler? ProgressUpdated; + + // Constructor + + /// + /// Initializes a instance for file operations on Windows. + /// + /// Specifies the window handle that will own the file operation dialogs. + /// Defines the behavior of the file operation, such as allowing undo and suppressing directory confirmation. + public unsafe WindowsBulkOperations(HWND ownerHWnd = default, FILEOPERATION_FLAGS flags = FILEOPERATION_FLAGS.FOF_ALLOWUNDO | FILEOPERATION_FLAGS.FOF_NOCONFIRMMKDIR) + { + IFileOperation* pFileOperation = null; + + HRESULT hr = PInvoke.CoCreateInstance(CLSID.CLSID_FileOperation, null, CLSCTX.CLSCTX_LOCAL_SERVER, IID.IID_IFileOperation, (void**)&pFileOperation); + hr.ThrowIfFailedOnDebug(); + + _pFileOperation = pFileOperation; + + if (ownerHWnd != default) + hr = _pFileOperation->SetOwnerWindow(ownerHWnd).ThrowIfFailedOnDebug(); + + hr = _pFileOperation->SetOperationFlags(flags).ThrowIfFailedOnDebug(); + + _pProgressSink = (IFileOperationProgressSink*)WindowsBulkOperationsSink.Create(this); + hr = _pFileOperation->Advise(_pProgressSink, out var progressSinkCookie).ThrowIfFailedOnDebug(); + _progressSinkCookie = progressSinkCookie; + } + + /// + /// Queues a copy operation. + /// + /// + /// + /// + /// If this method succeeds, it returns . Otherwise, it returns an error code. + public unsafe HRESULT QueueCopyOperation(WindowsStorable targetItem, WindowsFolder destinationFolder, string? copyName) + { + fixed (char* pszCopyName = copyName) + return _pFileOperation->CopyItem(targetItem.ThisPtr, destinationFolder.ThisPtr, pszCopyName, _pProgressSink); + } + + /// + /// Queues a delete operation. + /// + /// + /// If this method succeeds, it returns . Otherwise, it returns an error code. + public unsafe HRESULT QueueDeleteOperation(WindowsStorable targetItem) + { + return _pFileOperation->DeleteItem(targetItem.ThisPtr, _pProgressSink); + } + + /// + /// Queues a move operation. + /// + /// + /// + /// + /// If this method succeeds, it returns . Otherwise, it returns an error code. + public unsafe HRESULT QueueMoveOperation(WindowsStorable targetItem, WindowsFolder destinationFolder, string? newName) + { + fixed (char* pszNewName = newName) + return _pFileOperation->MoveItem(targetItem.ThisPtr, destinationFolder.ThisPtr, pszNewName, null); + } + + /// + /// Queues a create operation. + /// + /// + /// + /// + /// + /// If this method succeeds, it returns . Otherwise, it returns an error code. + public unsafe HRESULT QueueCreateOperation(WindowsFolder destinationFolder, FILE_FLAGS_AND_ATTRIBUTES fileAttributes, string name, string? templateName) + { + fixed (char* pszName = name, pszTemplateName = templateName) + return _pFileOperation->NewItem(destinationFolder.ThisPtr, (uint)fileAttributes, pszName, pszTemplateName, _pProgressSink); + } + + /// + /// Queues a rename operation. + /// + /// + /// + /// If this method succeeds, it returns . Otherwise, it returns an error code. + public unsafe HRESULT QueueRenameOperation(WindowsStorable targetItem, string newName) + { + fixed (char* pszNewName = newName) + return _pFileOperation->RenameItem(targetItem.ThisPtr, pszNewName, _pProgressSink); + } + + /// + /// Performs the all queued operations. + /// + /// If this method succeeds, it returns . Otherwise, it returns an error code. + public unsafe HRESULT PerformAllOperations() + { + return _pFileOperation->PerformOperations(); + } + + // Disposer + + /// + public unsafe void Dispose() + { + if (_pProgressSink is not null) + _pFileOperation->Unadvise(_progressSinkCookie); + + _pFileOperation->Release(); + _pProgressSink->Release(); + } + } +} diff --git a/src/Files.App.Storage/Windows/WindowsBulkOperationsEventArgs.cs b/src/Files.App.Storage/Windows/WindowsBulkOperationsEventArgs.cs new file mode 100644 index 000000000000..771dd12dc5af --- /dev/null +++ b/src/Files.App.Storage/Windows/WindowsBulkOperationsEventArgs.cs @@ -0,0 +1,30 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Windows.Win32.Foundation; +using Windows.Win32.UI.Shell; + +namespace Files.App.Storage +{ + [DebuggerDisplay("{" + nameof(ToString) + "()}")] + public class WindowsBulkOperationsEventArgs(_TRANSFER_SOURCE_FLAGS flags = _TRANSFER_SOURCE_FLAGS.TSF_NORMAL, WindowsStorable? sourceItem = null, WindowsFolder? destinationFolder = null, WindowsStorable? newlyCreated = null, string? name = null, string? templateName = null, HRESULT result = default) + : EventArgs + { + public _TRANSFER_SOURCE_FLAGS Flags { get; set; } = flags; + + public WindowsStorable? SourceItem { get; set; } = sourceItem; + + public WindowsFolder? DestinationFolder { get; set; } = destinationFolder; + + public WindowsStorable? NewlyCreated { get; set; } = newlyCreated; + + public string? Name { get; set; } = name; + + public string? TemplateName { get; set; } = templateName; + + public HRESULT Result { get; protected set; } = result; + + public override string ToString() + => $"Hr:\"{Result}\"; Src:\"{SourceItem}\"; Dst:\"{DestinationFolder}\"; New:\"{NewlyCreated}\"; Name:\"{Name}\""; + } +} diff --git a/src/Files.App.Storage/Windows/WindowsBulkOperationsSink.Methods.cs b/src/Files.App.Storage/Windows/WindowsBulkOperationsSink.Methods.cs new file mode 100644 index 000000000000..f4b9f764b87c --- /dev/null +++ b/src/Files.App.Storage/Windows/WindowsBulkOperationsSink.Methods.cs @@ -0,0 +1,173 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Windows.Win32.Foundation; +using Windows.Win32.UI.Shell; + +namespace Files.App.Storage +{ + public sealed partial class WindowsBulkOperations : IDisposable + { + private unsafe partial struct WindowsBulkOperationsSink : IFileOperationProgressSink.Interface + { + public HRESULT StartOperations() + { + if (_operationsHandle.Target is WindowsBulkOperations operations) + { + operations.OperationsStarted?.Invoke(operations, EventArgs.Empty); + return HRESULT.S_OK; + } + + return HRESULT.E_FAIL; + } + + public HRESULT FinishOperations(HRESULT hrResult) + { + if (_operationsHandle.Target is WindowsBulkOperations operations) + { + operations.OperationsFinished?.Invoke(operations, new(result: hrResult)); + return HRESULT.S_OK; + } + + return HRESULT.E_FAIL; + } + + public unsafe HRESULT PreRenameItem(uint dwFlags, IShellItem* pSource, PCWSTR pszNewName) + { + if (_operationsHandle.Target is WindowsBulkOperations operations) + { + operations.ItemRenaming?.Invoke(operations, new((_TRANSFER_SOURCE_FLAGS)dwFlags, WindowsStorable.TryParse(pSource), null, null, pszNewName.ToString(), null, default)); + return HRESULT.S_OK; + } + + return HRESULT.E_FAIL; + } + + public unsafe HRESULT PostRenameItem(uint dwFlags, IShellItem* pSource, PCWSTR pszNewName, HRESULT hrRename, IShellItem* psiNewlyCreated) + { + if (_operationsHandle.Target is WindowsBulkOperations operations) + { + operations.ItemRenamed?.Invoke(operations, new((_TRANSFER_SOURCE_FLAGS)dwFlags, WindowsStorable.TryParse(pSource), null, WindowsStorable.TryParse(psiNewlyCreated), pszNewName.ToString(), null, hrRename)); + return HRESULT.S_OK; + } + + return HRESULT.E_FAIL; + } + + public unsafe HRESULT PreMoveItem(uint dwFlags, IShellItem* pSource, IShellItem* psiDestinationFolder, PCWSTR pszNewName) + { + if (_operationsHandle.Target is WindowsBulkOperations operations) + { + operations.ItemMoving?.Invoke(operations, new((_TRANSFER_SOURCE_FLAGS)dwFlags, WindowsStorable.TryParse(pSource), new WindowsFolder(psiDestinationFolder), null, pszNewName.ToString(), null, default)); + return HRESULT.S_OK; + } + + return HRESULT.E_FAIL; + } + + public unsafe HRESULT PostMoveItem(uint dwFlags, IShellItem* pSource, IShellItem* psiDestinationFolder, PCWSTR pszNewName, HRESULT hrMove, IShellItem* psiNewlyCreated) + { + if (_operationsHandle.Target is WindowsBulkOperations operations) + { + operations.ItemMoved?.Invoke(operations, new((_TRANSFER_SOURCE_FLAGS)dwFlags, WindowsStorable.TryParse(pSource), new WindowsFolder(psiDestinationFolder), WindowsStorable.TryParse(psiNewlyCreated), pszNewName.ToString(), null, hrMove)); + return HRESULT.S_OK; + } + + return HRESULT.E_FAIL; + } + + public unsafe HRESULT PreCopyItem(uint dwFlags, IShellItem* pSource, IShellItem* psiDestinationFolder, PCWSTR pszNewName) + { + if (_operationsHandle.Target is WindowsBulkOperations operations) + { + operations.ItemCopying?.Invoke(operations, new((_TRANSFER_SOURCE_FLAGS)dwFlags, WindowsStorable.TryParse(pSource), new WindowsFolder(psiDestinationFolder), null, pszNewName.ToString(), null, default)); + return HRESULT.S_OK; + } + + return HRESULT.E_FAIL; + } + + public unsafe HRESULT PostCopyItem(uint dwFlags, IShellItem* pSource, IShellItem* psiDestinationFolder, PCWSTR pszNewName, HRESULT hrCopy, IShellItem* psiNewlyCreated) + { + if (_operationsHandle.Target is WindowsBulkOperations operations) + { + operations.ItemCopied?.Invoke(operations, new((_TRANSFER_SOURCE_FLAGS)dwFlags, WindowsStorable.TryParse(pSource), new WindowsFolder(psiDestinationFolder), WindowsStorable.TryParse(psiNewlyCreated), pszNewName.ToString(), null, hrCopy)); + return HRESULT.S_OK; + } + + return HRESULT.E_FAIL; + } + + public unsafe HRESULT PreDeleteItem(uint dwFlags, IShellItem* pSource) + { + if (_operationsHandle.Target is WindowsBulkOperations operations) + { + operations.ItemDeleting?.Invoke(operations, new((_TRANSFER_SOURCE_FLAGS)dwFlags, WindowsStorable.TryParse(pSource), null, null, null, null, default)); + return HRESULT.S_OK; + } + + return HRESULT.E_FAIL; + } + + public unsafe HRESULT PostDeleteItem(uint dwFlags, IShellItem* pSource, HRESULT hrDelete, IShellItem* psiNewlyCreated) + { + if (_operationsHandle.Target is WindowsBulkOperations operations) + { + operations.ItemDeleted?.Invoke(operations, new((_TRANSFER_SOURCE_FLAGS)dwFlags, WindowsStorable.TryParse(pSource), null, WindowsStorable.TryParse(psiNewlyCreated), null, null, hrDelete)); + return HRESULT.S_OK; + } + + return HRESULT.E_FAIL; + } + + public unsafe HRESULT PreNewItem(uint dwFlags, IShellItem* psiDestinationFolder, PCWSTR pszNewName) + { + if (_operationsHandle.Target is WindowsBulkOperations operations) + { + operations.ItemCreating?.Invoke(operations, new((_TRANSFER_SOURCE_FLAGS)dwFlags, null, new WindowsFolder(psiDestinationFolder), null, pszNewName.ToString(), null, default)); + return HRESULT.S_OK; + } + + return HRESULT.E_FAIL; + } + + public unsafe HRESULT PostNewItem(uint dwFlags, IShellItem* psiDestinationFolder, PCWSTR pszNewName, PCWSTR pszTemplateName, uint dwFileAttributes, HRESULT hrNew, IShellItem* psiNewItem) + { + if (_operationsHandle.Target is WindowsBulkOperations operations) + { + operations.ItemCreated?.Invoke(operations, new((_TRANSFER_SOURCE_FLAGS)dwFlags, null, new WindowsFolder(psiDestinationFolder), WindowsStorable.TryParse(psiNewItem), pszNewName.ToString(), pszTemplateName.ToString(), hrNew)); + return HRESULT.S_OK; + } + + return HRESULT.E_FAIL; + } + + public HRESULT UpdateProgress(uint iWorkTotal, uint iWorkSoFar) + { + if (_operationsHandle.Target is WindowsBulkOperations operations) + { + var percentage = iWorkTotal is 0 ? 0 : iWorkSoFar * 100.0 / iWorkTotal; + operations.ProgressUpdated?.Invoke(operations, new((int)percentage, null)); + return HRESULT.S_OK; + } + + return HRESULT.E_FAIL; + } + + public HRESULT ResetTimer() + { + return HRESULT.E_NOTIMPL; + } + + public HRESULT PauseTimer() + { + return HRESULT.E_NOTIMPL; + } + + public HRESULT ResumeTimer() + { + return HRESULT.E_NOTIMPL; + } + } + } +} diff --git a/src/Files.App.Storage/Windows/WindowsBulkOperationsSink.VTable.cs b/src/Files.App.Storage/Windows/WindowsBulkOperationsSink.VTable.cs new file mode 100644 index 000000000000..95c309023f76 --- /dev/null +++ b/src/Files.App.Storage/Windows/WindowsBulkOperationsSink.VTable.cs @@ -0,0 +1,161 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Windows.Win32.Foundation; +using Windows.Win32.UI.Shell; +using WinRT.Interop; + +namespace Files.App.Storage +{ + public sealed partial class WindowsBulkOperations : IDisposable + { + private unsafe partial struct WindowsBulkOperationsSink + { + private static readonly void** _lpPopulatedVtbl = PopulateVTable(); + + private void** _lpVtbl; + private volatile int _refCount; + private GCHandle _operationsHandle; + + public static WindowsBulkOperationsSink* Create(WindowsBulkOperations operations) + { + WindowsBulkOperationsSink* operationsSink = (WindowsBulkOperationsSink*)NativeMemory.Alloc((nuint)sizeof(WindowsBulkOperationsSink)); + operationsSink->_lpVtbl = _lpPopulatedVtbl; + operationsSink->_refCount = 1; + operationsSink->_operationsHandle = GCHandle.Alloc(operations); + + return operationsSink; + } + + private static void** PopulateVTable() + { + void** vtbl = (void**)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(WindowsBulkOperationsSink), sizeof(void*) * 19); + vtbl[0] = (delegate* unmanaged)&Vtbl.QueryInterface; + vtbl[1] = (delegate* unmanaged)&Vtbl.AddRef; + vtbl[2] = (delegate* unmanaged)&Vtbl.Release; + vtbl[3] = (delegate* unmanaged)&Vtbl.StartOperations; + vtbl[4] = (delegate* unmanaged)&Vtbl.FinishOperations; + vtbl[5] = (delegate* unmanaged)&Vtbl.PreRenameItem; + vtbl[6] = (delegate* unmanaged)&Vtbl.PostRenameItem; + vtbl[7] = (delegate* unmanaged)&Vtbl.PreMoveItem; + vtbl[8] = (delegate* unmanaged)&Vtbl.PostMoveItem; + vtbl[9] = (delegate* unmanaged)&Vtbl.PreCopyItem; + vtbl[10] = (delegate* unmanaged)&Vtbl.PostCopyItem; + vtbl[11] = (delegate* unmanaged)&Vtbl.PreDeleteItem; + vtbl[12] = (delegate* unmanaged)&Vtbl.PostDeleteItem; + vtbl[13] = (delegate* unmanaged)&Vtbl.PreNewItem; + vtbl[14] = (delegate* unmanaged)&Vtbl.PostNewItem; + vtbl[15] = (delegate* unmanaged)&Vtbl.UpdateProgress; + vtbl[16] = (delegate* unmanaged)&Vtbl.ResetTimer; + vtbl[17] = (delegate* unmanaged)&Vtbl.PauseTimer; + vtbl[18] = (delegate* unmanaged)&Vtbl.ResumeTimer; + + return vtbl; + } + + private static class Vtbl + { + [UnmanagedCallersOnly] + public static HRESULT QueryInterface(WindowsBulkOperationsSink* @this, Guid* riid, void** ppv) + { + if (ppv is null) + return HRESULT.E_POINTER; + + if (riid->Equals(IID.IID_IUnknown) || riid->Equals(IFileOperationProgressSink.IID_Guid)) + { + Interlocked.Increment(ref @this->_refCount); + *ppv = @this; + return HRESULT.S_OK; + } + + return HRESULT.E_NOINTERFACE; + } + + [UnmanagedCallersOnly] + public static int AddRef(WindowsBulkOperationsSink* @this) + => Interlocked.Increment(ref @this->_refCount); + + [UnmanagedCallersOnly] + public static int Release(WindowsBulkOperationsSink* @this) + { + int newRefCount = Interlocked.Decrement(ref @this->_refCount); + if (newRefCount is 0) + { + if (@this->_operationsHandle.IsAllocated) + @this->_operationsHandle.Free(); + + NativeMemory.Free(@this); + } + + return newRefCount; + } + + [UnmanagedCallersOnly] + public static HRESULT StartOperations(WindowsBulkOperationsSink* @this) + => @this->StartOperations(); + + [UnmanagedCallersOnly] + public static HRESULT FinishOperations(WindowsBulkOperationsSink* @this, HRESULT hrResult) + => @this->FinishOperations(hrResult); + + [UnmanagedCallersOnly] + public static HRESULT PreRenameItem(WindowsBulkOperationsSink* @this, uint dwFlags, IShellItem* psiItem, PCWSTR pszNewName) + => @this->PreRenameItem(dwFlags, psiItem, pszNewName); + + [UnmanagedCallersOnly] + public static HRESULT PostRenameItem(WindowsBulkOperationsSink* @this, uint dwFlags, IShellItem* psiItem, PCWSTR pszNewName, HRESULT hrRename, IShellItem* psiNewlyCreated) + => @this->PostRenameItem(dwFlags, psiItem, pszNewName, hrRename, psiNewlyCreated); + + [UnmanagedCallersOnly] + public static HRESULT PreMoveItem(WindowsBulkOperationsSink* @this, uint dwFlags, IShellItem* psiItem, IShellItem* psiDestinationFolder, PCWSTR pszNewName) + => @this->PreMoveItem(dwFlags, psiItem, psiDestinationFolder, pszNewName); + + [UnmanagedCallersOnly] + public static HRESULT PostMoveItem(WindowsBulkOperationsSink* @this, uint dwFlags, IShellItem* psiItem, IShellItem* psiDestinationFolder, PCWSTR pszNewName, HRESULT hrMove, IShellItem* psiNewlyCreated) + => @this->PostMoveItem(dwFlags, psiItem, psiDestinationFolder, pszNewName, hrMove, psiNewlyCreated); + + [UnmanagedCallersOnly] + public static HRESULT PreCopyItem(WindowsBulkOperationsSink* @this, uint dwFlags, IShellItem* psiItem, IShellItem* psiDestinationFolder, PCWSTR pszNewName) + => @this->PreCopyItem(dwFlags, psiItem, psiDestinationFolder, pszNewName); + + [UnmanagedCallersOnly] + public static HRESULT PostCopyItem(WindowsBulkOperationsSink* @this, uint dwFlags, IShellItem* psiItem, IShellItem* psiDestinationFolder, PCWSTR pszNewName, HRESULT hrCopy, IShellItem* psiNewlyCreated) + => @this->PostCopyItem(dwFlags, psiItem, psiDestinationFolder, pszNewName, hrCopy, psiNewlyCreated); + + [UnmanagedCallersOnly] + public static HRESULT PreDeleteItem(WindowsBulkOperationsSink* @this, uint dwFlags, IShellItem* psiItem) + => @this->PreDeleteItem(dwFlags, psiItem); + + [UnmanagedCallersOnly] + public static HRESULT PostDeleteItem(WindowsBulkOperationsSink* @this, uint dwFlags, IShellItem* psiItem, HRESULT hrDelete, IShellItem* psiNewlyCreated) + => @this->PostDeleteItem(dwFlags, psiItem, hrDelete, psiNewlyCreated); + + [UnmanagedCallersOnly] + public static HRESULT PreNewItem(WindowsBulkOperationsSink* @this, uint dwFlags, IShellItem* psiDestinationFolder, PCWSTR pszNewName) + => @this->PreNewItem(dwFlags, psiDestinationFolder, pszNewName); + + [UnmanagedCallersOnly] + public static HRESULT PostNewItem(WindowsBulkOperationsSink* @this, uint dwFlags, IShellItem* psiDestinationFolder, PCWSTR pszNewName, PCWSTR pszTemplateName, uint dwFileAttributes, HRESULT hrNew, IShellItem* psiNewItem) + => @this->PostNewItem(dwFlags, psiDestinationFolder, pszNewName, pszTemplateName, dwFileAttributes, hrNew, psiNewItem); + + [UnmanagedCallersOnly] + public static HRESULT UpdateProgress(WindowsBulkOperationsSink* @this, uint iWorkTotal, uint iWorkSoFar) + => @this->UpdateProgress(iWorkTotal, iWorkSoFar); + + [UnmanagedCallersOnly] + public static HRESULT ResetTimer(WindowsBulkOperationsSink* @this) + => @this->ResetTimer(); + + [UnmanagedCallersOnly] + public static HRESULT PauseTimer(WindowsBulkOperationsSink* @this) + => @this->PauseTimer(); + + [UnmanagedCallersOnly] + public static HRESULT ResumeTimer(WindowsBulkOperationsSink* @this) + => @this->ResumeTimer(); + } + } + } +} diff --git a/src/Files.App.Storage/Windows/WindowsFile.cs b/src/Files.App.Storage/Windows/WindowsFile.cs new file mode 100644 index 000000000000..ef4b7d85d24b --- /dev/null +++ b/src/Files.App.Storage/Windows/WindowsFile.cs @@ -0,0 +1,22 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using System.IO; +using Windows.Win32.UI.Shell; + +namespace Files.App.Storage +{ + [DebuggerDisplay("{" + nameof(ToString) + "()}")] + public unsafe class WindowsFile : WindowsStorable, IWindowsFile + { + public WindowsFile(IShellItem* ptr) + { + ThisPtr = ptr; + } + + public Task OpenStreamAsync(FileAccess accessMode, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Files.App.Storage/Windows/WindowsFolder.cs b/src/Files.App.Storage/Windows/WindowsFolder.cs new file mode 100644 index 000000000000..510300fa5f6d --- /dev/null +++ b/src/Files.App.Storage/Windows/WindowsFolder.cs @@ -0,0 +1,85 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using System.Runtime.CompilerServices; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.System.SystemServices; +using Windows.Win32.UI.Shell; + +namespace Files.App.Storage +{ + [DebuggerDisplay("{" + nameof(ToString) + "()}")] + public unsafe class WindowsFolder : WindowsStorable, IWindowsFolder + { + /// + public IContextMenu* ShellNewMenu + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set; + } + + public WindowsFolder(IShellItem* ptr) + { + ThisPtr = ptr; + } + + public WindowsFolder(Guid folderId) + { + IShellItem* pShellItem = default; + + HRESULT hr = PInvoke.SHGetKnownFolderItem(&folderId, KNOWN_FOLDER_FLAG.KF_FLAG_DEFAULT, HANDLE.Null, IID.IID_IShellItem, (void**)&pShellItem); + if (hr.Failed) + { + fixed (char* pszShellPath = $"Shell:::{folderId:B}") + hr = PInvoke.SHCreateItemFromParsingName(pszShellPath, null, IID.IID_IShellItem, (void**)&pShellItem); + + // Invalid FOLDERID; this should never happen. + hr.ThrowOnFailure(); + } + + ThisPtr = pShellItem; + } + + public IAsyncEnumerable GetItemsAsync(StorableType type = StorableType.All, CancellationToken cancellationToken = default) + { + using ComPtr pEnumShellItems = default; + + HRESULT hr = ThisPtr->BindToHandler(null, BHID.BHID_EnumItems, IID.IID_IEnumShellItems, (void**)pEnumShellItems.GetAddressOf()); + if (hr.ThrowIfFailedOnDebug().Failed) + return Enumerable.Empty().ToAsyncEnumerable(); + + List childItems = []; + + IShellItem* pChildShellItem = null; + while ((hr = pEnumShellItems.Get()->Next(1, &pChildShellItem)) == HRESULT.S_OK) + { + bool isFolder = pChildShellItem->GetAttributes(SFGAO_FLAGS.SFGAO_FOLDER, out var dwAttributes).Succeeded && dwAttributes is SFGAO_FLAGS.SFGAO_FOLDER; + + if (type.HasFlag(StorableType.File) && !isFolder) + { + childItems.Add(new WindowsFile(pChildShellItem)); + } + else if (type.HasFlag(StorableType.Folder) && isFolder) + { + childItems.Add(new WindowsFolder(pChildShellItem)); + } + } + + if (hr.ThrowIfFailedOnDebug().Failed) + return Enumerable.Empty().ToAsyncEnumerable(); + + return childItems.ToAsyncEnumerable(); + } + + public override void Dispose() + { + base.Dispose(); + + if (ShellNewMenu is not null) ShellNewMenu->Release(); + } + } +} diff --git a/src/Files.App.Storage/Windows/WindowsStorable.cs b/src/Files.App.Storage/Windows/WindowsStorable.cs new file mode 100644 index 000000000000..d7ed69cc2f31 --- /dev/null +++ b/src/Files.App.Storage/Windows/WindowsStorable.cs @@ -0,0 +1,114 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using System.Runtime.CompilerServices; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.System.SystemServices; +using Windows.Win32.UI.Shell; + +namespace Files.App.Storage +{ + public unsafe abstract class WindowsStorable : IWindowsStorable + { + /// + public IShellItem* ThisPtr + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set; + } + + /// + public IContextMenu* ContextMenu + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set; + } + + /// + public string Id => this.GetDisplayName(SIGDN.SIGDN_FILESYSPATH); + + /// + public string Name => this.GetDisplayName(SIGDN.SIGDN_PARENTRELATIVEFORUI); + + public static WindowsStorable? TryParse(string szPath) + { + HRESULT hr = default; + IShellItem* pShellItem = null; + + fixed (char* pszPath = szPath) + hr = PInvoke.SHCreateItemFromParsingName(pszPath, null, IID.IID_IShellItem, (void**)&pShellItem); + + if (pShellItem is null) + return null; + + return TryParse(pShellItem); + } + + public static WindowsStorable? TryParse(IShellItem* pShellItem) + { + bool isFolder = pShellItem->GetAttributes(SFGAO_FLAGS.SFGAO_FOLDER, out var returnedAttributes).Succeeded && returnedAttributes is SFGAO_FLAGS.SFGAO_FOLDER; + + return isFolder ? new WindowsFolder(pShellItem) : new WindowsFile(pShellItem); + } + + /// + public unsafe Task GetParentAsync(CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + + IShellItem* pParentFolder = default; + HRESULT hr = ThisPtr->GetParent(&pParentFolder); + if (hr.ThrowIfFailedOnDebug().Failed) + return Task.FromResult(null); + + return Task.FromResult(new WindowsFolder(pParentFolder)); + } + + /// + public override bool Equals(object? obj) + { + return Equals(obj as IWindowsStorable); + } + + /// + public override int GetHashCode() + { + return HashCode.Combine(Id, Name); + } + + /// + public virtual void Dispose() + { + if (ThisPtr is not null) ThisPtr->Release(); + if (ContextMenu is not null) ContextMenu->Release(); + } + + /// + public override string ToString() + { + return this.GetDisplayName(); + } + + /// + public unsafe bool Equals(IWindowsStorable? other) + { + if (other is null) + return false; + + return ThisPtr->Compare(other.ThisPtr, (uint)_SICHINTF.SICHINT_DISPLAY, out int order).Succeeded && order is 0; + } + + public static bool operator ==(WindowsStorable left, WindowsStorable right) + => left.Equals(right); + + public static bool operator !=(WindowsStorable left, WindowsStorable right) + => !(left == right); + } +} diff --git a/src/Files.App/7z.dll b/src/Files.App/7z.dll index d5ff365f9610..f98686ebf6bd 100644 Binary files a/src/Files.App/7z.dll and b/src/Files.App/7z.dll differ diff --git a/src/Files.App/7z64.dll b/src/Files.App/7z64.dll index e9e5d23fcfe6..2e3af203c0db 100644 Binary files a/src/Files.App/7z64.dll and b/src/Files.App/7z64.dll differ diff --git a/src/Files.App/7zArm64.dll b/src/Files.App/7zArm64.dll index b5964663f334..5e751f4bd824 100644 Binary files a/src/Files.App/7zArm64.dll and b/src/Files.App/7zArm64.dll differ diff --git a/src/Files.App/Actions/BaseUIAction.cs b/src/Files.App/Actions/BaseUIAction.cs index 62e3f7c8b931..2491340ef723 100644 --- a/src/Files.App/Actions/BaseUIAction.cs +++ b/src/Files.App/Actions/BaseUIAction.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { diff --git a/src/Files.App/Actions/Content/Archives/Compress/BaseCompressArchiveAction.cs b/src/Files.App/Actions/Content/Archives/Compress/BaseCompressArchiveAction.cs index 7e1cb31c825e..670ff2a7116d 100644 --- a/src/Files.App/Actions/Content/Archives/Compress/BaseCompressArchiveAction.cs +++ b/src/Files.App/Actions/Content/Archives/Compress/BaseCompressArchiveAction.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { @@ -12,6 +12,15 @@ internal abstract class BaseCompressArchiveAction : BaseUIAction, IAction public abstract string Description { get; } + public virtual string ExtendedLabel + => Label; + + public virtual ActionCategory Category + => ActionCategory.Archive; + + public RichGlyph Glyph + => new(themedIconStyle: "App.ThemedIcons.Zip"); + public override bool IsExecutable => IsContextPageTypeAdaptedToCommand() && StorageArchiveService.CanCompress(context.SelectedItems) && @@ -51,6 +60,8 @@ private bool IsContextPageTypeAdaptedToCommand() return context.PageType != ContentPageTypes.RecycleBin && context.PageType != ContentPageTypes.ZipFolder && + context.PageType != ContentPageTypes.ReleaseNotes && + context.PageType != ContentPageTypes.Settings && context.PageType != ContentPageTypes.None; } diff --git a/src/Files.App/Actions/Content/Archives/Compress/CompressIntoArchiveAction.cs b/src/Files.App/Actions/Content/Archives/Compress/CompressIntoArchiveAction.cs index 99524e36fdea..cdf4d3c7fc65 100644 --- a/src/Files.App/Actions/Content/Archives/Compress/CompressIntoArchiveAction.cs +++ b/src/Files.App/Actions/Content/Archives/Compress/CompressIntoArchiveAction.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.Dialogs; using Microsoft.UI.Xaml.Controls; @@ -7,13 +7,14 @@ namespace Files.App.Actions { - internal sealed class CompressIntoArchiveAction : BaseCompressArchiveAction + [GeneratedRichCommand] + internal sealed partial class CompressIntoArchiveAction : BaseCompressArchiveAction { public override string Label - => "CreateArchive".GetLocalizedResource(); + => Strings.CreateArchive.GetLocalizedResource(); public override string Description - => "CompressIntoArchiveDescription".GetLocalizedResource(); + => Strings.CompressIntoArchiveDescription.GetLocalizedFormatResource(context.SelectedItems.Count); public CompressIntoArchiveAction() { @@ -40,10 +41,13 @@ public override async Task ExecuteAsync(object? parameter = null) sources, directory, dialog.FileName, + dialog.CPUThreads, dialog.Password, dialog.FileFormat, dialog.CompressionLevel, - dialog.SplittingSize); + dialog.SplittingSize, + dialog.DictionarySize, + dialog.WordSize); await StorageArchiveService.CompressAsync(compressionModel); } diff --git a/src/Files.App/Actions/Content/Archives/Compress/CompressIntoSevenZipAction.cs b/src/Files.App/Actions/Content/Archives/Compress/CompressIntoSevenZipAction.cs index 1d3414ab34c1..f2453eb1c53c 100644 --- a/src/Files.App/Actions/Content/Archives/Compress/CompressIntoSevenZipAction.cs +++ b/src/Files.App/Actions/Content/Archives/Compress/CompressIntoSevenZipAction.cs @@ -1,15 +1,19 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class CompressIntoSevenZipAction : BaseCompressArchiveAction + [GeneratedRichCommand] + internal sealed partial class CompressIntoSevenZipAction : BaseCompressArchiveAction { public override string Label - => string.Format("CreateNamedArchive".GetLocalizedResource(), $"{StorageArchiveService.GenerateArchiveNameFromItems(context.SelectedItems)}.7z"); - + => string.Format(Strings.CreateNamedArchive.GetLocalizedResource(), $"{StorageArchiveService.GenerateArchiveNameFromItems(context.SelectedItems)}.7z"); + + public override string ExtendedLabel + => Strings.CompressIntoSevenZip.GetLocalizedResource(); + public override string Description - => "CompressIntoSevenZipDescription".GetLocalizedResource(); + => Strings.CompressIntoSevenZipDescription.GetLocalizedFormatResource(context.SelectedItems.Count); public CompressIntoSevenZipAction() { @@ -26,6 +30,7 @@ public override Task ExecuteAsync(object? parameter = null) sources, directory, fileName, + Environment.ProcessorCount, fileFormat: ArchiveFormats.SevenZip); return StorageArchiveService.CompressAsync(compressionModel); diff --git a/src/Files.App/Actions/Content/Archives/Compress/CompressIntoZipAction.cs b/src/Files.App/Actions/Content/Archives/Compress/CompressIntoZipAction.cs index 72e5ded7b1eb..edc24ccc79ac 100644 --- a/src/Files.App/Actions/Content/Archives/Compress/CompressIntoZipAction.cs +++ b/src/Files.App/Actions/Content/Archives/Compress/CompressIntoZipAction.cs @@ -1,15 +1,19 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class CompressIntoZipAction : BaseCompressArchiveAction + [GeneratedRichCommand] + internal sealed partial class CompressIntoZipAction : BaseCompressArchiveAction { public override string Label - => string.Format("CreateNamedArchive".GetLocalizedResource(), $"{StorageArchiveService.GenerateArchiveNameFromItems(context.SelectedItems)}.zip"); + => string.Format(Strings.CreateNamedArchive.GetLocalizedResource(), $"{StorageArchiveService.GenerateArchiveNameFromItems(context.SelectedItems)}.zip"); + + public override string ExtendedLabel + => Strings.CompressIntoZip.GetLocalizedResource(); public override string Description - => "CompressIntoZipDescription".GetLocalizedResource(); + => Strings.CompressIntoZipDescription.GetLocalizedFormatResource(context.SelectedItems.Count); public CompressIntoZipAction() { @@ -26,6 +30,7 @@ public override Task ExecuteAsync(object? parameter = null) sources, directory, fileName, + Environment.ProcessorCount, fileFormat: ArchiveFormats.Zip); return StorageArchiveService.CompressAsync(compressionModel); diff --git a/src/Files.App/Actions/Content/Archives/Decompress/BaseDecompressArchiveAction.cs b/src/Files.App/Actions/Content/Archives/Decompress/BaseDecompressArchiveAction.cs index cfe8039995fb..3cf8b8123ebe 100644 --- a/src/Files.App/Actions/Content/Archives/Decompress/BaseDecompressArchiveAction.cs +++ b/src/Files.App/Actions/Content/Archives/Decompress/BaseDecompressArchiveAction.cs @@ -1,10 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.Dialogs; using Microsoft.UI.Xaml.Controls; using SevenZip; -using System.Diagnostics.CodeAnalysis; using System.Text; using Windows.Foundation.Metadata; using Windows.Storage; @@ -20,12 +19,15 @@ internal abstract class BaseDecompressArchiveAction : BaseUIAction, IAction public abstract string Description { get; } + public virtual ActionCategory Category + => ActionCategory.Archive; + public virtual HotKey HotKey => HotKey.None; public override bool IsExecutable => (IsContextPageTypeAdaptedToCommand() && - StorageArchiveService.CanDecompress(context.SelectedItems) || + CanDecompressSelectedItems() || CanDecompressInsideArchive()) && UIHelpers.CanShowDialog; @@ -43,6 +45,8 @@ protected bool IsContextPageTypeAdaptedToCommand() return context.PageType != ContentPageTypes.RecycleBin && context.PageType != ContentPageTypes.ZipFolder && + context.PageType != ContentPageTypes.ReleaseNotes && + context.PageType != ContentPageTypes.Settings && context.PageType != ContentPageTypes.None; } @@ -125,11 +129,17 @@ protected virtual bool CanDecompressInsideArchive() return false; } + protected virtual bool CanDecompressSelectedItems() + { + return StorageArchiveService.CanDecompress(context.SelectedItems); + } + protected virtual void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) { switch (e.PropertyName) { case nameof(IContentPageContext.SelectedItems): + case nameof(IContentPageContext.Folder): OnPropertyChanged(nameof(IsExecutable)); break; } diff --git a/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchive.cs b/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchive.cs index 41c8a17e5463..f3bbf4b87eaf 100644 --- a/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchive.cs +++ b/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchive.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.Dialogs; using Files.Shared.Helpers; @@ -11,18 +11,24 @@ namespace Files.App.Actions { - internal sealed class DecompressArchive : BaseDecompressArchiveAction + [GeneratedRichCommand] + internal sealed partial class DecompressArchiveAction : BaseDecompressArchiveAction { + private readonly IUserSettingsService UserSettingsService = Ioc.Default.GetRequiredService(); + public override string Label - => "ExtractFiles".GetLocalizedResource(); + => Strings.ExtractFiles.GetLocalizedResource(); public override string Description - => "DecompressArchiveDescription".GetLocalizedResource(); + => Strings.DecompressArchiveDescription.GetLocalizedFormatResource(context.SelectedItems.Count); + + public string AccessKey + => "E"; public override HotKey HotKey => new(Keys.E, KeyModifiers.Ctrl); - public DecompressArchive() + public DecompressArchiveAction() { } @@ -31,19 +37,33 @@ public override async Task ExecuteAsync(object? parameter = null) if (context.ShellPage is null) return; - BaseStorageFile archive = await StorageHelpers.ToStorageItem(context.SelectedItem?.ItemPath ?? string.Empty); + var archivePath = GetArchivePath(); + + if (string.IsNullOrEmpty(archivePath)) + return; + + BaseStorageFile archive = await StorageHelpers.ToStorageItem(archivePath); if (archive?.Path is null) return; var isArchiveEncrypted = await FilesystemTasks.Wrap(() => StorageArchiveService.IsEncryptedAsync(archive.Path)); + var isArchiveEncodingUndetermined = await FilesystemTasks.Wrap(() => StorageArchiveService.IsEncodingUndeterminedAsync(archive.Path)); + Encoding? detectedEncoding = null; + if (isArchiveEncodingUndetermined) + { + detectedEncoding = await FilesystemTasks.Wrap(() => StorageArchiveService.DetectEncodingAsync(archive.Path)); + } var password = string.Empty; + Encoding? encoding = null; DecompressArchiveDialog decompressArchiveDialog = new(); DecompressArchiveDialogViewModel decompressArchiveViewModel = new(archive) { IsArchiveEncrypted = isArchiveEncrypted, - ShowPathSelection = true + IsArchiveEncodingUndetermined = isArchiveEncodingUndetermined, + ShowPathSelection = true, + DetectedEncoding = detectedEncoding, }; decompressArchiveDialog.ViewModel = decompressArchiveViewModel; @@ -57,6 +77,8 @@ public override async Task ExecuteAsync(object? parameter = null) if (isArchiveEncrypted && decompressArchiveViewModel.Password is not null) password = Encoding.UTF8.GetString(decompressArchiveViewModel.Password); + encoding = decompressArchiveViewModel.SelectedEncoding.Encoding; + // Check if archive still exists if (!StorageHelpers.Exists(archive.Path)) return; @@ -64,6 +86,9 @@ public override async Task ExecuteAsync(object? parameter = null) BaseStorageFolder destinationFolder = decompressArchiveViewModel.DestinationFolder; string destinationFolderPath = decompressArchiveViewModel.DestinationFolderPath; + // Save extraction location for future use + SaveExtractionLocation(destinationFolderPath); + if (destinationFolder is null) { BaseStorageFolder parentFolder = await StorageHelpers.ToStorageItem(Path.GetDirectoryName(archive.Path) ?? string.Empty); @@ -72,7 +97,7 @@ public override async Task ExecuteAsync(object? parameter = null) // Operate decompress var result = await FilesystemTasks.Wrap(() => - StorageArchiveService.DecompressAsync(archive?.Path ?? string.Empty, destinationFolder?.Path ?? string.Empty, password)); + StorageArchiveService.DecompressAsync(archive?.Path ?? string.Empty, destinationFolder?.Path ?? string.Empty, password, encoding)); if (decompressArchiveViewModel.OpenDestinationFolderOnCompletion) await NavigationHelpers.OpenPath(destinationFolderPath, context.ShellPage, FilesystemItemType.Directory); @@ -86,5 +111,33 @@ protected override bool CanDecompressInsideArchive() context.Folder is not null && FileExtensionHelpers.IsZipFile(Path.GetExtension(context.Folder.ItemPath)); } + + protected override bool CanDecompressSelectedItems() + { + return context.SelectedItems.Count == 1 && base.CanDecompressSelectedItems(); + } + + private string? GetArchivePath() + { + if (!string.IsNullOrEmpty(context.SelectedItem?.ItemPath)) + return context.SelectedItem?.ItemPath; + + if (context.PageType == ContentPageTypes.ZipFolder && !context.HasSelection) + return context.Folder?.ItemPath; + + return null; + } + + private void SaveExtractionLocation(string path) + { + var previousArchiveExtractionLocations = UserSettingsService.GeneralSettingsService.PreviousArchiveExtractionLocations?.ToList() ?? []; + previousArchiveExtractionLocations.Remove(path); + previousArchiveExtractionLocations.Insert(0, path); + + if (previousArchiveExtractionLocations.Count > 10) + UserSettingsService.GeneralSettingsService.PreviousArchiveExtractionLocations = previousArchiveExtractionLocations.RemoveFrom(11); + else + UserSettingsService.GeneralSettingsService.PreviousArchiveExtractionLocations = previousArchiveExtractionLocations; + } } } diff --git a/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveHere.cs b/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveHere.cs index 212ad6cbf88e..4af50ce13cc0 100644 --- a/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveHere.cs +++ b/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveHere.cs @@ -1,17 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class DecompressArchiveHere : BaseDecompressArchiveAction + [GeneratedRichCommand] + internal sealed partial class DecompressArchiveHereAction : BaseDecompressArchiveAction { public override string Label - => "ExtractHere".GetLocalizedResource(); + => Strings.ExtractHere.GetLocalizedResource(); public override string Description - => "DecompressArchiveHereDescription".GetLocalizedResource(); + => Strings.DecompressArchiveHereDescription.GetLocalizedFormatResource(context.SelectedItems.Count); - public DecompressArchiveHere() + public string AccessKey + => "H"; + + public DecompressArchiveHereAction() { } diff --git a/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveHereSmart.cs b/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveHereSmart.cs index 496017294408..74d723ce5e8d 100644 --- a/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveHereSmart.cs +++ b/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveHereSmart.cs @@ -1,20 +1,24 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class DecompressArchiveHereSmart : BaseDecompressArchiveAction + [GeneratedRichCommand] + internal sealed partial class DecompressArchiveHereSmartAction : BaseDecompressArchiveAction { public override string Label - => "ExtractHereSmart".GetLocalizedResource(); + => Strings.ExtractHereSmart.GetLocalizedResource(); public override string Description - => "DecompressArchiveHereSmartDescription".GetLocalizedResource(); + => Strings.DecompressArchiveHereSmartDescription.GetLocalizedFormatResource(context.SelectedItems.Count); + + public string AccessKey + => "S"; public override HotKey HotKey => new(Keys.E, KeyModifiers.CtrlShift); - public DecompressArchiveHereSmart() + public DecompressArchiveHereSmartAction() { } diff --git a/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveToChildFolderAction.cs b/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveToChildFolderAction.cs index 361bc3bf2fb1..d3b2d35e290e 100644 --- a/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveToChildFolderAction.cs +++ b/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveToChildFolderAction.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.Dialogs; using Microsoft.UI.Xaml.Controls; @@ -9,13 +9,17 @@ namespace Files.App.Actions { - internal sealed class DecompressArchiveToChildFolderAction : BaseDecompressArchiveAction + [GeneratedRichCommand] + internal sealed partial class DecompressArchiveToChildFolderAction : BaseDecompressArchiveAction { public override string Label => ComputeLabel(); public override string Description - => "DecompressArchiveToChildFolderDescription".GetLocalizedResource(); + => Strings.DecompressArchiveToChildFolderDescription.GetLocalizedFormatResource(context.SelectedItems.Count); + + public string AccessKey + => "C"; public DecompressArchiveToChildFolderAction() { @@ -71,6 +75,7 @@ protected override void Context_PropertyChanged(object? sender, PropertyChangedE switch (e.PropertyName) { case nameof(IContentPageContext.SelectedItems): + case nameof(IContentPageContext.Folder): { if (IsContextPageTypeAdaptedToCommand()) { @@ -86,11 +91,11 @@ protected override void Context_PropertyChanged(object? sender, PropertyChangedE private string ComputeLabel() { if (context.SelectedItems == null || context.SelectedItems.Count == 0) - return string.Format("BaseLayoutItemContextFlyoutExtractToChildFolder".GetLocalizedResource(), string.Empty); + return string.Format(Strings.BaseLayoutItemContextFlyoutExtractToChildFolder.GetLocalizedResource(), string.Empty); return context.SelectedItems.Count > 1 - ? string.Format("BaseLayoutItemContextFlyoutExtractToChildFolder".GetLocalizedResource(), "*") - : string.Format("BaseLayoutItemContextFlyoutExtractToChildFolder".GetLocalizedResource(), SystemIO.Path.GetFileNameWithoutExtension(context.SelectedItems.First().Name)); + ? string.Format(Strings.BaseLayoutItemContextFlyoutExtractToChildFolder.GetLocalizedResource(), "*") + : string.Format(Strings.BaseLayoutItemContextFlyoutExtractToChildFolder.GetLocalizedResource(), SystemIO.Path.GetFileNameWithoutExtension(context.SelectedItems.First().Name)); } } } diff --git a/src/Files.App/Actions/Content/Background/BaseSetAsAction.cs b/src/Files.App/Actions/Content/Background/BaseSetAsAction.cs index d4239457c320..e6c67c2b73b1 100644 --- a/src/Files.App/Actions/Content/Background/BaseSetAsAction.cs +++ b/src/Files.App/Actions/Content/Background/BaseSetAsAction.cs @@ -1,34 +1,59 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Controls; +using Windows.Foundation.Metadata; namespace Files.App.Actions { internal abstract class BaseSetAsAction : ObservableObject, IAction { - protected readonly IContentPageContext context; + protected readonly IContentPageContext ContentPageContext = Ioc.Default.GetRequiredService(); + protected readonly IWindowsWallpaperService WindowsWallpaperService = Ioc.Default.GetRequiredService(); public abstract string Label { get; } + public virtual string ExtendedLabel + => Label; + public abstract string Description { get; } + public virtual ActionCategory Category + => ActionCategory.Image; + public abstract RichGlyph Glyph { get; } public virtual bool IsExecutable => - context.ShellPage is not null && - context.PageType != ContentPageTypes.RecycleBin && - context.PageType != ContentPageTypes.ZipFolder && - (context.ShellPage?.SlimContentPage?.SelectedItemsPropertiesViewModel?.IsCompatibleToSetAsWindowsWallpaper ?? false); + ContentPageContext.ShellPage is not null && + ContentPageContext.PageType != ContentPageTypes.RecycleBin && + ContentPageContext.PageType != ContentPageTypes.ZipFolder && + ContentPageContext.PageType != ContentPageTypes.ReleaseNotes && + ContentPageContext.PageType != ContentPageTypes.Settings && + (ContentPageContext.ShellPage?.SlimContentPage?.SelectedItemsPropertiesViewModel?.IsCompatibleToSetAsWindowsWallpaper ?? false); public BaseSetAsAction() { - context = Ioc.Default.GetRequiredService(); - - context.PropertyChanged += Context_PropertyChanged; + ContentPageContext.PropertyChanged += ContentPageContext_PropertyChanged; } public abstract Task ExecuteAsync(object? parameter = null); - private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) + protected async void ShowErrorDialog(string message) + { + var errorDialog = new ContentDialog() + { + Title = Strings.FailedToSetBackground.GetLocalizedResource(), + Content = message, + PrimaryButtonText = Strings.OK.GetLocalizedResource(), + }; + + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + errorDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; + + await errorDialog.TryShowAsync(); + } + + private void ContentPageContext_PropertyChanged(object? sender, PropertyChangedEventArgs e) { switch (e.PropertyName) { @@ -38,10 +63,10 @@ private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) case nameof(IContentPageContext.SelectedItem): case nameof(IContentPageContext.SelectedItems): { - if (context.ShellPage is not null && context.ShellPage.SlimContentPage is not null) + if (ContentPageContext.ShellPage is not null && ContentPageContext.ShellPage.SlimContentPage is not null) { - var viewModel = context.ShellPage.SlimContentPage.SelectedItemsPropertiesViewModel; - var extensions = context.SelectedItems.Select(selectedItem => selectedItem.FileExtension).Distinct().ToList(); + var viewModel = ContentPageContext.ShellPage.SlimContentPage.SelectedItemsPropertiesViewModel; + var extensions = ContentPageContext.SelectedItems.Select(selectedItem => selectedItem.FileExtension).Distinct().ToList(); viewModel.CheckAllFileExtensions(extensions); } diff --git a/src/Files.App/Actions/Content/Background/SetAsAppBackgroundAction.cs b/src/Files.App/Actions/Content/Background/SetAsAppBackgroundAction.cs index d6b650f35e6a..cc7befc03e77 100644 --- a/src/Files.App/Actions/Content/Background/SetAsAppBackgroundAction.cs +++ b/src/Files.App/Actions/Content/Background/SetAsAppBackgroundAction.cs @@ -1,29 +1,36 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class SetAsAppBackgroundAction : BaseSetAsAction + [GeneratedRichCommand] + internal sealed partial class SetAsAppBackgroundAction : BaseSetAsAction { - private IAppearanceSettingsService AppearanceSettingsService { get; } = Ioc.Default.GetRequiredService(); + private readonly IAppearanceSettingsService AppearanceSettingsService = Ioc.Default.GetRequiredService(); public override string Label - => "SetAsAppBackground".GetLocalizedResource(); + => Strings.Application.GetLocalizedResource(); + + public override string ExtendedLabel + => Strings.SetAsAppBackground.GetLocalizedResource(); public override string Description - => "SetAsAppBackgroundDescription".GetLocalizedResource(); + => Strings.SetAsAppBackgroundDescription.GetLocalizedResource(); + + public string AccessKey + => "A"; public override RichGlyph Glyph => new("\uE91B"); public override bool IsExecutable => base.IsExecutable && - context.SelectedItem is not null; + ContentPageContext.SelectedItem is not null; public override Task ExecuteAsync(object? parameter = null) { - if (context.SelectedItem is not null) - AppearanceSettingsService.AppThemeBackgroundImageSource = context.SelectedItem.ItemPath; + if (IsExecutable && ContentPageContext.SelectedItem is ListedItem selectedItem) + AppearanceSettingsService.AppThemeBackgroundImageSource = selectedItem.ItemPath; return Task.CompletedTask; } diff --git a/src/Files.App/Actions/Content/Background/SetAsLockscreenBackgroundAction.cs b/src/Files.App/Actions/Content/Background/SetAsLockscreenBackgroundAction.cs index fbbff1712b68..8be0debcbaf4 100644 --- a/src/Files.App/Actions/Content/Background/SetAsLockscreenBackgroundAction.cs +++ b/src/Files.App/Actions/Content/Background/SetAsLockscreenBackgroundAction.cs @@ -1,29 +1,45 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class SetAsLockscreenBackgroundAction : BaseSetAsAction + [GeneratedRichCommand] + internal sealed partial class SetAsLockscreenBackgroundAction : BaseSetAsAction { public override string Label - => "SetAsLockscreen".GetLocalizedResource(); + => Strings.Lockscreen.GetLocalizedResource(); + + public override string ExtendedLabel + => Strings.SetAsLockscreen.GetLocalizedResource(); public override string Description - => "SetAsLockscreenBackgroundDescription".GetLocalizedResource(); + => Strings.SetAsLockscreenBackgroundDescription.GetLocalizedResource(); + + public string AccessKey + => "L"; public override RichGlyph Glyph => new("\uEE3F"); public override bool IsExecutable => base.IsExecutable && - context.SelectedItem is not null; + ContentPageContext.SelectedItem is not null; public override Task ExecuteAsync(object? parameter = null) { - if (context.SelectedItem is not null) - return WallpaperHelpers.SetAsBackgroundAsync(WallpaperType.LockScreen, context.SelectedItem.ItemPath); + if (!IsExecutable || ContentPageContext.SelectedItem is not ListedItem selectedItem) + return Task.CompletedTask; + + try + { + return WindowsWallpaperService.SetLockScreenWallpaper(selectedItem.ItemPath); + } + catch (Exception ex) + { + ShowErrorDialog(ex.Message); - return Task.CompletedTask; + return Task.CompletedTask; + } } } } diff --git a/src/Files.App/Actions/Content/Background/SetAsSlideshowBackgroundAction.cs b/src/Files.App/Actions/Content/Background/SetAsSlideshowBackgroundAction.cs index c66e7d8cbda4..7bb11581949f 100644 --- a/src/Files.App/Actions/Content/Background/SetAsSlideshowBackgroundAction.cs +++ b/src/Files.App/Actions/Content/Background/SetAsSlideshowBackgroundAction.cs @@ -1,27 +1,37 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class SetAsSlideshowBackgroundAction : BaseSetAsAction + [GeneratedRichCommand] + internal sealed partial class SetAsSlideshowBackgroundAction : BaseSetAsAction { public override string Label - => "SetAsSlideshow".GetLocalizedResource(); + => Strings.SetAsSlideshow.GetLocalizedResource(); public override string Description - => "SetAsSlideshowBackgroundDescription".GetLocalizedResource(); + => Strings.SetAsSlideshowBackgroundDescription.GetLocalizedResource(); public override RichGlyph Glyph => new("\uE91B"); public override bool IsExecutable => base.IsExecutable && - context.SelectedItems.Count > 1; + ContentPageContext.SelectedItems.Count > 1; public override Task ExecuteAsync(object? parameter = null) { - var paths = context.SelectedItems.Select(item => item.ItemPath).ToArray(); - WallpaperHelpers.SetSlideshow(paths); + if (!IsExecutable || ContentPageContext.SelectedItems.Select(item => item.ItemPath).ToArray() is not string[] paths || paths.Length is 0) + return Task.CompletedTask; + + try + { + WindowsWallpaperService.SetDesktopSlideshow(paths); + } + catch (Exception ex) + { + ShowErrorDialog(ex.Message); + } return Task.CompletedTask; } diff --git a/src/Files.App/Actions/Content/Background/SetAsWallpaperBackgroundAction.cs b/src/Files.App/Actions/Content/Background/SetAsWallpaperBackgroundAction.cs index a87d7bc4cec9..9d8e09608009 100644 --- a/src/Files.App/Actions/Content/Background/SetAsWallpaperBackgroundAction.cs +++ b/src/Files.App/Actions/Content/Background/SetAsWallpaperBackgroundAction.cs @@ -1,27 +1,43 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class SetAsWallpaperBackgroundAction : BaseSetAsAction + [GeneratedRichCommand] + internal sealed partial class SetAsWallpaperBackgroundAction : BaseSetAsAction { public override string Label - => "SetAsBackground".GetLocalizedResource(); + => Strings.Desktop.GetLocalizedResource(); + + public override string ExtendedLabel + => Strings.SetAsBackground.GetLocalizedResource(); public override string Description - => "SetAsWallpaperBackgroundDescription".GetLocalizedResource(); + => Strings.SetAsWallpaperBackgroundDescription.GetLocalizedResource(); + + public string AccessKey + => "W"; public override RichGlyph Glyph => new("\uE91B"); public override bool IsExecutable => base.IsExecutable && - context.SelectedItem is not null; + ContentPageContext.SelectedItem is not null; public override Task ExecuteAsync(object? parameter = null) { - if (context.SelectedItem is not null) - return WallpaperHelpers.SetAsBackgroundAsync(WallpaperType.Desktop, context.SelectedItem.ItemPath); + if (!IsExecutable || ContentPageContext.SelectedItem is not ListedItem selectedItem) + return Task.CompletedTask; + + try + { + WindowsWallpaperService.SetDesktopWallpaper(selectedItem.ItemPath); + } + catch (Exception ex) + { + ShowErrorDialog(ex.Message); + } return Task.CompletedTask; } diff --git a/src/Files.App/Actions/Content/ImageManipulation/BaseRotateAction.cs b/src/Files.App/Actions/Content/ImageManipulation/BaseRotateAction.cs index eead57e36a66..84ef8d6a0723 100644 --- a/src/Files.App/Actions/Content/ImageManipulation/BaseRotateAction.cs +++ b/src/Files.App/Actions/Content/ImageManipulation/BaseRotateAction.cs @@ -1,13 +1,14 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. +using Files.Shared.Helpers; using Windows.Graphics.Imaging; namespace Files.App.Actions { internal abstract class BaseRotateAction : ObservableObject, IAction { - private readonly IContentPageContext context; + protected readonly IContentPageContext context; private readonly InfoPaneViewModel _infoPaneViewModel; @@ -15,13 +16,22 @@ internal abstract class BaseRotateAction : ObservableObject, IAction public abstract string Description { get; } + public virtual ActionCategory Category + => ActionCategory.Image; + public abstract RichGlyph Glyph { get; } protected abstract BitmapRotation Rotation { get; } public bool IsExecutable => - IsContextPageTypeAdaptedToCommand() && - (context.ShellPage?.SlimContentPage?.SelectedItemsPropertiesViewModel?.IsCompatibleToSetAsWindowsWallpaper ?? false); + context.ShellPage is not null && + context.ShellPage.SlimContentPage is not null && + context.PageType != ContentPageTypes.RecycleBin && + context.PageType != ContentPageTypes.ZipFolder && + context.PageType != ContentPageTypes.ReleaseNotes && + context.PageType != ContentPageTypes.Settings && + context.HasSelection && + context.SelectedItems.All(x => FileExtensionHelpers.IsCompatibleToSetAsWindowsWallpaper(x.FileExtension)); public BaseRotateAction() { @@ -40,28 +50,10 @@ public async Task ExecuteAsync(object? parameter = null) await _infoPaneViewModel.UpdateSelectedItemPreviewAsync(); } - private bool IsContextPageTypeAdaptedToCommand() - { - return - context.PageType != ContentPageTypes.RecycleBin && - context.PageType != ContentPageTypes.ZipFolder && - context.PageType != ContentPageTypes.None; - } - private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) { - if (e.PropertyName is nameof(IContentPageContext.SelectedItem)) - { - if (context.ShellPage is not null && context.ShellPage.SlimContentPage is not null) - { - var viewModel = context.ShellPage.SlimContentPage.SelectedItemsPropertiesViewModel; - var extensions = context.SelectedItems.Select(selectedItem => selectedItem.FileExtension).Distinct().ToList(); - - viewModel.CheckAllFileExtensions(extensions); - } - + if (e.PropertyName is nameof(IContentPageContext.SelectedItems)) OnPropertyChanged(nameof(IsExecutable)); - } } } } diff --git a/src/Files.App/Actions/Content/ImageManipulation/RotateLeftAction.cs b/src/Files.App/Actions/Content/ImageManipulation/RotateLeftAction.cs index f51a6f8d18e1..9415b6210545 100644 --- a/src/Files.App/Actions/Content/ImageManipulation/RotateLeftAction.cs +++ b/src/Files.App/Actions/Content/ImageManipulation/RotateLeftAction.cs @@ -1,20 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.Graphics.Imaging; namespace Files.App.Actions { - internal sealed class RotateLeftAction : BaseRotateAction + [GeneratedRichCommand] + internal sealed partial class RotateLeftAction : BaseRotateAction { public override string Label - => "RotateLeft".GetLocalizedResource(); + => Strings.RotateLeft.GetLocalizedResource(); public override string Description - => "RotateLeftDescription".GetLocalizedResource(); + => Strings.RotateLeftDescription.GetLocalizedFormatResource(context.SelectedItems.Count); public override RichGlyph Glyph - => new(opacityStyle: "ColorIconRotateLeft"); + => new(themedIconStyle: "App.ThemedIcons.ImageRotate.ACW"); protected override BitmapRotation Rotation => BitmapRotation.Clockwise270Degrees; diff --git a/src/Files.App/Actions/Content/ImageManipulation/RotateRightAction.cs b/src/Files.App/Actions/Content/ImageManipulation/RotateRightAction.cs index 0451cec412cf..a4116d2bc353 100644 --- a/src/Files.App/Actions/Content/ImageManipulation/RotateRightAction.cs +++ b/src/Files.App/Actions/Content/ImageManipulation/RotateRightAction.cs @@ -1,20 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.Graphics.Imaging; namespace Files.App.Actions { - internal sealed class RotateRightAction : BaseRotateAction + [GeneratedRichCommand] + internal sealed partial class RotateRightAction : BaseRotateAction { public override string Label - => "RotateRight".GetLocalizedResource(); + => Strings.RotateRight.GetLocalizedResource(); public override string Description - => "RotateRightDescription".GetLocalizedResource(); + => Strings.RotateRightDescription.GetLocalizedFormatResource(context.SelectedItems.Count); public override RichGlyph Glyph - => new(opacityStyle: "ColorIconRotateRight"); + => new(themedIconStyle: "App.ThemedIcons.ImageRotate.CW"); protected override BitmapRotation Rotation => BitmapRotation.Clockwise90Degrees; diff --git a/src/Files.App/Actions/Content/Install/InstallCertificateAction.cs b/src/Files.App/Actions/Content/Install/InstallCertificateAction.cs index 6ec56d07756e..7f6d2ea99f7f 100644 --- a/src/Files.App/Actions/Content/Install/InstallCertificateAction.cs +++ b/src/Files.App/Actions/Content/Install/InstallCertificateAction.cs @@ -1,23 +1,27 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.Shared.Helpers; namespace Files.App.Actions { - internal sealed class InstallCertificateAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class InstallCertificateAction : ObservableObject, IAction { private readonly IContentPageContext context; public string Label - => "Install".GetLocalizedResource(); + => Strings.InstallCertificate.GetLocalizedResource(); public string Description - => "InstallCertificateDescription".GetLocalizedResource(); + => Strings.InstallCertificateDescription.GetLocalizedFormatResource(context.SelectedItems.Count); public RichGlyph Glyph => new("\uEB95"); + public ActionCategory Category + => ActionCategory.Install; + public bool IsExecutable => context.SelectedItems.Any() && context.SelectedItems.All(x => FileExtensionHelpers.IsCertificateFile(x.FileExtension)) && diff --git a/src/Files.App/Actions/Content/Install/InstallFontAction.cs b/src/Files.App/Actions/Content/Install/InstallFontAction.cs index f7637f23122a..31b5f4aeaed6 100644 --- a/src/Files.App/Actions/Content/Install/InstallFontAction.cs +++ b/src/Files.App/Actions/Content/Install/InstallFontAction.cs @@ -1,22 +1,27 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.Shared.Helpers; namespace Files.App.Actions { - internal sealed class InstallFontAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class InstallFontAction : ObservableObject, IAction { private readonly IContentPageContext context; + private static readonly StatusCenterViewModel StatusCenterViewModel = Ioc.Default.GetRequiredService(); public string Label - => "Install".GetLocalizedResource(); + => Strings.InstallFont.GetLocalizedResource(); public string Description - => "InstallFontDescription".GetLocalizedResource(); + => Strings.InstallFontDescription.GetLocalizedFormatResource(context.SelectedItems.Count); public RichGlyph Glyph - => new("\uE8D2"); + => new(themedIconStyle: "App.ThemedIcons.Actions.FontInstall"); + + public ActionCategory Category + => ActionCategory.Install; public bool IsExecutable => context.SelectedItems.Any() && @@ -31,10 +36,19 @@ public InstallFontAction() context.PropertyChanged += Context_PropertyChanged; } - public Task ExecuteAsync(object? parameter = null) + public async Task ExecuteAsync(object? parameter = null) { + if (context?.ShellPage?.ShellViewModel.WorkingDirectory is null) + return; + + var banner = StatusCenterHelper.AddCard_InstallFont(context.ShellPage.ShellViewModel.WorkingDirectory.CreateEnumerable(), ReturnResult.InProgress, context.SelectedItems.Count); + banner.IsCancelable = false; + var paths = context.SelectedItems.Select(item => item.ItemPath).ToArray(); - return Win32Helper.InstallFontsAsync(paths, false); + await Win32Helper.InstallFontsAsync(paths, false); + + StatusCenterViewModel.RemoveItem(banner); + StatusCenterHelper.AddCard_InstallFont(context.ShellPage.ShellViewModel.WorkingDirectory.CreateEnumerable(), ReturnResult.Success, context.SelectedItems.Count); } public void Context_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) diff --git a/src/Files.App/Actions/Content/Install/InstallInfDriverAction.cs b/src/Files.App/Actions/Content/Install/InstallInfDriverAction.cs index ed05e71dfa6f..96b1157730cd 100644 --- a/src/Files.App/Actions/Content/Install/InstallInfDriverAction.cs +++ b/src/Files.App/Actions/Content/Install/InstallInfDriverAction.cs @@ -1,23 +1,27 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.Shared.Helpers; namespace Files.App.Actions { - internal sealed class InstallInfDriverAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class InstallInfDriverAction : ObservableObject, IAction { private readonly IContentPageContext context; public string Label - => "Install".GetLocalizedResource(); - + => Strings.InstallDriver.GetLocalizedResource(); + public string Description - => "InstallInfDriverDescription".GetLocalizedResource(); + => Strings.InstallInfDriverDescription.GetLocalizedFormatResource(context.SelectedItems.Count); public RichGlyph Glyph => new("\uE9F5"); + public ActionCategory Category + => ActionCategory.Install; + public bool IsExecutable => context.SelectedItems.Count == 1 && FileExtensionHelpers.IsInfFile(context.SelectedItems[0].FileExtension) && diff --git a/src/Files.App/Actions/Content/PlayAllAction.cs b/src/Files.App/Actions/Content/PlayAllAction.cs index 45240742fdfc..cee5503a26c2 100644 --- a/src/Files.App/Actions/Content/PlayAllAction.cs +++ b/src/Files.App/Actions/Content/PlayAllAction.cs @@ -1,19 +1,23 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.Shared.Helpers; namespace Files.App.Actions { - internal sealed class PlayAllAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class PlayAllAction : ObservableObject, IAction { private readonly IContentPageContext context; public string Label - => "PlayAll".GetLocalizedResource(); + => Strings.PlayAll.GetLocalizedResource(); public string Description - => "PlayAllDescription".GetLocalizedResource(); + => Strings.PlayAllDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Media; public RichGlyph Glyph => new("\uE768"); diff --git a/src/Files.App/Actions/Content/PreviewPopup/LaunchPreviewPopupAction.cs b/src/Files.App/Actions/Content/PreviewPopup/LaunchPreviewPopupAction.cs index e78e93474ddc..44c37c75005a 100644 --- a/src/Files.App/Actions/Content/PreviewPopup/LaunchPreviewPopupAction.cs +++ b/src/Files.App/Actions/Content/PreviewPopup/LaunchPreviewPopupAction.cs @@ -1,26 +1,29 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class LaunchPreviewPopupAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class LaunchPreviewPopupAction : ObservableObject, IAction { private readonly IContentPageContext context; private readonly IPreviewPopupService previewPopupService; public string Label - => "LaunchPreviewPopup".GetLocalizedResource(); + => Strings.LaunchPreviewPopup.GetLocalizedResource(); public string Description - => "LaunchPreviewPopupDescription".GetLocalizedResource(); + => Strings.LaunchPreviewPopupDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Show; public HotKey HotKey => new(Keys.Space); public bool IsExecutable => context.SelectedItems.Count == 1 && - (!context.ShellPage?.ToolbarViewModel?.IsEditModeEnabled ?? false) && (!context.ShellPage?.SlimContentPage?.IsRenamingItem ?? false); public LaunchPreviewPopupAction() diff --git a/src/Files.App/Actions/Content/RefreshItemsAction.cs b/src/Files.App/Actions/Content/RefreshItemsAction.cs index a7edf349df6d..2b32a7426322 100644 --- a/src/Files.App/Actions/Content/RefreshItemsAction.cs +++ b/src/Files.App/Actions/Content/RefreshItemsAction.cs @@ -1,17 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class RefreshItemsAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class RefreshItemsAction : ObservableObject, IAction { private readonly IContentPageContext context; public string Label - => "Refresh".GetLocalizedResource(); + => Strings.Refresh.GetLocalizedResource(); public string Description - => "RefreshItemsDescription".GetLocalizedResource(); + => Strings.RefreshItemsDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.FileSystem; public RichGlyph Glyph => new("\uE72C"); diff --git a/src/Files.App/Actions/Content/Run/BaseRunAsAction.cs b/src/Files.App/Actions/Content/Run/BaseRunAsAction.cs index 3dea9efc660f..9fcab5d96849 100644 --- a/src/Files.App/Actions/Content/Run/BaseRunAsAction.cs +++ b/src/Files.App/Actions/Content/Run/BaseRunAsAction.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { @@ -13,6 +13,9 @@ internal abstract class BaseRunAsAction : ObservableObject, IAction public abstract string Description { get; } + public virtual ActionCategory Category + => ActionCategory.Run; + public abstract RichGlyph Glyph { get; } public virtual bool IsExecutable { get; } diff --git a/src/Files.App/Actions/Content/Run/RunAsAdminAction.cs b/src/Files.App/Actions/Content/Run/RunAsAdminAction.cs index a178ea505328..d1455da0b17f 100644 --- a/src/Files.App/Actions/Content/Run/RunAsAdminAction.cs +++ b/src/Files.App/Actions/Content/Run/RunAsAdminAction.cs @@ -1,19 +1,20 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.Shared.Helpers; namespace Files.App.Actions { - internal sealed class RunAsAdminAction : BaseRunAsAction + [GeneratedRichCommand] + internal sealed partial class RunAsAdminAction : BaseRunAsAction { private readonly IContentPageContext ContentPageContext = Ioc.Default.GetRequiredService(); public override string Label - => "RunAsAdministrator".GetLocalizedResource(); + => Strings.RunAsAdministrator.GetLocalizedResource(); public override string Description - => "RunAsAdminDescription".GetLocalizedResource(); + => Strings.RunAsAdminDescription.GetLocalizedResource(); public override RichGlyph Glyph => new("\uE7EF"); @@ -23,7 +24,7 @@ ContentPageContext.SelectedItem is not null && ContentPageContext.PageType != ContentPageTypes.RecycleBin && ContentPageContext.PageType != ContentPageTypes.ZipFolder && (FileExtensionHelpers.IsExecutableFile(ContentPageContext.SelectedItem.FileExtension) || - (ContentPageContext.SelectedItem is ShortcutItem shortcut && + (ContentPageContext.SelectedItem is IShortcutItem shortcut && shortcut.IsExecutable)); public RunAsAdminAction() : base("runas") diff --git a/src/Files.App/Actions/Content/Run/RunAsAnotherUserAction.cs b/src/Files.App/Actions/Content/Run/RunAsAnotherUserAction.cs index 53dd3a719b4f..cfd92f995871 100644 --- a/src/Files.App/Actions/Content/Run/RunAsAnotherUserAction.cs +++ b/src/Files.App/Actions/Content/Run/RunAsAnotherUserAction.cs @@ -1,18 +1,19 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.Shared.Helpers; namespace Files.App.Actions { - internal sealed class RunAsAnotherUserAction : BaseRunAsAction + [GeneratedRichCommand] + internal sealed partial class RunAsAnotherUserAction : BaseRunAsAction { private readonly IContentPageContext ContentPageContext = Ioc.Default.GetRequiredService(); public override string Label - => "BaseLayoutContextFlyoutRunAsAnotherUser/Text".GetLocalizedResource(); + => Strings.BaseLayoutContextFlyoutRunAsAnotherUser_Text.GetLocalizedResource(); public override string Description - => "RunAsAnotherUserDescription".GetLocalizedResource(); + => Strings.RunAsAnotherUserDescription.GetLocalizedResource(); public override RichGlyph Glyph => new("\uE7EE"); @@ -23,7 +24,7 @@ ContentPageContext.SelectedItem is not null && ContentPageContext.PageType != ContentPageTypes.ZipFolder && !FileExtensionHelpers.IsAhkFile(ContentPageContext.SelectedItem.FileExtension) && (FileExtensionHelpers.IsExecutableFile(ContentPageContext.SelectedItem.FileExtension) || - (ContentPageContext.SelectedItem is ShortcutItem shortcut && + (ContentPageContext.SelectedItem is IShortcutItem shortcut && shortcut.IsExecutable)); public RunAsAnotherUserAction() : base("runasuser") diff --git a/src/Files.App/Actions/Content/Run/RunWithPowershellAction.cs b/src/Files.App/Actions/Content/Run/RunWithPowershellAction.cs index bd4029c31c5c..dffe948dbedd 100644 --- a/src/Files.App/Actions/Content/Run/RunWithPowershellAction.cs +++ b/src/Files.App/Actions/Content/Run/RunWithPowershellAction.cs @@ -1,25 +1,30 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.Shared.Helpers; namespace Files.App.Actions { - internal sealed class RunWithPowershellAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class RunWithPowershellAction : ObservableObject, IAction { private readonly IContentPageContext context; public string Label - => "RunWithPowerShell".GetLocalizedResource(); + => Strings.RunWithPowerShell.GetLocalizedResource(); public string Description - => "RunWithPowershellDescription".GetLocalizedResource(); + => Strings.RunWithPowershellDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Run; public RichGlyph Glyph => new("\uE756"); public bool IsExecutable => context.SelectedItem is not null && + context.PageType != ContentPageTypes.RecycleBin && FileExtensionHelpers.IsPowerShellFile(context.SelectedItem.FileExtension); public RunWithPowershellAction() @@ -31,7 +36,11 @@ public RunWithPowershellAction() public Task ExecuteAsync(object? parameter = null) { - return Win32Helper.RunPowershellCommandAsync($"{context.ShellPage?.SlimContentPage?.SelectedItem?.ItemPath}", false); + return Win32Helper.RunPowershellCommandAsync( + $"& '{context.ShellPage?.SlimContentPage?.SelectedItem?.ItemPath}'", + PowerShellExecutionOptions.None, + context.Folder?.ItemPath + ); } private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) diff --git a/src/Files.App/Actions/Content/Selection/ClearSelectionAction.cs b/src/Files.App/Actions/Content/Selection/ClearSelectionAction.cs index e0eb0e783bd3..52b3e53b2b4b 100644 --- a/src/Files.App/Actions/Content/Selection/ClearSelectionAction.cs +++ b/src/Files.App/Actions/Content/Selection/ClearSelectionAction.cs @@ -1,20 +1,24 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { + [GeneratedRichCommand] internal sealed class ClearSelectionAction : IAction { private readonly IContentPageContext context; public string Label - => "ClearSelection".GetLocalizedResource(); + => Strings.ClearSelection.GetLocalizedResource(); public string Description - => "ClearSelectionDescription".GetLocalizedResource(); + => Strings.ClearSelectionDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Selection; public RichGlyph Glyph - => new("\uE8E6"); + => new(themedIconStyle: "App.ThemedIcons.SelectNone"); public bool IsExecutable { @@ -30,11 +34,9 @@ public bool IsExecutable if (page is null) return false; - bool isCommandPaletteOpen = page.ToolbarViewModel.IsCommandPaletteOpen; - bool isEditing = page.ToolbarViewModel.IsEditModeEnabled; bool isRenaming = page.SlimContentPage.IsRenamingItem; - return isCommandPaletteOpen || (!isEditing && !isRenaming); + return !isRenaming; } } diff --git a/src/Files.App/Actions/Content/Selection/InvertSelectionAction.cs b/src/Files.App/Actions/Content/Selection/InvertSelectionAction.cs index 4558204c0476..1d63ac2432cd 100644 --- a/src/Files.App/Actions/Content/Selection/InvertSelectionAction.cs +++ b/src/Files.App/Actions/Content/Selection/InvertSelectionAction.cs @@ -1,20 +1,24 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { + [GeneratedRichCommand] internal sealed class InvertSelectionAction : IAction { private readonly IContentPageContext context; public string Label - => "InvertSelection".GetLocalizedResource(); + => Strings.InvertSelection.GetLocalizedResource(); public string Description - => "InvertSelectionDescription".GetLocalizedResource(); + => Strings.InvertSelectionDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Selection; public RichGlyph Glyph - => new("\uE746"); + => new(themedIconStyle: "App.ThemedIcons.SelectInvert"); public bool IsExecutable { @@ -30,11 +34,8 @@ public bool IsExecutable if (page is null) return false; - bool isCommandPaletteOpen = page.ToolbarViewModel.IsCommandPaletteOpen; - bool isEditing = page.ToolbarViewModel.IsEditModeEnabled; bool isRenaming = page.SlimContentPage.IsRenamingItem; - - return isCommandPaletteOpen || (!isEditing && !isRenaming); + return !isRenaming; } } diff --git a/src/Files.App/Actions/Content/Selection/SelectAllAction.cs b/src/Files.App/Actions/Content/Selection/SelectAllAction.cs index 372727ce26ab..142d675fcb20 100644 --- a/src/Files.App/Actions/Content/Selection/SelectAllAction.cs +++ b/src/Files.App/Actions/Content/Selection/SelectAllAction.cs @@ -1,20 +1,24 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { + [GeneratedRichCommand] internal sealed class SelectAllAction : IAction { private readonly IContentPageContext context; public string Label - => "SelectAll".GetLocalizedResource(); + => Strings.SelectAll.GetLocalizedResource(); public string Description - => "SelectAllDescription".GetLocalizedResource(); + => Strings.SelectAllDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Selection; public RichGlyph Glyph - => new("\uE8B3"); + => new(themedIconStyle: "App.ThemedIcons.SelectAll"); public HotKey HotKey => new(Keys.A, KeyModifiers.Ctrl); @@ -35,11 +39,9 @@ public bool IsExecutable if (itemCount == selectedItemCount) return false; - bool isCommandPaletteOpen = page.ToolbarViewModel.IsCommandPaletteOpen; - bool isEditing = page.ToolbarViewModel.IsEditModeEnabled; bool isRenaming = page.SlimContentPage?.IsRenamingItem ?? false; - return isCommandPaletteOpen || (!isEditing && !isRenaming); + return !isRenaming; } } diff --git a/src/Files.App/Actions/Content/Selection/ToggleSelectAction.cs b/src/Files.App/Actions/Content/Selection/ToggleSelectAction.cs index fad8fc5a801a..3ad075593e63 100644 --- a/src/Files.App/Actions/Content/Selection/ToggleSelectAction.cs +++ b/src/Files.App/Actions/Content/Selection/ToggleSelectAction.cs @@ -1,18 +1,22 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Controls.Primitives; using Microsoft.UI.Xaml.Input; namespace Files.App.Actions { + [GeneratedRichCommand] internal sealed class ToggleSelectAction : IAction { public string Label - => "ToggleSelect".GetLocalizedResource(); + => Strings.ToggleSelect.GetLocalizedResource(); public string Description - => "ToggleSelectDescription".GetLocalizedResource(); + => Strings.ToggleSelectDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Selection; public HotKey HotKey => new(Keys.Space, KeyModifiers.Ctrl); diff --git a/src/Files.App/Actions/Content/Share/ShareItemAction.cs b/src/Files.App/Actions/Content/Share/ShareItemAction.cs index 8487d61e8039..0f283fc5c3b2 100644 --- a/src/Files.App/Actions/Content/Share/ShareItemAction.cs +++ b/src/Files.App/Actions/Content/Share/ShareItemAction.cs @@ -1,22 +1,29 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.ApplicationModel.DataTransfer; namespace Files.App.Actions { - internal sealed class ShareItemAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class ShareItemAction : ObservableObject, IAction { private readonly IContentPageContext context; public string Label - => "Share".GetLocalizedResource(); + => Strings.Share.GetLocalizedResource(); public string Description - => "ShareItemDescription".GetLocalizedResource(); + => Strings.ShareItemDescription.GetLocalizedFormatResource(context.SelectedItems.Count); public RichGlyph Glyph - => new(opacityStyle: "ColorIconShare"); + => new(themedIconStyle: "App.ThemedIcons.Share"); + + public ActionCategory Category + => ActionCategory.FileSystem; + + public string AccessKey + => "H"; public bool IsExecutable => IsContextPageTypeAdaptedToCommand() && diff --git a/src/Files.App/Actions/Content/Tags/OpenAllTaggedActions.cs b/src/Files.App/Actions/Content/Tags/OpenAllTaggedActions.cs index dfdf6d6bbd8a..42a7875b9e65 100644 --- a/src/Files.App/Actions/Content/Tags/OpenAllTaggedActions.cs +++ b/src/Files.App/Actions/Content/Tags/OpenAllTaggedActions.cs @@ -1,28 +1,32 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - sealed class OpenAllTaggedActions: ObservableObject, IAction - { + [GeneratedRichCommand] + sealed partial class OpenAllTaggedAction : ObservableObject, IAction + { private readonly IContentPageContext _pageContext; private readonly ITagsContext _tagsContext; public string Label - => "OpenAllTaggedItems".GetLocalizedResource(); + => Strings.OpenAllTaggedItems.GetLocalizedResource(); public string Description - => "OpenAllTaggedItemsDescription".GetLocalizedResource(); + => Strings.OpenAllTaggedItemsDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Open; public RichGlyph Glyph => new("\uE71D"); - public bool IsExecutable => + public bool IsExecutable => _pageContext.ShellPage is not null && _tagsContext.TaggedItems.Any(); - public OpenAllTaggedActions() + public OpenAllTaggedAction() { _pageContext = Ioc.Default.GetRequiredService(); _tagsContext = Ioc.Default.GetRequiredService(); @@ -37,17 +41,17 @@ public async Task ExecuteAsync(object? parameter = null) .Where(item => !item.isFolder) .Select(f => f.path) .ToList(); - + var folderPaths = _tagsContext .TaggedItems .Where(item => item.isFolder) .Select(f => f.path) .ToList(); - + await Task.WhenAll(filePaths.Select(path => NavigationHelpers.OpenPath(path, _pageContext.ShellPage!))); - foreach (var path in folderPaths) - await NavigationHelpers.OpenPathInNewTab(path, false); + foreach (var path in folderPaths) + await NavigationHelpers.OpenPathInNewTab(path); } private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) diff --git a/src/Files.App/Actions/Display/GroupAction.cs b/src/Files.App/Actions/Display/GroupAction.cs index 22e6113d3cac..f6f66acf93ad 100644 --- a/src/Files.App/Actions/Display/GroupAction.cs +++ b/src/Files.App/Actions/Display/GroupAction.cs @@ -1,147 +1,164 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class GroupByNoneAction : GroupByAction + [GeneratedRichCommand] + internal sealed partial class GroupByNoneAction : GroupByAction { protected override GroupOption GroupOption => GroupOption.None; public override string Label - => "None".GetLocalizedResource(); + => Strings.None.GetLocalizedResource(); public override string Description - => "GroupByNoneDescription".GetLocalizedResource(); + => Strings.GroupByNoneDescription.GetLocalizedResource(); + + public override string ExtendedLabel + => Label; } - internal sealed class GroupByNameAction : GroupByAction + [GeneratedRichCommand] + internal sealed partial class GroupByNameAction : GroupByAction { protected override GroupOption GroupOption => GroupOption.Name; public override string Label - => "Name".GetLocalizedResource(); + => Strings.Name.GetLocalizedResource(); public override string Description - => "GroupByNameDescription".GetLocalizedResource(); + => Strings.GroupByNameDescription.GetLocalizedResource(); } - internal sealed class GroupByDateModifiedAction : GroupByAction + [GeneratedRichCommand] + internal sealed partial class GroupByDateModifiedAction : GroupByAction { protected override GroupOption GroupOption => GroupOption.DateModified; public override string Label - => "DateModifiedLowerCase".GetLocalizedResource(); + => Strings.DateModifiedLowerCase.GetLocalizedResource(); public override string Description - => "GroupByDateModifiedDescription".GetLocalizedResource(); + => Strings.GroupByDateModifiedDescription.GetLocalizedResource(); } - internal sealed class GroupByDateCreatedAction : GroupByAction + [GeneratedRichCommand] + internal sealed partial class GroupByDateCreatedAction : GroupByAction { protected override GroupOption GroupOption => GroupOption.DateCreated; public override string Label - => "DateCreated".GetLocalizedResource(); + => Strings.DateCreated.GetLocalizedResource(); public override string Description - => "GroupByDateCreatedDescription".GetLocalizedResource(); + => Strings.GroupByDateCreatedDescription.GetLocalizedResource(); } - internal sealed class GroupBySizeAction : GroupByAction + [GeneratedRichCommand] + internal sealed partial class GroupBySizeAction : GroupByAction { protected override GroupOption GroupOption => GroupOption.Size; public override string Label - => "Size".GetLocalizedResource(); + => Strings.Size.GetLocalizedResource(); public override string Description - => "GroupBySizeDescription".GetLocalizedResource(); + => Strings.GroupBySizeDescription.GetLocalizedResource(); } - internal sealed class GroupByTypeAction : GroupByAction + [GeneratedRichCommand] + internal sealed partial class GroupByTypeAction : GroupByAction { protected override GroupOption GroupOption => GroupOption.FileType; public override string Label - => "Type".GetLocalizedResource(); + => Strings.Type.GetLocalizedResource(); public override string Description - => "GroupByTypeDescription".GetLocalizedResource(); + => Strings.GroupByTypeDescription.GetLocalizedResource(); } - internal sealed class GroupBySyncStatusAction : GroupByAction + [GeneratedRichCommand] + internal sealed partial class GroupBySyncStatusAction : GroupByAction { protected override GroupOption GroupOption => GroupOption.SyncStatus; public override string Label - => "SyncStatus".GetLocalizedResource(); + => Strings.SyncStatus.GetLocalizedResource(); public override string Description - => "GroupBySyncStatusDescription".GetLocalizedResource(); + => Strings.GroupBySyncStatusDescription.GetLocalizedResource(); protected override bool GetIsExecutable(ContentPageTypes pageType) => pageType is ContentPageTypes.CloudDrive; } - internal sealed class GroupByTagAction : GroupByAction + [GeneratedRichCommand] + internal sealed partial class GroupByTagAction : GroupByAction { protected override GroupOption GroupOption => GroupOption.FileTag; public override string Label - => "FileTags".GetLocalizedResource(); + => Strings.FileTags.GetLocalizedResource(); public override string Description - => "GroupByTagDescription".GetLocalizedResource(); + => Strings.GroupByTagDescription.GetLocalizedResource(); } - internal sealed class GroupByOriginalFolderAction : GroupByAction + [GeneratedRichCommand] + internal sealed partial class GroupByOriginalFolderAction : GroupByAction { protected override GroupOption GroupOption => GroupOption.OriginalFolder; public override string Label - => "OriginalFolder".GetLocalizedResource(); + => Strings.OriginalFolder.GetLocalizedResource(); public override string Description - => "GroupByOriginalFolderDescription".GetLocalizedResource(); + => Strings.GroupByOriginalFolderDescription.GetLocalizedResource(); protected override bool GetIsExecutable(ContentPageTypes pageType) => pageType is ContentPageTypes.RecycleBin; } - internal sealed class GroupByDateDeletedAction : GroupByAction + [GeneratedRichCommand] + internal sealed partial class GroupByDateDeletedAction : GroupByAction { protected override GroupOption GroupOption => GroupOption.DateDeleted; public override string Label - => "DateDeleted".GetLocalizedResource(); + => Strings.DateDeleted.GetLocalizedResource(); public override string Description - => "GroupByDateDeletedDescription".GetLocalizedResource(); + => Strings.GroupByDateDeletedDescription.GetLocalizedResource(); protected override bool GetIsExecutable(ContentPageTypes pageType) => pageType is ContentPageTypes.RecycleBin; } - internal sealed class GroupByFolderPathAction : GroupByAction + [GeneratedRichCommand] + internal sealed partial class GroupByFolderPathAction : GroupByAction { protected override GroupOption GroupOption => GroupOption.FolderPath; public override string Label - => "FolderPath".GetLocalizedResource(); + => Strings.FolderPath.GetLocalizedResource(); public override string Description - => "GroupByFolderPathDescription".GetLocalizedResource(); + => Strings.GroupByFolderPathDescription.GetLocalizedResource(); + + public override string ExtendedLabel + => Label; protected override bool GetIsExecutable(ContentPageTypes pageType) => pageType is ContentPageTypes.Library or ContentPageTypes.SearchResults; @@ -159,6 +176,12 @@ internal abstract class GroupByAction : ObservableObject, IToggleAction public abstract string Description { get; } + public virtual ActionCategory Category + => ActionCategory.Grouping; + + public virtual string ExtendedLabel + => Description; + public bool IsOn => DisplayContext.GroupOption == GroupOption; @@ -177,6 +200,7 @@ public GroupByAction() public Task ExecuteAsync(object? parameter = null) { DisplayContext.GroupOption = GroupOption; + LayoutHelpers.UpdateOpenTabsPreferences(); return Task.CompletedTask; } @@ -199,7 +223,8 @@ private void DisplayContext_PropertyChanged(object? sender, PropertyChangedEvent } } - internal sealed class GroupByDateModifiedYearAction : GroupByDateAction + [GeneratedRichCommand] + internal sealed partial class GroupByDateModifiedYearAction : GroupByDateAction { protected override GroupOption GroupOption => GroupOption.DateModified; @@ -208,13 +233,14 @@ protected override GroupByDateUnit GroupByDateUnit => GroupByDateUnit.Year; public override string Label - => "Year".GetLocalizedResource(); + => Strings.Year.GetLocalizedResource(); public override string Description - => "GroupByDateModifiedYearDescription".GetLocalizedResource(); + => Strings.GroupByDateModifiedYearDescription.GetLocalizedResource(); } - internal sealed class GroupByDateModifiedMonthAction : GroupByDateAction + [GeneratedRichCommand] + internal sealed partial class GroupByDateModifiedMonthAction : GroupByDateAction { protected override GroupOption GroupOption => GroupOption.DateModified; @@ -223,13 +249,14 @@ protected override GroupByDateUnit GroupByDateUnit => GroupByDateUnit.Month; public override string Label - => "Month".GetLocalizedResource(); + => Strings.Month.GetLocalizedResource(); public override string Description - => "GroupByDateModifiedMonthDescription".GetLocalizedResource(); + => Strings.GroupByDateModifiedMonthDescription.GetLocalizedResource(); } - internal sealed class GroupByDateModifiedDayAction : GroupByDateAction + [GeneratedRichCommand] + internal sealed partial class GroupByDateModifiedDayAction : GroupByDateAction { protected override GroupOption GroupOption => GroupOption.DateModified; @@ -238,13 +265,14 @@ protected override GroupByDateUnit GroupByDateUnit => GroupByDateUnit.Day; public override string Label - => "Day".GetLocalizedResource(); + => Strings.Day.GetLocalizedResource(); public override string Description - => "GroupByDateModifiedDayDescription".GetLocalizedResource(); + => Strings.GroupByDateModifiedDayDescription.GetLocalizedResource(); } - internal sealed class GroupByDateCreatedYearAction : GroupByDateAction + [GeneratedRichCommand] + internal sealed partial class GroupByDateCreatedYearAction : GroupByDateAction { protected override GroupOption GroupOption => GroupOption.DateCreated; @@ -253,13 +281,14 @@ protected override GroupByDateUnit GroupByDateUnit => GroupByDateUnit.Year; public override string Label - => "Year".GetLocalizedResource(); + => Strings.Year.GetLocalizedResource(); public override string Description - => "GroupByDateCreatedYearDescription".GetLocalizedResource(); + => Strings.GroupByDateCreatedYearDescription.GetLocalizedResource(); } - internal sealed class GroupByDateCreatedMonthAction : GroupByDateAction + [GeneratedRichCommand] + internal sealed partial class GroupByDateCreatedMonthAction : GroupByDateAction { protected override GroupOption GroupOption => GroupOption.DateCreated; @@ -268,13 +297,14 @@ protected override GroupByDateUnit GroupByDateUnit => GroupByDateUnit.Month; public override string Label - => "Month".GetLocalizedResource(); + => Strings.Month.GetLocalizedResource(); public override string Description - => "GroupByDateCreatedMonthDescription".GetLocalizedResource(); + => Strings.GroupByDateCreatedMonthDescription.GetLocalizedResource(); } - internal sealed class GroupByDateCreatedDayAction : GroupByDateAction + [GeneratedRichCommand] + internal sealed partial class GroupByDateCreatedDayAction : GroupByDateAction { protected override GroupOption GroupOption => GroupOption.DateCreated; @@ -283,13 +313,14 @@ protected override GroupByDateUnit GroupByDateUnit => GroupByDateUnit.Day; public override string Label - => "Day".GetLocalizedResource(); + => Strings.Day.GetLocalizedResource(); public override string Description - => "GroupByDateCreatedDayDescription".GetLocalizedResource(); + => Strings.GroupByDateCreatedDayDescription.GetLocalizedResource(); } - internal sealed class GroupByDateDeletedYearAction : GroupByDateAction + [GeneratedRichCommand] + internal sealed partial class GroupByDateDeletedYearAction : GroupByDateAction { protected override GroupOption GroupOption => GroupOption.DateDeleted; @@ -298,16 +329,17 @@ protected override GroupByDateUnit GroupByDateUnit => GroupByDateUnit.Year; public override string Label - => "Year".GetLocalizedResource(); + => Strings.Year.GetLocalizedResource(); public override string Description - => "GroupByDateDeletedYearDescription".GetLocalizedResource(); + => Strings.GroupByDateDeletedYearDescription.GetLocalizedResource(); protected override bool GetIsExecutable(ContentPageTypes pageType) => pageType is ContentPageTypes.RecycleBin; } - internal sealed class GroupByDateDeletedMonthAction : GroupByDateAction + [GeneratedRichCommand] + internal sealed partial class GroupByDateDeletedMonthAction : GroupByDateAction { protected override GroupOption GroupOption => GroupOption.DateDeleted; @@ -316,16 +348,17 @@ protected override GroupByDateUnit GroupByDateUnit => GroupByDateUnit.Month; public override string Label - => "Month".GetLocalizedResource(); + => Strings.Month.GetLocalizedResource(); public override string Description - => "GroupByDateDeletedMonthDescription".GetLocalizedResource(); + => Strings.GroupByDateDeletedMonthDescription.GetLocalizedResource(); protected override bool GetIsExecutable(ContentPageTypes pageType) => pageType is ContentPageTypes.RecycleBin; } - internal sealed class GroupByDateDeletedDayAction : GroupByDateAction + [GeneratedRichCommand] + internal sealed partial class GroupByDateDeletedDayAction : GroupByDateAction { protected override GroupOption GroupOption => GroupOption.DateDeleted; @@ -334,10 +367,10 @@ protected override GroupByDateUnit GroupByDateUnit => GroupByDateUnit.Day; public override string Label - => "Day".GetLocalizedResource(); + => Strings.Day.GetLocalizedResource(); public override string Description - => "GroupByDateDeletedDayDescription".GetLocalizedResource(); + => Strings.GroupByDateDeletedDayDescription.GetLocalizedResource(); protected override bool GetIsExecutable(ContentPageTypes pageType) => pageType is ContentPageTypes.RecycleBin; @@ -357,6 +390,12 @@ internal abstract class GroupByDateAction : ObservableObject, IToggleAction public abstract string Description { get; } + public virtual ActionCategory Category + => ActionCategory.Grouping; + + public virtual string ExtendedLabel + => Description; + public bool IsOn => DisplayContext.GroupOption == GroupOption && DisplayContext.GroupByDateUnit == GroupByDateUnit; @@ -377,6 +416,7 @@ public Task ExecuteAsync(object? parameter = null) { DisplayContext.GroupOption = GroupOption; DisplayContext.GroupByDateUnit = GroupByDateUnit; + LayoutHelpers.UpdateOpenTabsPreferences(); return Task.CompletedTask; } @@ -399,15 +439,19 @@ private void DisplayContext_PropertyChanged(object? sender, PropertyChangedEvent } } - internal sealed class GroupAscendingAction : ObservableObject, IToggleAction + [GeneratedRichCommand] + internal sealed partial class GroupAscendingAction : ObservableObject, IToggleAction { private readonly IDisplayPageContext context; public string Label - => "Ascending".GetLocalizedResource(); + => Strings.Ascending.GetLocalizedResource(); public string Description - => "GroupAscendingDescription".GetLocalizedResource(); + => Strings.GroupAscendingDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Grouping; public bool IsOn => context.GroupDirection is SortDirection.Ascending; @@ -425,6 +469,7 @@ public GroupAscendingAction() public Task ExecuteAsync(object? parameter = null) { context.GroupDirection = SortDirection.Ascending; + LayoutHelpers.UpdateOpenTabsPreferences(); return Task.CompletedTask; } @@ -443,15 +488,19 @@ private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) } } - internal sealed class GroupDescendingAction : ObservableObject, IToggleAction + [GeneratedRichCommand] + internal sealed partial class GroupDescendingAction : ObservableObject, IToggleAction { private readonly IDisplayPageContext context; public string Label - => "Descending".GetLocalizedResource(); + => Strings.Descending.GetLocalizedResource(); public string Description - => "GroupDescendingDescription".GetLocalizedResource(); + => Strings.GroupDescendingDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Grouping; public bool IsOn => context.GroupDirection is SortDirection.Descending; @@ -469,6 +518,7 @@ public GroupDescendingAction() public Task ExecuteAsync(object? parameter = null) { context.GroupDirection = SortDirection.Descending; + LayoutHelpers.UpdateOpenTabsPreferences(); return Task.CompletedTask; } @@ -487,15 +537,19 @@ private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) } } + [GeneratedRichCommand] internal sealed class ToggleGroupDirectionAction : IAction { private readonly IDisplayPageContext context; public string Label - => "ToggleSortDirection".GetLocalizedResource(); + => Strings.ToggleSortDirection.GetLocalizedResource(); public string Description - => "ToggleGroupDirectionDescription".GetLocalizedResource(); + => Strings.ToggleGroupDirectionDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Grouping; public ToggleGroupDirectionAction() { @@ -505,20 +559,25 @@ public ToggleGroupDirectionAction() public Task ExecuteAsync(object? parameter = null) { context.GroupDirection = context.SortDirection is SortDirection.Descending ? SortDirection.Ascending : SortDirection.Descending; + LayoutHelpers.UpdateOpenTabsPreferences(); return Task.CompletedTask; } } - internal sealed class GroupByYearAction : ObservableObject, IToggleAction + [GeneratedRichCommand] + internal sealed partial class GroupByYearAction : ObservableObject, IToggleAction { private readonly IDisplayPageContext context; public string Label - => "Year".GetLocalizedResource(); + => Strings.Year.GetLocalizedResource(); public string Description - => "GroupByYearDescription".GetLocalizedResource(); + => Strings.GroupByYearDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Grouping; public bool IsOn => context.GroupByDateUnit is GroupByDateUnit.Year; @@ -536,6 +595,7 @@ public GroupByYearAction() public Task ExecuteAsync(object? parameter = null) { context.GroupByDateUnit = GroupByDateUnit.Year; + LayoutHelpers.UpdateOpenTabsPreferences(); return Task.CompletedTask; } @@ -554,15 +614,19 @@ private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) } } - internal sealed class GroupByMonthAction : ObservableObject, IToggleAction + [GeneratedRichCommand] + internal sealed partial class GroupByMonthAction : ObservableObject, IToggleAction { private readonly IDisplayPageContext context; public string Label - => "Month".GetLocalizedResource(); + => Strings.Month.GetLocalizedResource(); public string Description - => "GroupByMonthDescription".GetLocalizedResource(); + => Strings.GroupByMonthDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Grouping; public bool IsOn => context.GroupByDateUnit is GroupByDateUnit.Month; @@ -580,6 +644,7 @@ public GroupByMonthAction() public Task ExecuteAsync(object? parameter = null) { context.GroupByDateUnit = GroupByDateUnit.Month; + LayoutHelpers.UpdateOpenTabsPreferences(); return Task.CompletedTask; } @@ -598,15 +663,19 @@ private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) } } + [GeneratedRichCommand] internal sealed class ToggleGroupByDateUnitAction : IAction { private readonly IDisplayPageContext context; public string Label - => "ToggleGroupingUnit".GetLocalizedResource(); + => Strings.ToggleGroupingUnit.GetLocalizedResource(); public string Description - => "ToggleGroupByDateUnitDescription".GetLocalizedResource(); + => Strings.ToggleGroupByDateUnitDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Grouping; public ToggleGroupByDateUnitAction() { @@ -621,6 +690,7 @@ public Task ExecuteAsync(object? parameter = null) GroupByDateUnit.Month => GroupByDateUnit.Day, _ => GroupByDateUnit.Year }; + LayoutHelpers.UpdateOpenTabsPreferences(); return Task.CompletedTask; } diff --git a/src/Files.App/Actions/Display/LayoutAction.cs b/src/Files.App/Actions/Display/LayoutAction.cs index 2e74a07e641f..a986519e6588 100644 --- a/src/Files.App/Actions/Display/LayoutAction.cs +++ b/src/Files.App/Actions/Display/LayoutAction.cs @@ -1,114 +1,140 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class LayoutDetailsAction : ToggleLayoutAction + [GeneratedRichCommand] + internal sealed partial class LayoutDetailsAction : ToggleLayoutAction { protected override LayoutTypes LayoutType => LayoutTypes.Details; public override string Label - => "Details".GetLocalizedResource(); + => Strings.Details.GetLocalizedResource(); public override string Description - => "LayoutDetailsDescription".GetLocalizedResource(); + => Strings.LayoutDetailsDescription.GetLocalizedResource(); public override RichGlyph Glyph - => new(opacityStyle: "Icons.DetailsLayout.16x16"); + => new(themedIconStyle: "App.ThemedIcons.IconLayout.Details"); public override HotKey HotKey => new(Keys.Number1, KeyModifiers.CtrlShift); } - internal sealed class LayoutListAction : ToggleLayoutAction + [GeneratedRichCommand] + internal sealed partial class LayoutListAction : ToggleLayoutAction { protected override LayoutTypes LayoutType => LayoutTypes.List; public override string Label - => "List".GetLocalizedResource(); + => Strings.List.GetLocalizedResource(); public override string Description - => "LayoutListDescription".GetLocalizedResource(); + => Strings.LayoutListDescription.GetLocalizedResource(); public override RichGlyph Glyph - => new(opacityStyle: "Icons.ListLayout.16x16"); + => new(themedIconStyle: "App.ThemedIcons.IconLayout.List"); public override HotKey HotKey => new(Keys.Number2, KeyModifiers.CtrlShift); } - internal sealed class LayoutTilesAction : ToggleLayoutAction + [GeneratedRichCommand] + internal sealed partial class LayoutCardsAction : ToggleLayoutAction { protected override LayoutTypes LayoutType - => LayoutTypes.Tiles; + => LayoutTypes.Cards; public override string Label - => "Tiles".GetLocalizedResource(); + => Strings.Cards.GetLocalizedResource(); public override string Description - => "LayoutTilesDescription".GetLocalizedResource(); + => Strings.LayoutCardsDescription.GetLocalizedResource(); public override RichGlyph Glyph - => new(opacityStyle: "Icons.TilesLayout.16x16"); + => new(themedIconStyle: "App.ThemedIcons.IconLayout.Tiles"); public override HotKey HotKey => new(Keys.Number3, KeyModifiers.CtrlShift); } - internal sealed class LayoutGridAction : ToggleLayoutAction + [GeneratedRichCommand] + internal sealed partial class LayoutGridAction : ToggleLayoutAction { protected override LayoutTypes LayoutType => LayoutTypes.Grid; public override string Label - => "Grid".GetLocalizedResource(); + => Strings.Grid.GetLocalizedResource(); public override string Description - => "LayoutGridDescription".GetLocalizedResource(); + => Strings.LayoutGridDescription.GetLocalizedResource(); public override RichGlyph Glyph - => new(opacityStyle: "Icons.GridLayout.16x16"); + => new(themedIconStyle: "App.ThemedIcons.IconSize.Small"); public override HotKey HotKey => new(Keys.Number4, KeyModifiers.CtrlShift); } - internal sealed class LayoutColumnsAction : ToggleLayoutAction + [GeneratedRichCommand] + internal sealed partial class LayoutColumnsAction : ToggleLayoutAction { + private readonly IContentPageContext ContentPageContext = Ioc.Default.GetRequiredService(); + protected override LayoutTypes LayoutType => LayoutTypes.Columns; public override string Label - => "Columns".GetLocalizedResource(); + => Strings.Columns.GetLocalizedResource(); public override string Description - => "LayoutColumnsDescription".GetLocalizedResource(); + => Strings.LayoutColumnsDescription.GetLocalizedResource(); public override RichGlyph Glyph - => new(opacityStyle: "Icons.ColumnsLayout.16x16"); + => new(themedIconStyle: "App.ThemedIcons.IconLayout.Columns"); public override HotKey HotKey => new(Keys.Number5, KeyModifiers.CtrlShift); + + public override bool IsExecutable + => ContentPageContext.PageType is not ContentPageTypes.RecycleBin; + + public LayoutColumnsAction() + { + ContentPageContext.PropertyChanged += ContentPageContext_PropertyChanged; + } + + private void ContentPageContext_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + switch (e.PropertyName) + { + case nameof(IContentPageContext.PageType): + OnPropertyChanged(nameof(IsExecutable)); + break; + } + } } - internal sealed class LayoutAdaptiveAction : ToggleLayoutAction + [GeneratedRichCommand] + internal sealed partial class LayoutAdaptiveAction : ToggleLayoutAction { protected override LayoutTypes LayoutType => LayoutTypes.Adaptive; public override string Label - => "Adaptive".GetLocalizedResource(); + => Strings.Adaptive.GetLocalizedResource(); public override string Description - => "LayoutAdaptiveDescription".GetLocalizedResource(); + => Strings.LayoutAdaptiveDescription.GetLocalizedResource(); public override bool IsExecutable => Context.IsLayoutAdaptiveEnabled; public override RichGlyph Glyph - => new("\uF576"); + => new(themedIconStyle: "App.ThemedIcons.IconLayout.Auto"); public override HotKey HotKey => new(Keys.Number6, KeyModifiers.CtrlShift); @@ -130,6 +156,9 @@ internal abstract class ToggleLayoutAction : ObservableObject, IToggleAction public abstract string Description { get; } + public virtual ActionCategory Category + => ActionCategory.Layout; + public abstract RichGlyph Glyph { get; } public abstract HotKey HotKey { get; } @@ -168,17 +197,20 @@ protected virtual void OnContextChanged(string propertyName) } } - internal sealed class LayoutDecreaseSizeAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class LayoutDecreaseSizeAction : ObservableObject, IAction { private static readonly IUserSettingsService UserSettingsService = Ioc.Default.GetRequiredService(); - private readonly IDisplayPageContext DisplayPageContext = Ioc.Default.GetRequiredService(); private readonly IContentPageContext ContentPageContext = Ioc.Default.GetRequiredService(); public string Label - => "DecreaseSize".GetLocalizedResource(); + => Strings.DecreaseSize.GetLocalizedResource(); public string Description - => "LayoutDecreaseSizeDescription".GetLocalizedResource(); + => Strings.LayoutDecreaseSizeDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Layout; public HotKey HotKey => new(Keys.Subtract, KeyModifiers.Ctrl); @@ -188,15 +220,16 @@ public HotKey MediaHotKey public bool IsExecutable => ContentPageContext.PageType is not ContentPageTypes.Home && - ((DisplayPageContext.LayoutType == LayoutTypes.Details && UserSettingsService.LayoutSettingsService.DetailsViewSize > DetailsViewSizeKind.Compact) || - (DisplayPageContext.LayoutType == LayoutTypes.List && UserSettingsService.LayoutSettingsService.ListViewSize > ListViewSizeKind.Compact) || - (DisplayPageContext.LayoutType == LayoutTypes.Grid && UserSettingsService.LayoutSettingsService.GridViewSize > GridViewSizeKind.Small) || - (DisplayPageContext.LayoutType == LayoutTypes.Columns && UserSettingsService.LayoutSettingsService.ColumnsViewSize > ColumnsViewSizeKind.Compact)); + ContentPageContext.ShellPage?.InstanceViewModel.FolderSettings.LayoutMode is FolderLayoutModes layoutMode && + ((layoutMode is FolderLayoutModes.DetailsView && UserSettingsService.LayoutSettingsService.DetailsViewSize > DetailsViewSizeKind.Compact) || + (layoutMode is FolderLayoutModes.ListView && UserSettingsService.LayoutSettingsService.ListViewSize > ListViewSizeKind.Compact) || + (layoutMode is FolderLayoutModes.CardsView && UserSettingsService.LayoutSettingsService.CardsViewSize > CardsViewSizeKind.Small) || + (layoutMode is FolderLayoutModes.GridView && UserSettingsService.LayoutSettingsService.GridViewSize > GridViewSizeKind.Small) || + (layoutMode is FolderLayoutModes.ColumnView && UserSettingsService.LayoutSettingsService.ColumnsViewSize > ColumnsViewSizeKind.Compact)); public LayoutDecreaseSizeAction() { ContentPageContext.PropertyChanged += ContentPageContext_PropertyChanged; - DisplayPageContext.PropertyChanged += DisplayPageContext_PropertyChanged; UserSettingsService.LayoutSettingsService.PropertyChanged += UserSettingsService_PropertyChanged; } @@ -210,16 +243,6 @@ private void ContentPageContext_PropertyChanged(object? sender, PropertyChangedE } } - private void DisplayPageContext_PropertyChanged(object? sender, PropertyChangedEventArgs e) - { - switch (e.PropertyName) - { - case nameof(IDisplayPageContext.LayoutType): - OnPropertyChanged(nameof(IsExecutable)); - break; - } - } - private void UserSettingsService_PropertyChanged(object? sender, PropertyChangedEventArgs e) { switch (e.PropertyName) @@ -228,6 +251,7 @@ private void UserSettingsService_PropertyChanged(object? sender, PropertyChanged case nameof(ILayoutSettingsService.ListViewSize): case nameof(ILayoutSettingsService.GridViewSize): case nameof(ILayoutSettingsService.ColumnsViewSize): + case nameof(ILayoutSettingsService.CardsViewSize): OnPropertyChanged(nameof(IsExecutable)); break; } @@ -235,43 +259,50 @@ private void UserSettingsService_PropertyChanged(object? sender, PropertyChanged public Task ExecuteAsync(object? parameter = null) { - switch (DisplayPageContext.LayoutType) + switch (ContentPageContext.ShellPage?.InstanceViewModel.FolderSettings.LayoutMode) { - case LayoutTypes.Details: + case FolderLayoutModes.DetailsView: if (UserSettingsService.LayoutSettingsService.DetailsViewSize > DetailsViewSizeKind.Compact) UserSettingsService.LayoutSettingsService.DetailsViewSize -= 1; break; - case LayoutTypes.List: + case FolderLayoutModes.ListView: if (UserSettingsService.LayoutSettingsService.ListViewSize > ListViewSizeKind.Compact) UserSettingsService.LayoutSettingsService.ListViewSize -= 1; break; - case LayoutTypes.Tiles: + case FolderLayoutModes.CardsView: + if (UserSettingsService.LayoutSettingsService.CardsViewSize > CardsViewSizeKind.Small) + UserSettingsService.LayoutSettingsService.CardsViewSize -= 1; break; - case LayoutTypes.Grid: + case FolderLayoutModes.GridView: if (UserSettingsService.LayoutSettingsService.GridViewSize > GridViewSizeKind.Small) UserSettingsService.LayoutSettingsService.GridViewSize -= 1; break; - case LayoutTypes.Columns: + case FolderLayoutModes.ColumnView: if (UserSettingsService.LayoutSettingsService.ColumnsViewSize > ColumnsViewSizeKind.Compact) UserSettingsService.LayoutSettingsService.ColumnsViewSize -= 1; break; + default: + break; } return Task.CompletedTask; } } - internal sealed class LayoutIncreaseSizeAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class LayoutIncreaseSizeAction : ObservableObject, IAction { private static readonly IUserSettingsService UserSettingsService = Ioc.Default.GetRequiredService(); - private readonly IDisplayPageContext DisplayPageContext = Ioc.Default.GetRequiredService(); private readonly IContentPageContext ContentPageContext = Ioc.Default.GetRequiredService(); public string Label - => "IncreaseSize".GetLocalizedResource(); + => Strings.IncreaseSize.GetLocalizedResource(); public string Description - => "LayoutIncreaseSizeDescription".GetLocalizedResource(); + => Strings.LayoutIncreaseSizeDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Layout; public HotKey HotKey => new(Keys.Add, KeyModifiers.Ctrl); @@ -281,15 +312,16 @@ public HotKey MediaHotKey public bool IsExecutable => ContentPageContext.PageType is not ContentPageTypes.Home && - ((DisplayPageContext.LayoutType == LayoutTypes.Details && UserSettingsService.LayoutSettingsService.DetailsViewSize < DetailsViewSizeKind.ExtraLarge) || - (DisplayPageContext.LayoutType == LayoutTypes.List && UserSettingsService.LayoutSettingsService.ListViewSize < ListViewSizeKind.ExtraLarge) || - (DisplayPageContext.LayoutType == LayoutTypes.Grid && UserSettingsService.LayoutSettingsService.GridViewSize < GridViewSizeKind.ExtraLarge) || - (DisplayPageContext.LayoutType == LayoutTypes.Columns && UserSettingsService.LayoutSettingsService.ColumnsViewSize < ColumnsViewSizeKind.ExtraLarge)); + ContentPageContext.ShellPage?.InstanceViewModel.FolderSettings.LayoutMode is FolderLayoutModes layoutMode && + ((layoutMode is FolderLayoutModes.DetailsView && UserSettingsService.LayoutSettingsService.DetailsViewSize < DetailsViewSizeKind.ExtraLarge) || + (layoutMode is FolderLayoutModes.ListView && UserSettingsService.LayoutSettingsService.ListViewSize < ListViewSizeKind.ExtraLarge) || + (layoutMode is FolderLayoutModes.CardsView && UserSettingsService.LayoutSettingsService.CardsViewSize < CardsViewSizeKind.ExtraLarge) || + (layoutMode is FolderLayoutModes.GridView && UserSettingsService.LayoutSettingsService.GridViewSize < GridViewSizeKind.ExtraLarge) || + (layoutMode is FolderLayoutModes.ColumnView && UserSettingsService.LayoutSettingsService.ColumnsViewSize < ColumnsViewSizeKind.ExtraLarge)); public LayoutIncreaseSizeAction() { ContentPageContext.PropertyChanged += ContentPageContext_PropertyChanged; - DisplayPageContext.PropertyChanged += DisplayPageContext_PropertyChanged; UserSettingsService.LayoutSettingsService.PropertyChanged += UserSettingsService_PropertyChanged; } @@ -303,16 +335,6 @@ private void ContentPageContext_PropertyChanged(object? sender, PropertyChangedE } } - private void DisplayPageContext_PropertyChanged(object? sender, PropertyChangedEventArgs e) - { - switch (e.PropertyName) - { - case nameof(IDisplayPageContext.LayoutType): - OnPropertyChanged(nameof(IsExecutable)); - break; - } - } - private void UserSettingsService_PropertyChanged(object? sender, PropertyChangedEventArgs e) { switch (e.PropertyName) @@ -328,26 +350,30 @@ private void UserSettingsService_PropertyChanged(object? sender, PropertyChanged public Task ExecuteAsync(object? parameter = null) { - switch (DisplayPageContext.LayoutType) + switch (ContentPageContext.ShellPage?.InstanceViewModel.FolderSettings.LayoutMode) { - case LayoutTypes.Details: + case FolderLayoutModes.DetailsView: if (UserSettingsService.LayoutSettingsService.DetailsViewSize < DetailsViewSizeKind.ExtraLarge) UserSettingsService.LayoutSettingsService.DetailsViewSize += 1; break; - case LayoutTypes.List: + case FolderLayoutModes.ListView: if (UserSettingsService.LayoutSettingsService.ListViewSize < ListViewSizeKind.ExtraLarge) UserSettingsService.LayoutSettingsService.ListViewSize += 1; break; - case LayoutTypes.Tiles: + case FolderLayoutModes.CardsView: + if (UserSettingsService.LayoutSettingsService.CardsViewSize < CardsViewSizeKind.ExtraLarge) + UserSettingsService.LayoutSettingsService.CardsViewSize += 1; break; - case LayoutTypes.Grid: + case FolderLayoutModes.GridView: if (UserSettingsService.LayoutSettingsService.GridViewSize < GridViewSizeKind.ExtraLarge) UserSettingsService.LayoutSettingsService.GridViewSize += 1; break; - case LayoutTypes.Columns: + case FolderLayoutModes.ColumnView: if (UserSettingsService.LayoutSettingsService.ColumnsViewSize < ColumnsViewSizeKind.ExtraLarge) UserSettingsService.LayoutSettingsService.ColumnsViewSize += 1; break; + default: + break; } return Task.CompletedTask; diff --git a/src/Files.App/Actions/Display/SortAction.cs b/src/Files.App/Actions/Display/SortAction.cs index 2f97fe50b4e4..4ef95d73fc00 100644 --- a/src/Files.App/Actions/Display/SortAction.cs +++ b/src/Files.App/Actions/Display/SortAction.cs @@ -1,135 +1,148 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class SortByNameAction : SortByAction + [GeneratedRichCommand] + internal sealed partial class SortByNameAction : SortByAction { protected override SortOption SortOption => SortOption.Name; public override string Label - => "Name".GetLocalizedResource(); + => Strings.Name.GetLocalizedResource(); public override string Description - => "SortByNameDescription".GetLocalizedResource(); + => Strings.SortByNameDescription.GetLocalizedResource(); } - internal sealed class SortByDateModifiedAction : SortByAction + [GeneratedRichCommand] + internal sealed partial class SortByDateModifiedAction : SortByAction { protected override SortOption SortOption => SortOption.DateModified; public override string Label - => "DateModifiedLowerCase".GetLocalizedResource(); + => Strings.DateModifiedLowerCase.GetLocalizedResource(); public override string Description - => "SortByDateModifiedDescription".GetLocalizedResource(); + => Strings.SortByDateModifiedDescription.GetLocalizedResource(); } - internal sealed class SortByDateCreatedAction : SortByAction + [GeneratedRichCommand] + internal sealed partial class SortByDateCreatedAction : SortByAction { protected override SortOption SortOption => SortOption.DateCreated; public override string Label - => "DateCreated".GetLocalizedResource(); + => Strings.DateCreated.GetLocalizedResource(); public override string Description - => "SortByDateCreatedDescription".GetLocalizedResource(); + => Strings.SortByDateCreatedDescription.GetLocalizedResource(); } - internal sealed class SortBySizeAction : SortByAction + [GeneratedRichCommand] + internal sealed partial class SortBySizeAction : SortByAction { protected override SortOption SortOption => SortOption.Size; public override string Label - => "Size".GetLocalizedResource(); + => Strings.Size.GetLocalizedResource(); public override string Description - => "SortBySizeDescription".GetLocalizedResource(); + => Strings.SortBySizeDescription.GetLocalizedResource(); } - internal sealed class SortByTypeAction : SortByAction + [GeneratedRichCommand] + internal sealed partial class SortByTypeAction : SortByAction { protected override SortOption SortOption => SortOption.FileType; public override string Label - => "Type".GetLocalizedResource(); + => Strings.Type.GetLocalizedResource(); public override string Description - => "SortByTypeDescription".GetLocalizedResource(); + => Strings.SortByTypeDescription.GetLocalizedResource(); } - internal sealed class SortBySyncStatusAction : SortByAction + [GeneratedRichCommand] + internal sealed partial class SortBySyncStatusAction : SortByAction { protected override SortOption SortOption => SortOption.SyncStatus; public override string Label - => "SyncStatus".GetLocalizedResource(); + => Strings.SyncStatus.GetLocalizedResource(); public override string Description - => "SortBySyncStatusDescription".GetLocalizedResource(); + => Strings.SortBySyncStatusDescription.GetLocalizedResource(); protected override bool GetIsExecutable(ContentPageTypes pageType) => pageType is ContentPageTypes.CloudDrive; } - internal sealed class SortByTagAction : SortByAction + [GeneratedRichCommand] + internal sealed partial class SortByTagAction : SortByAction { protected override SortOption SortOption => SortOption.FileTag; public override string Label - => "FileTags".GetLocalizedResource(); + => Strings.FileTags.GetLocalizedResource(); public override string Description - => "SortByTagDescription".GetLocalizedResource(); + => Strings.SortByTagDescription.GetLocalizedResource(); } - internal sealed class SortByPathAction : SortByAction + [GeneratedRichCommand] + internal sealed partial class SortByPathAction : SortByAction { protected override SortOption SortOption => SortOption.Path; public override string Label - => "Path".GetLocalizedResource(); + => Strings.Path.GetLocalizedResource(); public override string Description - => "SortByPathDescription".GetLocalizedResource(); + => Strings.SortByPathDescription.GetLocalizedResource(); + + public override string ExtendedLabel + => Label; protected override bool GetIsExecutable(ContentPageTypes pageType) => pageType is ContentPageTypes.SearchResults; } - internal sealed class SortByOriginalFolderAction : SortByAction + [GeneratedRichCommand] + internal sealed partial class SortByOriginalFolderAction : SortByAction { protected override SortOption SortOption => SortOption.OriginalFolder; public override string Label - => "OriginalFolder".GetLocalizedResource(); + => Strings.OriginalFolder.GetLocalizedResource(); public override string Description - => "SortByOriginalFolderDescription".GetLocalizedResource(); + => Strings.SortByOriginalFolderDescription.GetLocalizedResource(); protected override bool GetIsExecutable(ContentPageTypes pageType) => pageType is ContentPageTypes.RecycleBin; } - internal sealed class SortByDateDeletedAction : SortByAction + [GeneratedRichCommand] + internal sealed partial class SortByDateDeletedAction : SortByAction { protected override SortOption SortOption => SortOption.DateDeleted; public override string Label - => "DateDeleted".GetLocalizedResource(); + => Strings.DateDeleted.GetLocalizedResource(); public override string Description - => "SortByDateDeletedDescription".GetLocalizedResource(); + => Strings.SortByDateDeletedDescription.GetLocalizedResource(); protected override bool GetIsExecutable(ContentPageTypes pageType) => pageType is ContentPageTypes.RecycleBin; @@ -147,6 +160,12 @@ internal abstract class SortByAction : ObservableObject, IToggleAction public abstract string Description { get; } + public virtual ActionCategory Category + => ActionCategory.Sorting; + + public virtual string ExtendedLabel + => Description; + public bool IsOn => displayContext.SortOption == SortOption; @@ -165,6 +184,7 @@ public SortByAction() public Task ExecuteAsync(object? parameter = null) { displayContext.SortOption = SortOption; + LayoutHelpers.UpdateOpenTabsPreferences(); return Task.CompletedTask; } @@ -184,15 +204,19 @@ private void DisplayContext_PropertyChanged(object? sender, PropertyChangedEvent } } - internal sealed class SortAscendingAction : ObservableObject, IToggleAction + [GeneratedRichCommand] + internal sealed partial class SortAscendingAction : ObservableObject, IToggleAction { private readonly IDisplayPageContext context; public string Label - => "Ascending".GetLocalizedResource(); + => Strings.Ascending.GetLocalizedResource(); public string Description - => "SortAscendingDescription".GetLocalizedResource(); + => Strings.SortAscendingDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Sorting; public bool IsOn => context.SortDirection is SortDirection.Ascending; @@ -207,6 +231,7 @@ public SortAscendingAction() public Task ExecuteAsync(object? parameter = null) { context.SortDirection = SortDirection.Ascending; + LayoutHelpers.UpdateOpenTabsPreferences(); return Task.CompletedTask; } @@ -218,15 +243,19 @@ private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) } } - internal sealed class SortDescendingAction : ObservableObject, IToggleAction + [GeneratedRichCommand] + internal sealed partial class SortDescendingAction : ObservableObject, IToggleAction { private readonly IDisplayPageContext context; public string Label - => "Descending".GetLocalizedResource(); + => Strings.Descending.GetLocalizedResource(); public string Description - => "SortDescendingDescription".GetLocalizedResource(); + => Strings.SortDescendingDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Sorting; public bool IsOn => context.SortDirection is SortDirection.Descending; @@ -241,6 +270,7 @@ public SortDescendingAction() public Task ExecuteAsync(object? parameter = null) { context.SortDirection = SortDirection.Descending; + LayoutHelpers.UpdateOpenTabsPreferences(); return Task.CompletedTask; } @@ -252,15 +282,19 @@ private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) } } + [GeneratedRichCommand] internal sealed class ToggleSortDirectionAction : IAction { private readonly IDisplayPageContext context; public string Label - => "ToggleSortDirection".GetLocalizedResource(); + => Strings.ToggleSortDirection.GetLocalizedResource(); public string Description - => "ToggleSortDirectionDescription".GetLocalizedResource(); + => Strings.ToggleSortDirectionDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Sorting; public ToggleSortDirectionAction() { @@ -274,6 +308,8 @@ context.SortDirection is SortDirection.Descending ? SortDirection.Ascending : SortDirection.Descending; + LayoutHelpers.UpdateOpenTabsPreferences(); + return Task.CompletedTask; } } diff --git a/src/Files.App/Actions/Display/SortFilesAndFoldersTogetherAction.cs b/src/Files.App/Actions/Display/SortFilesAndFoldersTogetherAction.cs index 93891108decb..30d1e280d1be 100644 --- a/src/Files.App/Actions/Display/SortFilesAndFoldersTogetherAction.cs +++ b/src/Files.App/Actions/Display/SortFilesAndFoldersTogetherAction.cs @@ -1,17 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class SortFilesAndFoldersTogetherAction : ObservableObject, IToggleAction + [GeneratedRichCommand] + internal sealed partial class SortFilesAndFoldersTogetherAction : ObservableObject, IToggleAction { private readonly IDisplayPageContext context; public string Label - => "SortFilesAndFoldersTogether".GetLocalizedResource(); + => Strings.SortFilesAndFoldersTogether.GetLocalizedResource(); public string Description - => "SortFilesAndFoldersTogetherDescription".GetLocalizedResource(); + => Strings.SortFilesAndFoldersTogetherDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Sorting; public bool IsOn => context.SortDirectoriesAlongsideFiles; @@ -26,6 +30,7 @@ public SortFilesAndFoldersTogetherAction() public Task ExecuteAsync(object? parameter = null) { context.SortDirectoriesAlongsideFiles = true; + LayoutHelpers.UpdateOpenTabsPreferences(); return Task.CompletedTask; } diff --git a/src/Files.App/Actions/Display/SortFilesFirstAction.cs b/src/Files.App/Actions/Display/SortFilesFirstAction.cs index ec97c4861a5f..4248bb72bbfa 100644 --- a/src/Files.App/Actions/Display/SortFilesFirstAction.cs +++ b/src/Files.App/Actions/Display/SortFilesFirstAction.cs @@ -1,17 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class SortFilesFirstAction : ObservableObject, IToggleAction + [GeneratedRichCommand] + internal sealed partial class SortFilesFirstAction : ObservableObject, IToggleAction { private readonly IDisplayPageContext context; public string Label - => "SortFilesFirst".GetLocalizedResource(); + => Strings.SortFilesFirst.GetLocalizedResource(); public string Description - => "SortFilesFirstDescription".GetLocalizedResource(); + => Strings.SortFilesFirstDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Sorting; public bool IsOn => context.SortFilesFirst && !context.SortDirectoriesAlongsideFiles; @@ -27,6 +31,7 @@ public Task ExecuteAsync(object? parameter = null) { context.SortFilesFirst = true; context.SortDirectoriesAlongsideFiles = false; + LayoutHelpers.UpdateOpenTabsPreferences(); return Task.CompletedTask; } diff --git a/src/Files.App/Actions/Display/SortFoldersFirstAction.cs b/src/Files.App/Actions/Display/SortFoldersFirstAction.cs index 5550d091b63a..97dafae18da7 100644 --- a/src/Files.App/Actions/Display/SortFoldersFirstAction.cs +++ b/src/Files.App/Actions/Display/SortFoldersFirstAction.cs @@ -1,17 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class SortFoldersFirstAction : ObservableObject, IToggleAction + [GeneratedRichCommand] + internal sealed partial class SortFoldersFirstAction : ObservableObject, IToggleAction { private readonly IDisplayPageContext context; public string Label - => "SortFoldersFirst".GetLocalizedResource(); + => Strings.SortFoldersFirst.GetLocalizedResource(); public string Description - => "SortFoldersFirstDescription".GetLocalizedResource(); + => Strings.SortFoldersFirstDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Sorting; public bool IsOn => !context.SortFilesFirst && !context.SortDirectoriesAlongsideFiles; @@ -27,6 +31,7 @@ public Task ExecuteAsync(object? parameter = null) { context.SortFilesFirst = false; context.SortDirectoriesAlongsideFiles = false; + LayoutHelpers.UpdateOpenTabsPreferences(); return Task.CompletedTask; } diff --git a/src/Files.App/Actions/FileSystem/AddItemAction.cs b/src/Files.App/Actions/FileSystem/AddItemAction.cs index 07ab95721dab..c1e7d50b8ec6 100644 --- a/src/Files.App/Actions/FileSystem/AddItemAction.cs +++ b/src/Files.App/Actions/FileSystem/AddItemAction.cs @@ -1,9 +1,10 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class AddItemAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class AddItemAction : ObservableObject, IAction { private readonly IContentPageContext context; @@ -12,13 +13,19 @@ internal sealed class AddItemAction : ObservableObject, IAction private readonly AddItemDialogViewModel viewModel = new(); public string Label - => "BaseLayoutContextFlyoutNew/Label".GetLocalizedResource(); + => Strings.BaseLayoutContextFlyoutNew_Label.GetLocalizedResource(); + + public string ExtendedLabel + => Description; public string Description - => "AddItemDescription".GetLocalizedResource(); + => Strings.AddItemDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Create; public RichGlyph Glyph - => new(opacityStyle: "ColorIconNew"); + => new(themedIconStyle: "App.ThemedIcons.New.Item"); public HotKey HotKey => new(Keys.I, KeyModifiers.CtrlShift); diff --git a/src/Files.App/Actions/FileSystem/BaseDeleteAction.cs b/src/Files.App/Actions/FileSystem/BaseDeleteAction.cs index 21311f6bd177..d4d64e52ad1d 100644 --- a/src/Files.App/Actions/FileSystem/BaseDeleteAction.cs +++ b/src/Files.App/Actions/FileSystem/BaseDeleteAction.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.Storage; @@ -11,6 +11,9 @@ internal abstract class BaseDeleteAction : BaseUIAction protected readonly IContentPageContext context; + public virtual ActionCategory Category + => ActionCategory.FileSystem; + public override bool IsExecutable => context.HasSelection && (!context.ShellPage?.SlimContentPage?.IsRenamingItem ?? false) && diff --git a/src/Files.App/Actions/FileSystem/CopyItemFromHomeAction.cs b/src/Files.App/Actions/FileSystem/CopyItemFromHomeAction.cs new file mode 100644 index 000000000000..3221903ff501 --- /dev/null +++ b/src/Files.App/Actions/FileSystem/CopyItemFromHomeAction.cs @@ -0,0 +1,124 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.Extensions.Logging; +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 ActionCategory Category + => ActionCategory.FileSystem; + + public RichGlyph Glyph + => new(themedIconStyle: "App.ThemedIcons.Copy"); + + public bool IsAccessibleGlobally + => false; + + public bool IsExecutable + => GetIsExecutable(); + + public CopyItemFromHomeAction() + { + context = Ioc.Default.GetRequiredService(); + HomePageContext = Ioc.Default.GetRequiredService(); + } + + 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); + } + } +} diff --git a/src/Files.App/Actions/FileSystem/CopyItemPathAction.cs b/src/Files.App/Actions/FileSystem/CopyItemPathAction.cs new file mode 100644 index 000000000000..1093989f0dcc --- /dev/null +++ b/src/Files.App/Actions/FileSystem/CopyItemPathAction.cs @@ -0,0 +1,60 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Windows.ApplicationModel.DataTransfer; + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed class CopyItemPathAction : IAction + { + private readonly IContentPageContext context; + + public string Label + => Strings.CopyItemPath.GetLocalizedResource(); + + public string Description + => Strings.CopyItemPathDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.FileSystem; + + public RichGlyph Glyph + => new RichGlyph(themedIconStyle: "App.ThemedIcons.CopyAsPath"); + + public HotKey HotKey + => new(Keys.C, KeyModifiers.CtrlShift); + + public bool IsExecutable + => context.HasSelection; + + public CopyItemPathAction() + { + context = Ioc.Default.GetRequiredService(); + } + + public Task ExecuteAsync(object? parameter = null) + { + if (context.ShellPage?.SlimContentPage is not null) + { + var path = context.ShellPage.SlimContentPage.SelectedItems is not null + ? context.ShellPage.SlimContentPage.SelectedItems.Select(x => x.ItemPath).Aggregate((accum, current) => accum + "\n" + current) + : context.ShellPage.ShellViewModel.WorkingDirectory; + + if (FtpHelpers.IsFtpPath(path)) + path = path.Replace('\\', '/'); + + SafetyExtensions.IgnoreExceptions(() => + { + DataPackage data = new(); + data.SetText(path); + + Clipboard.SetContent(data); + Clipboard.Flush(); + }); + } + + return Task.CompletedTask; + } + } +} diff --git a/src/Files.App/Actions/FileSystem/CopyItemPathWithQuotesAction.cs b/src/Files.App/Actions/FileSystem/CopyItemPathWithQuotesAction.cs new file mode 100644 index 000000000000..2d6374e43b69 --- /dev/null +++ b/src/Files.App/Actions/FileSystem/CopyItemPathWithQuotesAction.cs @@ -0,0 +1,61 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Windows.ApplicationModel.DataTransfer; + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed class CopyItemPathWithQuotesAction : IAction + { + private readonly IContentPageContext context; + + public string Label + => Strings.CopyItemPathWithQuotes.GetLocalizedResource(); + + public string Description + => Strings.CopyItemPathWithQuotesDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.FileSystem; + + public RichGlyph Glyph + => new RichGlyph(themedIconStyle: "App.ThemedIcons.CopyAsPath"); + + public HotKey HotKey + => new(Keys.C, KeyModifiers.CtrlAlt); + + public bool IsExecutable + => context.HasSelection; + + public CopyItemPathWithQuotesAction() + { + context = Ioc.Default.GetRequiredService(); + } + + public Task ExecuteAsync(object? parameter = null) + { + if (context.ShellPage?.SlimContentPage is not null) + { + var selectedItems = context.ShellPage.SlimContentPage.SelectedItems; + var path = selectedItems is not null + ? string.Join("\n", selectedItems.Select(item => $"\"{item.ItemPath}\"")) + : context.ShellPage.ShellViewModel.WorkingDirectory; + + if (FtpHelpers.IsFtpPath(path)) + path = path.Replace('\\', '/'); + + SafetyExtensions.IgnoreExceptions(() => + { + DataPackage data = new(); + data.SetText(path); + + Clipboard.SetContent(data); + Clipboard.Flush(); + }); + } + + return Task.CompletedTask; + } + } +} diff --git a/src/Files.App/Actions/FileSystem/CopyPathAction.cs b/src/Files.App/Actions/FileSystem/CopyPathAction.cs index af9f08452482..c1d1cf13b256 100644 --- a/src/Files.App/Actions/FileSystem/CopyPathAction.cs +++ b/src/Files.App/Actions/FileSystem/CopyPathAction.cs @@ -1,28 +1,32 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.ApplicationModel.DataTransfer; namespace Files.App.Actions { + [GeneratedRichCommand] internal sealed class CopyPathAction : IAction { private readonly IContentPageContext context; public string Label - => "CopyPath".GetLocalizedResource(); + => Strings.CopyPath.GetLocalizedResource(); public string Description - => "CopyPathDescription".GetLocalizedResource(); + => Strings.CopyPathDescription.GetLocalizedResource(); - public RichGlyph Glyph - => new RichGlyph(opacityStyle: "ColorIconCopyPath"); + public ActionCategory Category + => ActionCategory.FileSystem; - public HotKey HotKey - => new(Keys.C, KeyModifiers.CtrlShift); + public RichGlyph Glyph + => new RichGlyph(themedIconStyle: "App.ThemedIcons.CopyAsPath"); - public bool IsExecutable - => context.HasSelection; + public bool IsExecutable => + context.PageType != ContentPageTypes.Home && + context.PageType != ContentPageTypes.RecycleBin && + context.PageType != ContentPageTypes.ReleaseNotes && + context.PageType != ContentPageTypes.Settings; public CopyPathAction() { @@ -33,12 +37,10 @@ public Task ExecuteAsync(object? parameter = null) { if (context.ShellPage?.SlimContentPage is not null) { - var path = context.ShellPage.SlimContentPage.SelectedItems is not null - ? context.ShellPage.SlimContentPage.SelectedItems.Select(x => x.ItemPath).Aggregate((accum, current) => accum + "\n" + current) - : context.ShellPage.ShellViewModel.WorkingDirectory; + var path = context.ShellPage.ShellViewModel.WorkingDirectory; if (FtpHelpers.IsFtpPath(path)) - path = path.Replace("\\", "/", StringComparison.Ordinal); + path = path.Replace('\\', '/'); SafetyExtensions.IgnoreExceptions(() => { diff --git a/src/Files.App/Actions/FileSystem/CopyPathWithQuotesAction.cs b/src/Files.App/Actions/FileSystem/CopyPathWithQuotesAction.cs index 9bbc4ec5d00f..e6da45c48893 100644 --- a/src/Files.App/Actions/FileSystem/CopyPathWithQuotesAction.cs +++ b/src/Files.App/Actions/FileSystem/CopyPathWithQuotesAction.cs @@ -1,28 +1,32 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.ApplicationModel.DataTransfer; namespace Files.App.Actions { + [GeneratedRichCommand] internal sealed class CopyPathWithQuotesAction : IAction { private readonly IContentPageContext context; public string Label - => "CopyPathWithQuotes".GetLocalizedResource(); + => Strings.CopyPathWithQuotes.GetLocalizedResource(); public string Description - => "CopyPathWithQuotesDescription".GetLocalizedResource(); + => Strings.CopyPathWithQuotesDescription.GetLocalizedResource(); - public RichGlyph Glyph - => new RichGlyph(opacityStyle: "ColorIconCopyPath"); + public ActionCategory Category + => ActionCategory.FileSystem; - public HotKey HotKey - => new(Keys.C, KeyModifiers.CtrlAlt); + public RichGlyph Glyph + => new RichGlyph(themedIconStyle: "App.ThemedIcons.CopyAsPath"); - public bool IsExecutable - => context.HasSelection; + public bool IsExecutable => + context.PageType != ContentPageTypes.Home && + context.PageType != ContentPageTypes.RecycleBin && + context.PageType != ContentPageTypes.ReleaseNotes && + context.PageType != ContentPageTypes.Settings; public CopyPathWithQuotesAction() { @@ -33,13 +37,10 @@ public Task ExecuteAsync(object? parameter = null) { if (context.ShellPage?.SlimContentPage is not null) { - var selectedItems = context.ShellPage.SlimContentPage.SelectedItems; - var path = selectedItems is not null - ? string.Join("\n", selectedItems.Select(item => $"\"{item.ItemPath}\"")) - : context.ShellPage.ShellViewModel.WorkingDirectory; + var path = "\"" + context.ShellPage.ShellViewModel.WorkingDirectory + "\""; if (FtpHelpers.IsFtpPath(path)) - path = path.Replace("\\", "/", StringComparison.Ordinal); + path = path.Replace('\\', '/'); SafetyExtensions.IgnoreExceptions(() => { diff --git a/src/Files.App/Actions/FileSystem/CreateAlternateDataStreamAction.cs b/src/Files.App/Actions/FileSystem/CreateAlternateDataStreamAction.cs new file mode 100644 index 000000000000..749e0e9c3199 --- /dev/null +++ b/src/Files.App/Actions/FileSystem/CreateAlternateDataStreamAction.cs @@ -0,0 +1,123 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Controls; +using Windows.Foundation.Metadata; + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class CreateAlternateDataStreamAction : BaseUIAction, IAction + { + private readonly IContentPageContext context; + + private static readonly IFoldersSettingsService FoldersSettingsService = Ioc.Default.GetRequiredService(); + private static readonly IApplicationSettingsService ApplicationSettingsService = Ioc.Default.GetRequiredService(); + + public string Label + => Strings.CreateAlternateDataStream.GetLocalizedResource(); + + public string Description + => Strings.CreateAlternateDataStreamDescription.GetLocalizedFormatResource(context.SelectedItems.Count); + + public ActionCategory Category + => ActionCategory.Create; + + public RichGlyph Glyph + => new RichGlyph(themedIconStyle: "App.ThemedIcons.AltDataStream"); + + public override bool IsExecutable => + context.HasSelection && + context.CanCreateItem && + UIHelpers.CanShowDialog; + + public CreateAlternateDataStreamAction() + { + context = Ioc.Default.GetRequiredService(); + + context.PropertyChanged += Context_PropertyChanged; + } + + public async Task ExecuteAsync(object? parameter = null) + { + var nameDialog = DynamicDialogFactory.GetFor_CreateAlternateDataStreamDialog(); + await nameDialog.TryShowAsync(); + + if (nameDialog.DynamicResult != DynamicDialogResult.Primary) + return; + + var userInput = nameDialog.ViewModel.AdditionalData as string; + await Task.WhenAll(context.SelectedItems.Select(async selectedItem => + { + var isDateOk = Win32Helper.GetFileDateModified(selectedItem.ItemPath, out var dateModified); + var isReadOnly = Win32Helper.HasFileAttribute(selectedItem.ItemPath, System.IO.FileAttributes.ReadOnly); + + // Unset read-only attribute (#7534) + if (isReadOnly) + Win32Helper.UnsetFileAttribute(selectedItem.ItemPath, System.IO.FileAttributes.ReadOnly); + + if (!Win32Helper.WriteStringToFile($"{selectedItem.ItemPath}:{userInput}", "")) + { + var dialog = new ContentDialog + { + Title = Strings.ErrorCreatingDataStreamTitle.GetLocalizedResource(), + Content = Strings.ErrorCreatingDataStreamDescription.GetLocalizedResource(), + PrimaryButtonText = "Ok".GetLocalizedResource() + }; + + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + dialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; + + await dialog.TryShowAsync(); + } + + // Restore read-only attribute (#7534) + if (isReadOnly) + Win32Helper.SetFileAttribute(selectedItem.ItemPath, System.IO.FileAttributes.ReadOnly); + + // Restore date modified + if (isDateOk) + Win32Helper.SetFileDateModified(selectedItem.ItemPath, dateModified); + })); + + if (context.ShellPage is null) + return; + + if (FoldersSettingsService.AreAlternateStreamsVisible) + await context.ShellPage.Refresh_Click(); + else if (ApplicationSettingsService.ShowDataStreamsAreHiddenPrompt) + { + var dialog = new ContentDialog + { + Title = Strings.DataStreamsAreHiddenTitle.GetLocalizedResource(), + Content = Strings.DataStreamsAreHiddenDescription.GetLocalizedResource(), + PrimaryButtonText = Strings.Yes.GetLocalizedResource(), + SecondaryButtonText = Strings.DontShowAgain.GetLocalizedResource() + }; + + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + dialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; + + var result = await dialog.TryShowAsync(); + if (result == ContentDialogResult.Primary) + { + FoldersSettingsService.AreAlternateStreamsVisible = true; + await context.ShellPage.Refresh_Click(); + } + else + ApplicationSettingsService.ShowDataStreamsAreHiddenPrompt = false; + } + } + + private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + switch (e.PropertyName) + { + case nameof(IContentPageContext.HasSelection): + case nameof(IContentPageContext.CanCreateItem): + OnPropertyChanged(nameof(IsExecutable)); + break; + } + } + } +} diff --git a/src/Files.App/Actions/FileSystem/CreateFileAction.cs b/src/Files.App/Actions/FileSystem/CreateFileAction.cs new file mode 100644 index 000000000000..ff16196d84cd --- /dev/null +++ b/src/Files.App/Actions/FileSystem/CreateFileAction.cs @@ -0,0 +1,59 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class CreateFileAction : BaseUIAction, IAction + { + private readonly IContentPageContext context; + + public string Label + => Strings.File.GetLocalizedResource(); + + public string Description + => Strings.NewFile.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Create; + + public string AccessKey + => "I"; + + public string AutomationId + => "File"; + + public RichGlyph Glyph + => new(baseGlyph: "\uE7C3"); + + public override bool IsExecutable => + context.CanCreateItem && + UIHelpers.CanShowDialog; + + public CreateFileAction() + { + context = Ioc.Default.GetRequiredService(); + + context.PropertyChanged += Context_PropertyChanged; + } + + public Task ExecuteAsync(object? parameter = null) + { + if (context.ShellPage is not null) + _ = UIFilesystemHelpers.CreateFileFromDialogResultTypeAsync(AddItemDialogItemType.File, null, context.ShellPage); + + return Task.CompletedTask; + } + + private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + switch (e.PropertyName) + { + case nameof(IContentPageContext.CanCreateItem): + case nameof(IContentPageContext.HasSelection): + OnPropertyChanged(nameof(IsExecutable)); + break; + } + } + } +} diff --git a/src/Files.App/Actions/FileSystem/CreateFolderAction.cs b/src/Files.App/Actions/FileSystem/CreateFolderAction.cs index 6a4d1c18c625..63ecf1cddd4e 100644 --- a/src/Files.App/Actions/FileSystem/CreateFolderAction.cs +++ b/src/Files.App/Actions/FileSystem/CreateFolderAction.cs @@ -1,17 +1,27 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class CreateFolderAction : BaseUIAction, IAction + [GeneratedRichCommand] + internal sealed partial class CreateFolderAction : BaseUIAction, IAction { private readonly IContentPageContext context; public string Label - => "Folder".GetLocalizedResource(); + => Strings.Folder.GetLocalizedResource(); public string Description - => "CreateFolderDescription".GetLocalizedResource(); + => Strings.CreateFolderDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Create; + + public string AccessKey + => "F"; + + public string AutomationId + => "InnerNavigationToolbarNewFolderButton"; public HotKey HotKey => new(Keys.N, KeyModifiers.CtrlShift); @@ -33,7 +43,7 @@ public CreateFolderAction() public Task ExecuteAsync(object? parameter = null) { if (context.ShellPage is not null) - UIFilesystemHelpers.CreateFileFromDialogResultTypeAsync(AddItemDialogItemType.Folder, null!, context.ShellPage); + _ = UIFilesystemHelpers.CreateFileFromDialogResultTypeAsync(AddItemDialogItemType.Folder, null!, context.ShellPage); return Task.CompletedTask; } diff --git a/src/Files.App/Actions/FileSystem/CreateFolderWithSelectionAction.cs b/src/Files.App/Actions/FileSystem/CreateFolderWithSelectionAction.cs index 435f47719231..5608fb618abc 100644 --- a/src/Files.App/Actions/FileSystem/CreateFolderWithSelectionAction.cs +++ b/src/Files.App/Actions/FileSystem/CreateFolderWithSelectionAction.cs @@ -1,20 +1,24 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class CreateFolderWithSelectionAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class CreateFolderWithSelectionAction : ObservableObject, IAction { private readonly IContentPageContext context; public string Label - => "CreateFolderWithSelection".GetLocalizedResource(); + => Strings.CreateFolderWithSelection.GetLocalizedResource(); public string Description - => "CreateFolderWithSelectionDescription".GetLocalizedResource(); + => Strings.CreateFolderWithSelectionDescription.GetLocalizedFormatResource(context.SelectedItems.Count); + + public ActionCategory Category + => ActionCategory.Create; public RichGlyph Glyph - => new(opacityStyle: "ColorIconNewFolder"); + => new(themedIconStyle: "App.ThemedIcons.New.Folder"); public bool IsExecutable => context.ShellPage is not null && diff --git a/src/Files.App/Actions/FileSystem/CreateShortcutAction.cs b/src/Files.App/Actions/FileSystem/CreateShortcutAction.cs index 36af33835dd2..c1f84f252b9d 100644 --- a/src/Files.App/Actions/FileSystem/CreateShortcutAction.cs +++ b/src/Files.App/Actions/FileSystem/CreateShortcutAction.cs @@ -1,20 +1,24 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class CreateShortcutAction : BaseUIAction, IAction + [GeneratedRichCommand] + internal sealed partial class CreateShortcutAction : BaseUIAction, IAction { private readonly IContentPageContext context; public string Label - => "CreateShortcut".GetLocalizedResource(); + => Strings.CreateShortcut.GetLocalizedResource(); public string Description - => "CreateShortcutDescription".GetLocalizedResource(); + => Strings.CreateShortcutDescription.GetLocalizedFormatResource(context.SelectedItems.Count); + + public ActionCategory Category + => ActionCategory.Create; public RichGlyph Glyph - => new(opacityStyle: "ColorIconShortcut"); + => new(themedIconStyle: "App.ThemedIcons.URL"); public override bool IsExecutable => context.HasSelection && diff --git a/src/Files.App/Actions/FileSystem/CreateShortcutFromDialogAction.cs b/src/Files.App/Actions/FileSystem/CreateShortcutFromDialogAction.cs index 0143590018f6..e47e1f39d2a7 100644 --- a/src/Files.App/Actions/FileSystem/CreateShortcutFromDialogAction.cs +++ b/src/Files.App/Actions/FileSystem/CreateShortcutFromDialogAction.cs @@ -1,17 +1,27 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class CreateShortcutFromDialogAction : BaseUIAction, IAction + [GeneratedRichCommand] + internal sealed partial class CreateShortcutFromDialogAction : BaseUIAction, IAction { private readonly IContentPageContext context; public string Label - => "Shortcut".GetLocalizedResource(); + => Strings.Shortcut.GetLocalizedResource(); public string Description - => "CreateShortcutFromDialogDescription".GetLocalizedResource(); + => Strings.CreateShortcutFromDialogDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Create; + + public string AccessKey + => "S"; + + public string AutomationId + => "InnerNavigationToolbarNewShortcutButton"; public RichGlyph Glyph => new("\uE71B"); diff --git a/src/Files.App/Actions/FileSystem/DeleteItemAction.cs b/src/Files.App/Actions/FileSystem/DeleteItemAction.cs index 8748c73ddf53..34f768e804fc 100644 --- a/src/Files.App/Actions/FileSystem/DeleteItemAction.cs +++ b/src/Files.App/Actions/FileSystem/DeleteItemAction.cs @@ -1,18 +1,25 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class DeleteItemAction : BaseDeleteAction, IAction + [GeneratedRichCommand] + internal sealed partial class DeleteItemAction : BaseDeleteAction, IAction { public string Label - => "Delete".GetLocalizedResource(); + => Strings.Delete.GetLocalizedResource(); public string Description - => "DeleteItemDescription".GetLocalizedResource(); + => Strings.DeleteItemDescription.GetLocalizedFormatResource(context.SelectedItems.Count); + + public ActionCategory Category + => ActionCategory.FileSystem; public RichGlyph Glyph - => new RichGlyph(opacityStyle: "ColorIconDelete"); + => new RichGlyph(themedIconStyle: "App.ThemedIcons.Delete"); + + public string AutomationId + => "InnerNavigationToolbarDeleteButton"; public HotKey HotKey => new(Keys.Delete); diff --git a/src/Files.App/Actions/FileSystem/DeleteItemPermanentlyAction.cs b/src/Files.App/Actions/FileSystem/DeleteItemPermanentlyAction.cs index 5ee4822b968a..e660b63dc705 100644 --- a/src/Files.App/Actions/FileSystem/DeleteItemPermanentlyAction.cs +++ b/src/Files.App/Actions/FileSystem/DeleteItemPermanentlyAction.cs @@ -1,15 +1,19 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class DeleteItemPermanentlyAction : BaseDeleteAction, IAction + [GeneratedRichCommand] + internal sealed partial class DeleteItemPermanentlyAction : BaseDeleteAction, IAction { public string Label - => "DeletePermanently".GetLocalizedResource(); + => Strings.DeletePermanently.GetLocalizedResource(); public string Description - => "DeleteItemPermanentlyDescription".GetLocalizedResource(); + => Strings.DeleteItemPermanentlyDescription.GetLocalizedFormatResource(context.SelectedItems.Count); + + public ActionCategory Category + => ActionCategory.FileSystem; public HotKey HotKey => new(Keys.Delete, KeyModifiers.Shift); diff --git a/src/Files.App/Actions/FileSystem/EmptyRecycleBinAction.cs b/src/Files.App/Actions/FileSystem/EmptyRecycleBinAction.cs index e22037566268..4613eb121dd7 100644 --- a/src/Files.App/Actions/FileSystem/EmptyRecycleBinAction.cs +++ b/src/Files.App/Actions/FileSystem/EmptyRecycleBinAction.cs @@ -1,25 +1,35 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Controls; +using Windows.Foundation.Metadata; namespace Files.App.Actions { - internal sealed class EmptyRecycleBinAction : BaseUIAction, IAction + [GeneratedRichCommand] + internal sealed partial class EmptyRecycleBinAction : BaseUIAction, IAction { + private readonly IStorageTrashBinService StorageTrashBinService = Ioc.Default.GetRequiredService(); + private readonly StatusCenterViewModel StatusCenterViewModel = Ioc.Default.GetRequiredService(); + private readonly IUserSettingsService UserSettingsService = Ioc.Default.GetRequiredService(); private readonly IContentPageContext context; public string Label - => "EmptyRecycleBin".GetLocalizedResource(); + => Strings.EmptyRecycleBin.GetLocalizedResource(); public string Description - => "EmptyRecycleBinDescription".GetLocalizedResource(); + => Strings.EmptyRecycleBinDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.FileSystem; public RichGlyph Glyph - => new(opacityStyle: "ColorIconDelete"); + => new(themedIconStyle: "App.ThemedIcons.Delete"); public override bool IsExecutable => UIHelpers.CanShowDialog && ((context.PageType == ContentPageTypes.RecycleBin && context.HasItem) || - RecycleBinHelpers.RecycleBinHasItems()); + StorageTrashBinService.HasItems()); public EmptyRecycleBinAction() { @@ -30,7 +40,31 @@ public EmptyRecycleBinAction() public async Task ExecuteAsync(object? parameter = null) { - await RecycleBinHelpers.EmptyRecycleBinAsync(); + // TODO: Use AppDialogService + var confirmationDialog = new ContentDialog() + { + Title = Strings.ConfirmEmptyBinDialogTitle.GetLocalizedResource(), + Content = Strings.ConfirmEmptyBinDialogContent.GetLocalizedResource(), + PrimaryButtonText = Strings.Yes.GetLocalizedResource(), + SecondaryButtonText = Strings.Cancel.GetLocalizedResource(), + DefaultButton = ContentDialogButton.Primary + }; + + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + confirmationDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; + + if (UserSettingsService.FoldersSettingsService.DeleteConfirmationPolicy is DeleteConfirmationPolicies.Never || + await confirmationDialog.TryShowAsync() is ContentDialogResult.Primary) + { + var banner = StatusCenterHelper.AddCard_EmptyRecycleBin(ReturnResult.InProgress); + + bool result = await Task.Run(StorageTrashBinService.EmptyTrashBin); + + StatusCenterViewModel.RemoveItem(banner); + + // Post a status based on the result + StatusCenterHelper.AddCard_EmptyRecycleBin(result ? ReturnResult.Success : ReturnResult.Failed); + } } private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) diff --git a/src/Files.App/Actions/FileSystem/FlattenFolderAction.cs b/src/Files.App/Actions/FileSystem/FlattenFolderAction.cs new file mode 100644 index 000000000000..7027d201e3f7 --- /dev/null +++ b/src/Files.App/Actions/FileSystem/FlattenFolderAction.cs @@ -0,0 +1,143 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.Extensions.Logging; +using Microsoft.UI.Xaml.Controls; +using System.IO; +using Windows.Foundation.Metadata; +using Windows.Storage; + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class FlattenFolderAction : ObservableObject, IAction + { + private readonly IContentPageContext context; + private readonly IGeneralSettingsService GeneralSettingsService = Ioc.Default.GetRequiredService(); + + public string Label + => Strings.FlattenFolder.GetLocalizedResource(); + + public string Description + => Strings.FlattenFolderDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.FileSystem; + + public RichGlyph Glyph + => new(themedIconStyle: "App.ThemedIcons.Folder"); + + public bool IsExecutable => + GeneralSettingsService.ShowFlattenOptions && + context.ShellPage is not null && + context.HasSelection && + context.SelectedItems.Count is 1 && + context.SelectedItem is not null && + context.SelectedItem.PrimaryItemAttribute is StorageItemTypes.Folder; + + public FlattenFolderAction() + { + context = Ioc.Default.GetRequiredService(); + + context.PropertyChanged += Context_PropertyChanged; + GeneralSettingsService.PropertyChanged += GeneralSettingsService_PropertyChanged; + } + + public async Task ExecuteAsync(object? parameter = null) + { + if (context.SelectedItem is null) + return; + + var optionsDialog = new ContentDialog() + { + Title = Strings.FlattenFolder.GetLocalizedResource(), + Content = Strings.FlattenFolderDialogContent.GetLocalizedResource(), + PrimaryButtonText = Strings.Flatten.GetLocalizedResource(), + SecondaryButtonText = Strings.Cancel.GetLocalizedResource(), + DefaultButton = ContentDialogButton.Primary + }; + + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + optionsDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; + + var result = await optionsDialog.TryShowAsync(); + if (result != ContentDialogResult.Primary) + return; + + FlattenFolder(context.SelectedItem.ItemPath); + } + + private void FlattenFolder(string path) + { + var containedFolders = Directory.GetDirectories(path); + var containedFiles = Directory.GetFiles(path); + + foreach (var containedFolder in containedFolders) + { + FlattenFolder(containedFolder); + + var folderName = Path.GetFileName(containedFolder); + var destinationPath = Path.Combine(context?.SelectedItem?.ItemPath ?? string.Empty, folderName); + + if (Directory.Exists(destinationPath)) + continue; + + try + { + Directory.Move(containedFolder, destinationPath); + } + catch (Exception ex) + { + App.Logger.LogWarning(ex.Message, $"Folder '{folderName}' already exists in the destination folder."); + } + } + + foreach (var containedFile in containedFiles) + { + var fileName = Path.GetFileName(containedFile); + var destinationPath = Path.Combine(context?.SelectedItem?.ItemPath ?? string.Empty, fileName); + + if (File.Exists(destinationPath)) + continue; + + try + { + File.Move(containedFile, destinationPath); + } + catch (Exception ex) + { + App.Logger.LogWarning(ex.Message, $"Failed to move file '{fileName}'."); + } + } + + if (Directory.GetFiles(path).Length == 0 && Directory.GetDirectories(path).Length == 0) + { + try + { + Directory.Delete(path); + } + catch (Exception ex) + { + App.Logger.LogWarning(ex.Message, $"Failed to delete folder '{path}'."); + } + } + } + + private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + switch (e.PropertyName) + { + case nameof(IContentPageContext.HasSelection): + case nameof(IContentPageContext.SelectedItem): + OnPropertyChanged(nameof(IsExecutable)); + break; + } + } + + private void GeneralSettingsService_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName is nameof(IGeneralSettingsService.ShowFlattenOptions)) + OnPropertyChanged(nameof(IsExecutable)); + } + } +} diff --git a/src/Files.App/Actions/FileSystem/FormatDriveAction.cs b/src/Files.App/Actions/FileSystem/FormatDriveAction.cs index c8d555bb8081..6142bfe4b016 100644 --- a/src/Files.App/Actions/FileSystem/FormatDriveAction.cs +++ b/src/Files.App/Actions/FileSystem/FormatDriveAction.cs @@ -1,37 +1,41 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.App.Utils.Shell; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class FormatDriveAction : ObservableObject, IAction + [GeneratedRichCommand] + internal partial class FormatDriveAction : ObservableObject, IAction { - private readonly IContentPageContext context; + private readonly IContentPageContext context = Ioc.Default.GetRequiredService(); - private readonly DrivesViewModel drivesViewModel; + private readonly DrivesViewModel drivesViewModel = Ioc.Default.GetRequiredService(); public string Label - => "FormatDriveText".GetLocalizedResource(); + => Strings.FormatDriveText.GetLocalizedResource(); public string Description - => "FormatDriveDescription".GetLocalizedResource(); + => Strings.FormatDriveDescription.GetLocalizedResource(); + + public virtual ActionCategory Category + => ActionCategory.FileSystem; - public bool IsExecutable => + public virtual bool IsExecutable => context.HasItem && !context.HasSelection && - (drivesViewModel.Drives.Cast().FirstOrDefault(x => - string.Equals(x.Path, context.Folder?.ItemPath))?.MenuOptions.ShowFormatDrive ?? false); + drivesViewModel.Drives + .Cast() + .FirstOrDefault(x => string.Equals(x.Path, context.Folder?.ItemPath)) is DriveItem driveItem && + !(driveItem.Type == DriveType.Network || string.Equals(context.Folder?.ItemPath, $@"{Constants.UserEnvironmentPaths.SystemDrivePath}\", StringComparison.OrdinalIgnoreCase)); + + public virtual bool IsAccessibleGlobally + => true; public FormatDriveAction() { - context = Ioc.Default.GetRequiredService(); - drivesViewModel = Ioc.Default.GetRequiredService(); - context.PropertyChanged += Context_PropertyChanged; } - public Task ExecuteAsync(object? parameter = null) + public virtual Task ExecuteAsync(object? parameter = null) { return Win32Helper.OpenFormatDriveDialog(context.Folder?.ItemPath ?? string.Empty); } diff --git a/src/Files.App/Actions/FileSystem/FormatDriveFromHomeAction.cs b/src/Files.App/Actions/FileSystem/FormatDriveFromHomeAction.cs new file mode 100644 index 000000000000..4811d0c928b7 --- /dev/null +++ b/src/Files.App/Actions/FileSystem/FormatDriveFromHomeAction.cs @@ -0,0 +1,30 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class FormatDriveFromHomeAction : FormatDriveAction + { + private IHomePageContext HomePageContext { get; } = Ioc.Default.GetRequiredService(); + + private readonly DrivesViewModel drivesViewModel = Ioc.Default.GetRequiredService(); + + public override bool IsExecutable => + HomePageContext.IsAnyItemRightClicked && + HomePageContext.RightClickedItem is not null && + HomePageContext.RightClickedItem.Path is not null && + drivesViewModel.Drives + .Cast() + .FirstOrDefault(x => string.Equals(x.Path, HomePageContext.RightClickedItem.Path)) is DriveItem driveItem && + !(driveItem.Type == DriveType.Network || string.Equals(HomePageContext.RightClickedItem.Path, $@"{Constants.UserEnvironmentPaths.SystemDrivePath}\", StringComparison.OrdinalIgnoreCase)); + + public override bool IsAccessibleGlobally + => false; + + public override Task ExecuteAsync(object? parameter = null) + { + return Win32Helper.OpenFormatDriveDialog(HomePageContext?.RightClickedItem?.Path ?? string.Empty); + } + } +} \ No newline at end of file diff --git a/src/Files.App/Actions/FileSystem/FormatDriveFromSidebarAction.cs b/src/Files.App/Actions/FileSystem/FormatDriveFromSidebarAction.cs new file mode 100644 index 000000000000..a223421b8323 --- /dev/null +++ b/src/Files.App/Actions/FileSystem/FormatDriveFromSidebarAction.cs @@ -0,0 +1,30 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class FormatDriveFromSidebarAction : FormatDriveAction + { + private ISidebarContext SidebarContext { get; } = Ioc.Default.GetRequiredService(); + + private readonly DrivesViewModel drivesViewModel = Ioc.Default.GetRequiredService(); + + public override bool IsExecutable => + SidebarContext.IsItemRightClicked && + SidebarContext.RightClickedItem is not null && + SidebarContext.RightClickedItem.Path is not null && + drivesViewModel.Drives + .Cast() + .FirstOrDefault(x => string.Equals(x.Path, SidebarContext.RightClickedItem.Path)) is DriveItem driveItem && + !(driveItem.Type == DriveType.Network || string.Equals(SidebarContext.RightClickedItem.Path, $@"{Constants.UserEnvironmentPaths.SystemDrivePath}\", StringComparison.OrdinalIgnoreCase)); + + public override bool IsAccessibleGlobally + => false; + + public override Task ExecuteAsync(object? parameter = null) + { + return Win32Helper.OpenFormatDriveDialog(SidebarContext?.RightClickedItem?.Path ?? string.Empty); + } + } +} \ No newline at end of file diff --git a/src/Files.App/Actions/FileSystem/OpenFileLocationAction.cs b/src/Files.App/Actions/FileSystem/OpenFileLocationAction.cs index 8e37acd1be0d..79b6b68f01d6 100644 --- a/src/Files.App/Actions/FileSystem/OpenFileLocationAction.cs +++ b/src/Files.App/Actions/FileSystem/OpenFileLocationAction.cs @@ -1,19 +1,23 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.IO; namespace Files.App.Actions { - internal sealed class OpenFileLocationAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class OpenFileLocationAction : ObservableObject, IAction { private readonly IContentPageContext context; public string Label - => "OpenFileLocation".GetLocalizedResource(); + => Strings.OpenFileLocation.GetLocalizedResource(); public string Description - => "OpenFileLocationDescription".GetLocalizedResource(); + => Strings.OpenFileLocationDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Open; public RichGlyph Glyph => new(baseGlyph: "\uE8DA"); @@ -21,7 +25,7 @@ public RichGlyph Glyph public bool IsExecutable => context.ShellPage is not null && context.HasSelection && - context.SelectedItem is ShortcutItem; + context.SelectedItem is IShortcutItem; public OpenFileLocationAction() { @@ -35,7 +39,7 @@ public async Task ExecuteAsync(object? parameter = null) if (context.ShellPage?.ShellViewModel is null) return; - var item = context.SelectedItem as ShortcutItem; + var item = context.SelectedItem as IShortcutItem; if (string.IsNullOrWhiteSpace(item?.TargetPath)) return; @@ -55,12 +59,12 @@ public async Task ExecuteAsync(object? parameter = null) } else if (destFolder == FileSystemStatusCode.NotFound) { - await DialogDisplayHelper.ShowDialogAsync("FileNotFoundDialog/Title".GetLocalizedResource(), "FileNotFoundDialog/Text".GetLocalizedResource()); + await DialogDisplayHelper.ShowDialogAsync(Strings.FileNotFoundDialog_Title.GetLocalizedResource(), Strings.FileNotFoundDialog_Text.GetLocalizedResource()); } else { - await DialogDisplayHelper.ShowDialogAsync("InvalidItemDialogTitle".GetLocalizedResource(), - string.Format("InvalidItemDialogContent".GetLocalizedResource(), Environment.NewLine, destFolder.ErrorCode.ToString())); + await DialogDisplayHelper.ShowDialogAsync(Strings.InvalidItemDialogTitle.GetLocalizedResource(), + string.Format(Strings.InvalidItemDialogContent.GetLocalizedResource(), Environment.NewLine, destFolder.ErrorCode.ToString())); } } diff --git a/src/Files.App/Actions/FileSystem/OpenItemAction.cs b/src/Files.App/Actions/FileSystem/OpenItemAction.cs index d983d16704d1..789a3225e35f 100644 --- a/src/Files.App/Actions/FileSystem/OpenItemAction.cs +++ b/src/Files.App/Actions/FileSystem/OpenItemAction.cs @@ -1,34 +1,37 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.IO; using Windows.Storage; namespace Files.App.Actions { - internal sealed class OpenItemAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class OpenItemAction : ObservableObject, IAction { private readonly IContentPageContext context; public string Label - => "Open".GetLocalizedResource(); + => Strings.Open.GetLocalizedResource(); public string Description - => "OpenItemDescription".GetLocalizedResource(); + => Strings.OpenItemDescription.GetLocalizedFormatResource(context.SelectedItems.Count); + + public ActionCategory Category + => ActionCategory.Open; public RichGlyph Glyph - => new(opacityStyle: "ColorIconOpenFile"); + => new(themedIconStyle: "App.ThemedIcons.OpenFile"); public HotKey HotKey => new(Keys.Enter); - private const int MaxOpenCount = 10; public bool IsExecutable => context.HasSelection && - context.SelectedItems.Count <= MaxOpenCount && !(context.ShellPage is ColumnShellPage && - context.SelectedItem?.PrimaryItemAttribute == StorageItemTypes.Folder); + context.SelectedItem?.PrimaryItemAttribute == StorageItemTypes.Folder) && + context.PageType != ContentPageTypes.RecycleBin; public OpenItemAction() { @@ -52,23 +55,28 @@ private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) } } - internal sealed class OpenItemWithApplicationPickerAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class OpenItemWithApplicationPickerAction : ObservableObject, IAction { private readonly IContentPageContext context; public string Label - => "OpenWith".GetLocalizedResource(); + => Strings.OpenWith.GetLocalizedResource(); public string Description - => "OpenItemWithApplicationPickerDescription".GetLocalizedResource(); + => Strings.OpenItemWithApplicationPickerDescription.GetLocalizedFormatResource(context.SelectedItems.Count); + + public ActionCategory Category + => ActionCategory.Open; public RichGlyph Glyph - => new(opacityStyle: "ColorIconOpenWith"); + => new(themedIconStyle: "App.ThemedIcons.OpenWith"); public bool IsExecutable => context.HasSelection && + context.PageType != ContentPageTypes.RecycleBin && context.SelectedItems.All(i => - (i.PrimaryItemAttribute == StorageItemTypes.File && !i.IsShortcut && !i.IsExecutable) || + (i.PrimaryItemAttribute == StorageItemTypes.File && !i.IsShortcut && (!i.IsExecutable || i.IsScriptFile)) || (i.PrimaryItemAttribute == StorageItemTypes.Folder && i.IsArchive)); public OpenItemWithApplicationPickerAction() @@ -93,15 +101,19 @@ private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) } } - internal sealed class OpenParentFolderAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class OpenParentFolderAction : ObservableObject, IAction { private readonly IContentPageContext context; public string Label - => "BaseLayoutItemContextFlyoutOpenParentFolder/Text".GetLocalizedResource(); + => Strings.BaseLayoutItemContextFlyoutOpenParentFolder_Text.GetLocalizedResource(); public string Description - => "OpenParentFolderDescription".GetLocalizedResource(); + => Strings.OpenParentFolderDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Open; public RichGlyph Glyph => new(baseGlyph: "\uE197"); diff --git a/src/Files.App/Actions/FileSystem/PasteItemAction.cs b/src/Files.App/Actions/FileSystem/PasteItemAction.cs index a89c0d5a59f1..5610db6994b9 100644 --- a/src/Files.App/Actions/FileSystem/PasteItemAction.cs +++ b/src/Files.App/Actions/FileSystem/PasteItemAction.cs @@ -1,28 +1,30 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.DependencyInjection; -using Files.App.Data.Models; -using Files.App.Extensions; -using Files.App.Helpers; -using System.ComponentModel; -using System.Threading.Tasks; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class PasteItemAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class PasteItemAction : ObservableObject, IAction { private readonly IContentPageContext context; public string Label - => "Paste".GetLocalizedResource(); + => Strings.Paste.GetLocalizedResource(); public string Description - => "PasteItemDescription".GetLocalizedResource(); + => Strings.PasteItemDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.FileSystem; public RichGlyph Glyph - => new(opacityStyle: "ColorIconPaste"); + => new(themedIconStyle: "App.ThemedIcons.Paste"); + + public string AutomationId + => "InnerNavigationToolbarPasteButton"; + + public string AccessKey + => "V"; public HotKey HotKey => new(Keys.V, KeyModifiers.Ctrl); @@ -53,7 +55,9 @@ public bool GetIsExecutable() App.AppModel.IsPasteEnabled && context.PageType != ContentPageTypes.Home && context.PageType != ContentPageTypes.RecycleBin && - context.PageType != ContentPageTypes.SearchResults; + context.PageType != ContentPageTypes.SearchResults && + context.PageType != ContentPageTypes.ReleaseNotes && + context.PageType != ContentPageTypes.Settings; } private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) diff --git a/src/Files.App/Actions/FileSystem/PasteItemAsShortcutAction.cs b/src/Files.App/Actions/FileSystem/PasteItemAsShortcutAction.cs new file mode 100644 index 000000000000..15729fe5de36 --- /dev/null +++ b/src/Files.App/Actions/FileSystem/PasteItemAsShortcutAction.cs @@ -0,0 +1,66 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class PasteItemAsShortcutAction : ObservableObject, IAction + { + private readonly IContentPageContext context; + + public string Label + => Strings.PasteShortcut.GetLocalizedResource(); + + public string Description + => Strings.PasteShortcutDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.FileSystem; + + public RichGlyph Glyph + => new(themedIconStyle: "App.ThemedIcons.Paste"); + + public bool IsExecutable + => GetIsExecutable(); + + public PasteItemAsShortcutAction() + { + context = Ioc.Default.GetRequiredService(); + + context.PropertyChanged += Context_PropertyChanged; + App.AppModel.PropertyChanged += AppModel_PropertyChanged; + } + + public Task ExecuteAsync(object? parameter = null) + { + if (context.ShellPage is null) + return Task.CompletedTask; + + string path = context.ShellPage.ShellViewModel.WorkingDirectory; + return UIFilesystemHelpers.PasteItemAsShortcutAsync(path, context.ShellPage); + } + + public bool GetIsExecutable() + { + return + App.AppModel.IsPasteEnabled && + context.PageType != ContentPageTypes.Home && + context.PageType != ContentPageTypes.RecycleBin && + context.PageType != ContentPageTypes.SearchResults && + context.PageType != ContentPageTypes.ReleaseNotes && + context.PageType != ContentPageTypes.Settings; + } + + private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName is nameof(IContentPageContext.PageType)) + OnPropertyChanged(nameof(IsExecutable)); + } + + private void AppModel_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName is nameof(AppModel.IsPasteEnabled)) + OnPropertyChanged(nameof(IsExecutable)); + } + } +} diff --git a/src/Files.App/Actions/FileSystem/PasteItemToSelectionAction.cs b/src/Files.App/Actions/FileSystem/PasteItemToSelectionAction.cs index 51837c2377bb..3808d5b7d824 100644 --- a/src/Files.App/Actions/FileSystem/PasteItemToSelectionAction.cs +++ b/src/Files.App/Actions/FileSystem/PasteItemToSelectionAction.cs @@ -1,20 +1,24 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class PasteItemToSelectionAction : BaseUIAction, IAction + [GeneratedRichCommand] + internal sealed partial class PasteItemToSelectionAction : BaseUIAction, IAction { private readonly IContentPageContext context; public string Label - => "Paste".GetLocalizedResource(); + => Strings.PasteToSelectedFolder.GetLocalizedResource(); public string Description - => "PasteItemToSelectionDescription".GetLocalizedResource(); + => Strings.PasteItemToSelectionDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.FileSystem; public RichGlyph Glyph - => new(opacityStyle: "ColorIconPaste"); + => new(themedIconStyle: "App.ThemedIcons.Paste"); public HotKey HotKey => new(Keys.V, KeyModifiers.CtrlShift); @@ -47,7 +51,7 @@ public bool GetIsExecutable() if (!App.AppModel.IsPasteEnabled) return false; - if (context.PageType is ContentPageTypes.Home or ContentPageTypes.RecycleBin or ContentPageTypes.SearchResults) + if (context.PageType is ContentPageTypes.Home or ContentPageTypes.RecycleBin or ContentPageTypes.SearchResults or ContentPageTypes.ReleaseNotes or ContentPageTypes.Settings) return false; if (!context.HasSelection) diff --git a/src/Files.App/Actions/FileSystem/RenameAction.cs b/src/Files.App/Actions/FileSystem/RenameAction.cs index 621b1266668b..22ce69b54184 100644 --- a/src/Files.App/Actions/FileSystem/RenameAction.cs +++ b/src/Files.App/Actions/FileSystem/RenameAction.cs @@ -1,29 +1,39 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class RenameAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class RenameAction : ObservableObject, IAction { private readonly IContentPageContext context; public string Label - => "Rename".GetLocalizedResource(); + => Strings.Rename.GetLocalizedResource(); public string Description - => "RenameDescription".GetLocalizedResource(); + => Strings.RenameDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.FileSystem; public HotKey HotKey => new(Keys.F2); public RichGlyph Glyph - => new(opacityStyle: "ColorIconRename"); + => new(themedIconStyle: "App.ThemedIcons.Rename"); + + public string AutomationId + => "InnerNavigationToolbarRenameButton"; + + public string AccessKey + => "M"; public bool IsExecutable => context.ShellPage is not null && IsPageTypeValid() && context.ShellPage.SlimContentPage is not null && - IsSelectionValid(); + context.HasSelection; public RenameAction() { @@ -32,16 +42,18 @@ public RenameAction() context.PropertyChanged += Context_PropertyChanged; } - public Task ExecuteAsync(object? parameter = null) + public async Task ExecuteAsync(object? parameter = null) { - context.ShellPage?.SlimContentPage?.ItemManipulationModel.StartRenameItem(); - - return Task.CompletedTask; - } - - private bool IsSelectionValid() - { - return context.HasSelection && context.SelectedItems.Count == 1; + if (context.SelectedItems.Count > 1) + { + var viewModel = new BulkRenameDialogViewModel(); + var dialogService = Ioc.Default.GetRequiredService(); + var result = await dialogService.ShowDialogAsync(viewModel); + } + else + { + context.ShellPage?.SlimContentPage?.ItemManipulationModel.StartRenameItem(); + } } private bool IsPageTypeValid() @@ -50,7 +62,9 @@ private bool IsPageTypeValid() context.PageType != ContentPageTypes.None && context.PageType != ContentPageTypes.Home && context.PageType != ContentPageTypes.RecycleBin && - context.PageType != ContentPageTypes.ZipFolder; + context.PageType != ContentPageTypes.ZipFolder && + context.PageType != ContentPageTypes.ReleaseNotes && + context.PageType != ContentPageTypes.Settings; } private void Context_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) diff --git a/src/Files.App/Actions/FileSystem/RestoreAllRecycleBinAction.cs b/src/Files.App/Actions/FileSystem/RestoreAllRecycleBinAction.cs index 11bc865db33a..4b7b35c02620 100644 --- a/src/Files.App/Actions/FileSystem/RestoreAllRecycleBinAction.cs +++ b/src/Files.App/Actions/FileSystem/RestoreAllRecycleBinAction.cs @@ -1,26 +1,66 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Controls; +using Windows.Foundation.Metadata; namespace Files.App.Actions { - internal sealed class RestoreAllRecycleBinAction : BaseUIAction, IAction + [GeneratedRichCommand] + internal sealed partial class RestoreAllRecycleBinAction : BaseUIAction, IAction { + private readonly IStorageTrashBinService StorageTrashBinService = Ioc.Default.GetRequiredService(); + public string Label - => "RestoreAllItems".GetLocalizedResource(); + => Strings.RestoreAllItems.GetLocalizedResource(); public string Description - => "RestoreAllRecycleBinDescription".GetLocalizedResource(); + => Strings.RestoreAllRecycleBinDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.FileSystem; public RichGlyph Glyph - => new(opacityStyle: "ColorIconRestoreItem"); + => new(themedIconStyle: "App.ThemedIcons.RestoreDeleted"); public override bool IsExecutable => UIHelpers.CanShowDialog && - RecycleBinHelpers.RecycleBinHasItems(); + StorageTrashBinService.HasItems(); public async Task ExecuteAsync(object? parameter = null) { - await RecycleBinHelpers.RestoreRecycleBinAsync(); + // TODO: Use AppDialogService + var confirmationDialog = new ContentDialog() + { + Title = Strings.ConfirmRestoreBinDialogTitle.GetLocalizedResource(), + Content = Strings.ConfirmRestoreBinDialogContent.GetLocalizedResource(), + PrimaryButtonText = Strings.Yes.GetLocalizedResource(), + SecondaryButtonText = Strings.Cancel.GetLocalizedResource(), + DefaultButton = ContentDialogButton.Primary + }; + + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + confirmationDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; + + if (await confirmationDialog.TryShowAsync() is not ContentDialogResult.Primary) + return; + + bool result = await StorageTrashBinService.RestoreAllTrashesAsync(); + + // Show error dialog when failed + if (!result) + { + var errorDialog = new ContentDialog() + { + Title = Strings.FailedToRestore.GetLocalizedResource(), + PrimaryButtonText = Strings.OK.GetLocalizedResource(), + }; + + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + errorDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; + + await errorDialog.TryShowAsync(); + } } } } diff --git a/src/Files.App/Actions/FileSystem/RestoreRecycleBinAction.cs b/src/Files.App/Actions/FileSystem/RestoreRecycleBinAction.cs index 03e9ec942048..970501c97cbf 100644 --- a/src/Files.App/Actions/FileSystem/RestoreRecycleBinAction.cs +++ b/src/Files.App/Actions/FileSystem/RestoreRecycleBinAction.cs @@ -1,20 +1,28 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Controls; +using Windows.Foundation.Metadata; +using Windows.Storage; namespace Files.App.Actions { - internal sealed class RestoreRecycleBinAction : BaseUIAction, IAction + [GeneratedRichCommand] + internal sealed partial class RestoreRecycleBinAction : BaseUIAction, IAction { private readonly IContentPageContext context; public string Label - => "Restore".GetLocalizedResource(); + => Strings.Restore.GetLocalizedResource(); public string Description - => "RestoreRecycleBinDescription".GetLocalizedResource(); + => Strings.RestoreRecycleBinDescription.GetLocalizedFormatResource(context.SelectedItems.Count); + + public ActionCategory Category + => ActionCategory.FileSystem; public RichGlyph Glyph - => new(opacityStyle: "ColorIconRestoreItem"); + => new(themedIconStyle: "App.ThemedIcons.RestoreDeleted"); public override bool IsExecutable => context.PageType is ContentPageTypes.RecycleBin && @@ -30,8 +38,32 @@ public RestoreRecycleBinAction() public async Task ExecuteAsync(object? parameter = null) { - if (context.ShellPage is not null) - await RecycleBinHelpers.RestoreSelectionRecycleBinAsync(context.ShellPage); + var confirmationDialog = new ContentDialog() + { + Title = Strings.ConfirmRestoreSelectionBinDialogTitle.GetLocalizedResource(), + Content = string.Format(Strings.ConfirmRestoreSelectionBinDialogContent.GetLocalizedFormatResource(context.SelectedItems.Count), context.SelectedItems.Count), + PrimaryButtonText = Strings.Yes.GetLocalizedResource(), + SecondaryButtonText = Strings.Cancel.GetLocalizedResource(), + DefaultButton = ContentDialogButton.Primary + }; + + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + confirmationDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; + + ContentDialogResult result = await confirmationDialog.TryShowAsync(); + + if (result is not ContentDialogResult.Primary) + return; + + var items = context.SelectedItems.ToList().Where(x => x is RecycleBinItem).Select((item) => new + { + Source = StorageHelpers.FromPathAndType( + item.ItemPath, + item.PrimaryItemAttribute == StorageItemTypes.File ? FilesystemItemType.File : FilesystemItemType.Directory), + Dest = ((RecycleBinItem)item).ItemOriginalPath + }); + + await context.ShellPage!.FilesystemHelpers.RestoreItemsFromTrashAsync(items.Select(x => x.Source), items.Select(x => x.Dest), true); } private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) diff --git a/src/Files.App/Actions/FileSystem/Transfer/BaseTransferItemAction.cs b/src/Files.App/Actions/FileSystem/Transfer/BaseTransferItemAction.cs index 9965a73f07b4..a9addf150083 100644 --- a/src/Files.App/Actions/FileSystem/Transfer/BaseTransferItemAction.cs +++ b/src/Files.App/Actions/FileSystem/Transfer/BaseTransferItemAction.cs @@ -1,12 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.Extensions.Logging; using System.Collections.Concurrent; using System.IO; using Windows.ApplicationModel.DataTransfer; using Windows.Storage; -using Windows.System; namespace Files.App.Actions { @@ -25,7 +24,7 @@ public BaseTransferItemAction() public async Task ExecuteTransferAsync(DataPackageOperation type = DataPackageOperation.Copy) { - if (ContentPageContext.ShellPage is null || + if (ContentPageContext.ShellPage?.SlimContentPage is null || ContentPageContext.ShellPage.SlimContentPage.IsItemSelected is false) return; diff --git a/src/Files.App/Actions/FileSystem/Transfer/CopyItemAction.cs b/src/Files.App/Actions/FileSystem/Transfer/CopyItemAction.cs index 2ce0391936ec..83953aaaedb3 100644 --- a/src/Files.App/Actions/FileSystem/Transfer/CopyItemAction.cs +++ b/src/Files.App/Actions/FileSystem/Transfer/CopyItemAction.cs @@ -1,20 +1,30 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.ApplicationModel.DataTransfer; namespace Files.App.Actions { - internal sealed class CopyItemAction : BaseTransferItemAction, IAction + [GeneratedRichCommand] + internal sealed partial class CopyItemAction : BaseTransferItemAction, IAction { public string Label - => "Copy".GetLocalizedResource(); + => Strings.Copy.GetLocalizedResource(); public string Description - => "CopyItemDescription".GetLocalizedResource(); + => Strings.CopyItemDescription.GetLocalizedFormatResource(ContentPageContext.SelectedItems.Count); + + public ActionCategory Category + => ActionCategory.FileSystem; public RichGlyph Glyph - => new(opacityStyle: "ColorIconCopy"); + => new(themedIconStyle: "App.ThemedIcons.Copy"); + + public string AutomationId + => "InnerNavigationToolbarCopyButton"; + + public string AccessKey + => "C"; public HotKey HotKey => new(Keys.C, KeyModifiers.Ctrl); diff --git a/src/Files.App/Actions/FileSystem/Transfer/CutItemAction.cs b/src/Files.App/Actions/FileSystem/Transfer/CutItemAction.cs index 1651d6f405ad..e27f638399f8 100644 --- a/src/Files.App/Actions/FileSystem/Transfer/CutItemAction.cs +++ b/src/Files.App/Actions/FileSystem/Transfer/CutItemAction.cs @@ -1,20 +1,30 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.ApplicationModel.DataTransfer; namespace Files.App.Actions { - internal sealed class CutItemAction : BaseTransferItemAction, IAction + [GeneratedRichCommand] + internal sealed partial class CutItemAction : BaseTransferItemAction, IAction { public string Label - => "Cut".GetLocalizedResource(); + => Strings.Cut.GetLocalizedResource(); public string Description - => "CutItemDescription".GetLocalizedResource(); + => Strings.CutItemDescription.GetLocalizedFormatResource(ContentPageContext.SelectedItems.Count); + + public ActionCategory Category + => ActionCategory.FileSystem; public RichGlyph Glyph - => new(opacityStyle: "ColorIconCut"); + => new(themedIconStyle: "App.ThemedIcons.Cut"); + + public string AutomationId + => "InnerNavigationToolbarCutButton"; + + public string AccessKey + => "X"; public HotKey HotKey => new(Keys.X, KeyModifiers.Ctrl); diff --git a/src/Files.App/Actions/Git/GitCloneAction.cs b/src/Files.App/Actions/Git/GitCloneAction.cs new file mode 100644 index 000000000000..26c872286592 --- /dev/null +++ b/src/Files.App/Actions/Git/GitCloneAction.cs @@ -0,0 +1,51 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class GitCloneAction : ObservableObject, IAction + { + private readonly IContentPageContext pageContext = Ioc.Default.GetRequiredService(); + private readonly IDialogService dialogService = Ioc.Default.GetRequiredService(); + + public string Label { get; } = Strings.Clone.GetLocalizedResource(); + + public string Description { get; } = Strings.GitCloneDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Git; + + public bool IsExecutable + => pageContext.CanCreateItem && !pageContext.IsGitRepository; + + public RichGlyph Glyph + => new(themedIconStyle: "App.ThemedIcons.Git"); + + public GitCloneAction() + { + pageContext.PropertyChanged += Context_PropertyChanged; + } + + public Task ExecuteAsync(object? parameter = null) + { + if (pageContext.ShellPage is null) + return Task.CompletedTask; + + var repoUrl = parameter?.ToString() ?? string.Empty; + var viewModel = new CloneRepoDialogViewModel(repoUrl, pageContext.ShellPage.ShellViewModel.WorkingDirectory); + return dialogService.ShowDialogAsync(viewModel); + } + + private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + switch (e.PropertyName) + { + case nameof(IContentPageContext.CanCreateItem): + case nameof(IContentPageContext.IsGitRepository): + OnPropertyChanged(nameof(IsExecutable)); + break; + } + } + } +} diff --git a/src/Files.App/Actions/Git/GitFetchAction.cs b/src/Files.App/Actions/Git/GitFetchAction.cs index eba8b43109a0..4c9a473faa1f 100644 --- a/src/Files.App/Actions/Git/GitFetchAction.cs +++ b/src/Files.App/Actions/Git/GitFetchAction.cs @@ -1,17 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class GitFetchAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class GitFetchAction : ObservableObject, IAction { private readonly IContentPageContext _context; public string Label - => "GitFetch".GetLocalizedResource(); + => Strings.GitFetch.GetLocalizedResource(); public string Description - => "GitFetchDescription".GetLocalizedResource(); + => Strings.GitFetchDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Git; public bool IsExecutable => _context.CanExecuteGitAction; @@ -19,7 +23,7 @@ public bool IsExecutable public GitFetchAction() { _context = Ioc.Default.GetRequiredService(); - + _context.PropertyChanged += Context_PropertyChanged; } diff --git a/src/Files.App/Actions/Git/GitInitAction.cs b/src/Files.App/Actions/Git/GitInitAction.cs index b9d6b79746d3..ddd00b1b26f1 100644 --- a/src/Files.App/Actions/Git/GitInitAction.cs +++ b/src/Files.App/Actions/Git/GitInitAction.cs @@ -1,19 +1,23 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - sealed class GitInitAction : ObservableObject, IAction - { + [GeneratedRichCommand] + sealed partial class GitInitAction : ObservableObject, IAction + { private readonly IContentPageContext _context; public string Label - => "InitRepo".GetLocalizedResource(); + => Strings.InitRepo.GetLocalizedResource(); public string Description - => "InitRepoDescription".GetLocalizedResource(); + => Strings.InitRepoDescription.GetLocalizedResource(); - public bool IsExecutable => + public ActionCategory Category + => ActionCategory.Git; + + public bool IsExecutable => _context.Folder is not null && _context.Folder.ItemPath != SystemIO.Path.GetPathRoot(_context.Folder.ItemPath) && !_context.IsGitRepository; diff --git a/src/Files.App/Actions/Git/GitPullAction.cs b/src/Files.App/Actions/Git/GitPullAction.cs index 187ab4e88176..5771f9a9e845 100644 --- a/src/Files.App/Actions/Git/GitPullAction.cs +++ b/src/Files.App/Actions/Git/GitPullAction.cs @@ -1,17 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class GitPullAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class GitPullAction : ObservableObject, IAction { private readonly IContentPageContext _context; public string Label - => "GitPull".GetLocalizedResource(); + => Strings.GitPull.GetLocalizedResource(); public string Description - => "GitPullDescription".GetLocalizedResource(); + => Strings.GitPullDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Git; public RichGlyph Glyph => new("\uE74B"); diff --git a/src/Files.App/Actions/Git/GitPushAction.cs b/src/Files.App/Actions/Git/GitPushAction.cs index 97940673cc67..d6c68262dd6f 100644 --- a/src/Files.App/Actions/Git/GitPushAction.cs +++ b/src/Files.App/Actions/Git/GitPushAction.cs @@ -1,15 +1,19 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class GitPushAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class GitPushAction : ObservableObject, IAction { private readonly IContentPageContext _context; - public string Label { get; } = "Push".GetLocalizedResource(); + public string Label { get; } = Strings.Push.GetLocalizedResource(); - public string Description { get; } = "GitPushDescription".GetLocalizedResource(); + public string Description { get; } = Strings.GitPushDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Git; public RichGlyph Glyph { get; } = new("\uE74A"); diff --git a/src/Files.App/Actions/Git/GitSyncAction.cs b/src/Files.App/Actions/Git/GitSyncAction.cs index afa342faee57..e7e78a5e3f7c 100644 --- a/src/Files.App/Actions/Git/GitSyncAction.cs +++ b/src/Files.App/Actions/Git/GitSyncAction.cs @@ -1,15 +1,19 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class GitSyncAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class GitSyncAction : ObservableObject, IAction { private readonly IContentPageContext _context; - public string Label { get; } = "GitSync".GetLocalizedResource(); + public string Label { get; } = Strings.GitSync.GetLocalizedResource(); - public string Description { get; } = "GitSyncDescription".GetLocalizedResource(); + public string Description { get; } = Strings.GitSyncDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Git; public RichGlyph Glyph { get; } = new("\uEDAB"); diff --git a/src/Files.App/Actions/Global/EditPathAction.cs b/src/Files.App/Actions/Global/EditPathAction.cs index cd02dfa97a90..b2d53b9516d5 100644 --- a/src/Files.App/Actions/Global/EditPathAction.cs +++ b/src/Files.App/Actions/Global/EditPathAction.cs @@ -1,17 +1,22 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { + [GeneratedRichCommand] internal sealed class EditPathAction : IAction { - private readonly IContentPageContext context; + private readonly IContentPageContext context = Ioc.Default.GetRequiredService(); + private readonly IGeneralSettingsService GeneralSettingsService = Ioc.Default.GetRequiredService(); public string Label - => "EditPath".GetLocalizedResource(); + => Strings.EditPath.GetLocalizedResource(); public string Description - => "EditPathDescription".GetLocalizedResource(); + => Strings.EditPathDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Open; public HotKey HotKey => new(Keys.L, KeyModifiers.Ctrl); @@ -19,15 +24,18 @@ public HotKey HotKey public HotKey SecondHotKey => new(Keys.D, KeyModifiers.Alt); + public RichGlyph Glyph + => new(themedIconStyle: "App.ThemedIcons.Omnibar.Path"); + public EditPathAction() { - context = Ioc.Default.GetRequiredService(); + } public Task ExecuteAsync(object? parameter = null) { if (context.ShellPage is not null) - context.ShellPage.ToolbarViewModel.IsEditModeEnabled = true; + _ = context.ShellPage!.ToolbarViewModel.SwitchToPathMode(); return Task.CompletedTask; } diff --git a/src/Files.App/Actions/Global/EnterCompactOverlayAction.cs b/src/Files.App/Actions/Global/EnterCompactOverlayAction.cs index f8b2370b14fc..9949fc46125d 100644 --- a/src/Files.App/Actions/Global/EnterCompactOverlayAction.cs +++ b/src/Files.App/Actions/Global/EnterCompactOverlayAction.cs @@ -1,26 +1,30 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Windowing; using Windows.Graphics; namespace Files.App.Actions { - internal sealed class EnterCompactOverlayAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class EnterCompactOverlayAction : ObservableObject, IAction { private readonly IWindowContext windowContext; public string Label - => "EnterCompactOverlay".GetLocalizedResource(); + => Strings.EnterCompactOverlay.GetLocalizedResource(); public RichGlyph Glyph - => new(opacityStyle: "ColorIconEnterCompactOverlay"); + => new(themedIconStyle: "App.ThemedIcons.CompactOverlay"); public HotKey HotKey => new(Keys.Up, KeyModifiers.CtrlAlt); public string Description - => "EnterCompactOverlayDescription".GetLocalizedResource(); + => Strings.EnterCompactOverlayDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Window; public bool IsExecutable => !windowContext.IsCompactOverlay; diff --git a/src/Files.App/Actions/Global/ExitCompactOverlayAction.cs b/src/Files.App/Actions/Global/ExitCompactOverlayAction.cs index 8d951ef3c5f9..52cd9fc9b635 100644 --- a/src/Files.App/Actions/Global/ExitCompactOverlayAction.cs +++ b/src/Files.App/Actions/Global/ExitCompactOverlayAction.cs @@ -1,25 +1,29 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Windowing; namespace Files.App.Actions { - internal sealed class ExitCompactOverlayAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class ExitCompactOverlayAction : ObservableObject, IAction { private readonly IWindowContext windowContext; public string Label - => "ExitCompactOverlay".GetLocalizedResource(); + => Strings.ExitCompactOverlay.GetLocalizedResource(); public RichGlyph Glyph - => new(opacityStyle: "ColorIconExitCompactOverlay"); + => new(themedIconStyle: "App.ThemedIcons.CompactOverlayExit"); public HotKey HotKey => new(Keys.Down, KeyModifiers.CtrlAlt); public string Description - => "ExitCompactOverlayDescription".GetLocalizedResource(); + => Strings.ExitCompactOverlayDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Window; public bool IsExecutable => windowContext.IsCompactOverlay; diff --git a/src/Files.App/Actions/Global/OpenHelpAction.cs b/src/Files.App/Actions/Global/OpenHelpAction.cs index 24e033d921e6..89daa1da6309 100644 --- a/src/Files.App/Actions/Global/OpenHelpAction.cs +++ b/src/Files.App/Actions/Global/OpenHelpAction.cs @@ -1,17 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.System; namespace Files.App.Actions { + [GeneratedRichCommand] internal sealed class OpenHelpAction : IAction { public string Label - => "Help".GetLocalizedResource(); + => Strings.Help.GetLocalizedResource(); public string Description - => "OpenHelpDescription".GetLocalizedResource(); + => Strings.OpenHelpDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Open; public HotKey HotKey => new(Keys.F1); diff --git a/src/Files.App/Actions/Global/RedoAction.cs b/src/Files.App/Actions/Global/RedoAction.cs index f063e3447d09..d61a35bcc7c9 100644 --- a/src/Files.App/Actions/Global/RedoAction.cs +++ b/src/Files.App/Actions/Global/RedoAction.cs @@ -1,17 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class RedoAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class RedoAction : ObservableObject, IAction { private readonly IContentPageContext context; public string Label - => "Redo".GetLocalizedResource(); + => Strings.Redo.GetLocalizedResource(); public string Description - => "RedoDescription".GetLocalizedResource(); + => Strings.RedoDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.FileSystem; public HotKey HotKey => new(Keys.Y, KeyModifiers.Ctrl); diff --git a/src/Files.App/Actions/Global/SearchAction.cs b/src/Files.App/Actions/Global/SearchAction.cs index cf2bac0ee9a4..d755b38414c8 100644 --- a/src/Files.App/Actions/Global/SearchAction.cs +++ b/src/Files.App/Actions/Global/SearchAction.cs @@ -1,17 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class SearchAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class SearchAction : ObservableObject, IAction { private readonly IContentPageContext context; public string Label - => "Search".GetLocalizedResource(); + => Strings.Search.GetLocalizedResource(); public string Description - => "SearchDescription".GetLocalizedResource(); + => Strings.SearchDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Open; public HotKey HotKey => new(Keys.F, KeyModifiers.Ctrl); @@ -20,10 +24,10 @@ public HotKey SecondHotKey => new(Keys.F3); public RichGlyph Glyph - => new(); + => new(themedIconStyle: "App.ThemedIcons.Omnibar.Search"); public bool IsExecutable - => !context.IsSearchBoxVisible; + => context.ShellPage is not null; public SearchAction() { @@ -34,7 +38,11 @@ public SearchAction() public Task ExecuteAsync(object? parameter = null) { - context.ShellPage!.ToolbarViewModel.SwitchSearchBoxVisibility(); + // Check if ShellPage is available before executing the action + if (context.ShellPage is null) + return Task.CompletedTask; + + _ = context.ShellPage.ToolbarViewModel.SwitchToSearchMode(); return Task.CompletedTask; } @@ -43,7 +51,7 @@ private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) { switch (e.PropertyName) { - case nameof(IContentPageContext.IsSearchBoxVisible): + case nameof(IContentPageContext.ShellPage): OnPropertyChanged(nameof(IsExecutable)); break; } diff --git a/src/Files.App/Actions/Global/ToggleCompactOverlayAction.cs b/src/Files.App/Actions/Global/ToggleCompactOverlayAction.cs index 466d214ec9c4..db54a590ae3a 100644 --- a/src/Files.App/Actions/Global/ToggleCompactOverlayAction.cs +++ b/src/Files.App/Actions/Global/ToggleCompactOverlayAction.cs @@ -1,23 +1,27 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Windowing; using Windows.Graphics; namespace Files.App.Actions { - internal sealed class ToggleCompactOverlayAction : ObservableObject, IToggleAction + [GeneratedRichCommand] + internal sealed partial class ToggleCompactOverlayAction : ObservableObject, IToggleAction { private readonly IWindowContext windowContext; public string Label - => "ToggleCompactOverlay".GetLocalizedResource(); + => Strings.ToggleCompactOverlay.GetLocalizedResource(); public HotKey HotKey => new(Keys.F12); public string Description - => "ToggleCompactOverlayDescription".GetLocalizedResource(); + => Strings.ToggleCompactOverlayDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Window; public bool IsOn => windowContext.IsCompactOverlay; diff --git a/src/Files.App/Actions/Global/ToggleFullScreenAction.cs b/src/Files.App/Actions/Global/ToggleFullScreenAction.cs index 510bd3e3e0b1..87eff0d9b1df 100644 --- a/src/Files.App/Actions/Global/ToggleFullScreenAction.cs +++ b/src/Files.App/Actions/Global/ToggleFullScreenAction.cs @@ -1,17 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Windowing; namespace Files.App.Actions { + [GeneratedRichCommand] internal sealed class ToggleFullScreenAction : IToggleAction { public string Label - => "FullScreen".GetLocalizedResource(); + => Strings.FullScreen.GetLocalizedResource(); public string Description - => "ToggleFullScreenDescription".GetLocalizedResource(); + => Strings.ToggleFullScreenDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Window; public HotKey HotKey => new(Keys.F11); diff --git a/src/Files.App/Actions/Global/UndoAction.cs b/src/Files.App/Actions/Global/UndoAction.cs index 3dd80d838920..f12421cf7692 100644 --- a/src/Files.App/Actions/Global/UndoAction.cs +++ b/src/Files.App/Actions/Global/UndoAction.cs @@ -1,17 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class UndoAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class UndoAction : ObservableObject, IAction { private readonly IContentPageContext context; public string Label - => "Undo".GetLocalizedResource(); + => Strings.Undo.GetLocalizedResource(); public string Description - => "UndoDescription".GetLocalizedResource(); + => Strings.UndoDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.FileSystem; public HotKey HotKey => new(Keys.Z, KeyModifiers.Ctrl); diff --git a/src/Files.App/Actions/IAction.cs b/src/Files.App/Actions/IAction.cs index d7dd7bd602ff..0c4a388dd188 100644 --- a/src/Files.App/Actions/IAction.cs +++ b/src/Files.App/Actions/IAction.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { @@ -16,12 +16,24 @@ public interface IAction /// string Description { get; } + /// + /// Gets the extended label of this command, used to differentiate similar worded labels. Defaults to . + /// + string ExtendedLabel + => Label; + /// /// Glyph information to display icon. /// RichGlyph Glyph => RichGlyph.None; + /// + /// Access key hint used in menus. + /// + string AccessKey + => string.Empty; + /// /// Primary hotkey to execute the action. /// @@ -46,6 +58,12 @@ HotKey ThirdHotKey HotKey MediaHotKey => HotKey.None; + /// + /// Gets the automation ID for UI testing. + /// + string AutomationId + => string.Empty; + /// /// Returns whether the action is executable in the current context. /// @@ -58,6 +76,9 @@ bool IsExecutable bool IsAccessibleGlobally => true; + ActionCategory Category + => ActionCategory.Unspecified; + /// /// Executes the action asynchronously. /// diff --git a/src/Files.App/Actions/IToggleAction.cs b/src/Files.App/Actions/IToggleAction.cs index 9a337767dfd4..7653bb0aff5d 100644 --- a/src/Files.App/Actions/IToggleAction.cs +++ b/src/Files.App/Actions/IToggleAction.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { diff --git a/src/Files.App/Actions/Navigation/AddHorizontalPaneAction.cs b/src/Files.App/Actions/Navigation/AddHorizontalPaneAction.cs deleted file mode 100644 index 9b1a20ddf1b1..000000000000 --- a/src/Files.App/Actions/Navigation/AddHorizontalPaneAction.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -namespace Files.App.Actions -{ - internal sealed class AddHorizontalPaneAction : ObservableObject, IAction - { - private readonly IContentPageContext ContentPageContext = Ioc.Default.GetRequiredService(); - private readonly IGeneralSettingsService GeneralSettingsService = Ioc.Default.GetRequiredService(); - - public string Label - => "AddHorizontalPane".GetLocalizedResource(); - - public string Description - => "AddHorizontalPaneDescription".GetLocalizedResource(); - - public HotKey HotKey - => new(Keys.H, KeyModifiers.AltShift); - - public RichGlyph Glyph - => new(opacityStyle: "ColorIconAddHorizontalPane"); - - public bool IsExecutable => - ContentPageContext.IsMultiPaneAvailable && - !ContentPageContext.IsMultiPaneActive; - - public AddHorizontalPaneAction() - { - ContentPageContext.PropertyChanged += ContentPageContext_PropertyChanged; - } - - public Task ExecuteAsync(object? parameter = null) - { - ContentPageContext.ShellPage!.PaneHolder.OpenSecondaryPane(arrangement: ShellPaneArrangement.Horizontal); - - return Task.CompletedTask; - } - - private void ContentPageContext_PropertyChanged(object? sender, PropertyChangedEventArgs e) - { - switch (e.PropertyName) - { - case nameof(IContentPageContext.IsMultiPaneAvailable): - case nameof(IContentPageContext.IsMultiPaneActive): - OnPropertyChanged(nameof(IsExecutable)); - break; - } - } - } -} diff --git a/src/Files.App/Actions/Navigation/AddVerticalPaneAction.cs b/src/Files.App/Actions/Navigation/AddVerticalPaneAction.cs deleted file mode 100644 index 491ac1e5970e..000000000000 --- a/src/Files.App/Actions/Navigation/AddVerticalPaneAction.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -namespace Files.App.Actions -{ - internal sealed class AddVerticalPaneAction : ObservableObject, IAction - { - private readonly IContentPageContext ContentPageContext = Ioc.Default.GetRequiredService(); - private readonly IGeneralSettingsService GeneralSettingsService = Ioc.Default.GetRequiredService(); - - public string Label - => "AddVerticalPane".GetLocalizedResource(); - - public string Description - => "AddVerticalPaneDescription".GetLocalizedResource(); - - public HotKey HotKey - => new(Keys.V, KeyModifiers.AltShift); - - public RichGlyph Glyph - => new(opacityStyle: "ColorIconAddVerticalPane"); - - public bool IsExecutable => - ContentPageContext.IsMultiPaneAvailable && - !ContentPageContext.IsMultiPaneActive; - - public AddVerticalPaneAction() - { - ContentPageContext.PropertyChanged += ContentPageContext_PropertyChanged; - } - - public Task ExecuteAsync(object? parameter = null) - { - ContentPageContext.ShellPage!.PaneHolder.OpenSecondaryPane(arrangement: ShellPaneArrangement.Vertical); - - return Task.CompletedTask; - } - - private void ContentPageContext_PropertyChanged(object? sender, PropertyChangedEventArgs e) - { - switch (e.PropertyName) - { - case nameof(IContentPageContext.IsMultiPaneAvailable): - case nameof(IContentPageContext.IsMultiPaneActive): - OnPropertyChanged(nameof(IsExecutable)); - break; - } - } - } -} diff --git a/src/Files.App/Actions/Navigation/ArrangePanesHorizontallyAction.cs b/src/Files.App/Actions/Navigation/ArrangePanesHorizontallyAction.cs index 0ff61dce2052..92e8eaad063d 100644 --- a/src/Files.App/Actions/Navigation/ArrangePanesHorizontallyAction.cs +++ b/src/Files.App/Actions/Navigation/ArrangePanesHorizontallyAction.cs @@ -1,21 +1,25 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class ArrangePanesHorizontallyAction : ObservableObject, IToggleAction + [GeneratedRichCommand] + internal sealed partial class ArrangePanesHorizontallyAction : ObservableObject, IToggleAction { private readonly IContentPageContext ContentPageContext = Ioc.Default.GetRequiredService(); private readonly IMultiPanesContext MultiPanesContext = Ioc.Default.GetRequiredService(); public string Label - => "ArrangePanesHorizontally".GetLocalizedResource(); + => Strings.ArrangePanesHorizontally.GetLocalizedResource(); public string Description - => "ArrangePanesHorizontallyDescription".GetLocalizedResource(); + => Strings.ArrangePanesHorizontallyDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.DualPane; public RichGlyph Glyph - => new(opacityStyle: "ColorIconAddHorizontalPane"); + => new(themedIconStyle: "App.ThemedIcons.Panes.Horizontal"); public bool IsOn => MultiPanesContext.ShellPaneArrangement is ShellPaneArrangement.Horizontal; diff --git a/src/Files.App/Actions/Navigation/ArrangePanesVerticallyAction.cs b/src/Files.App/Actions/Navigation/ArrangePanesVerticallyAction.cs index 18460037de9c..49251e87d581 100644 --- a/src/Files.App/Actions/Navigation/ArrangePanesVerticallyAction.cs +++ b/src/Files.App/Actions/Navigation/ArrangePanesVerticallyAction.cs @@ -1,21 +1,25 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class ArrangePanesVerticallyAction : ObservableObject, IToggleAction + [GeneratedRichCommand] + internal sealed partial class ArrangePanesVerticallyAction : ObservableObject, IToggleAction { private readonly IContentPageContext ContentPageContext = Ioc.Default.GetRequiredService(); private readonly IMultiPanesContext MultiPanesContext = Ioc.Default.GetRequiredService(); public string Label - => "ArrangePanesVertically".GetLocalizedResource(); + => Strings.ArrangePanesVertically.GetLocalizedResource(); public string Description - => "ArrangePanesVerticallyDescription".GetLocalizedResource(); + => Strings.ArrangePanesVerticallyDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.DualPane; public RichGlyph Glyph - => new(opacityStyle: "ColorIconAddVerticalPane"); + => new(themedIconStyle: "App.ThemedIcons.Panes.Vertical"); public bool IsOn => MultiPanesContext.ShellPaneArrangement is ShellPaneArrangement.Vertical; diff --git a/src/Files.App/Actions/Navigation/CloseActivePaneAction.cs b/src/Files.App/Actions/Navigation/CloseActivePaneAction.cs index e557f3f754b2..08bd7d9c5f81 100644 --- a/src/Files.App/Actions/Navigation/CloseActivePaneAction.cs +++ b/src/Files.App/Actions/Navigation/CloseActivePaneAction.cs @@ -1,23 +1,27 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class CloseActivePaneAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class CloseActivePaneAction : ObservableObject, IAction { private IContentPageContext ContentPageContext { get; } = Ioc.Default.GetRequiredService(); public string Label - => "CloseActivePane".GetLocalizedResource(); + => Strings.CloseActivePane.GetLocalizedResource(); public string Description - => "CloseActivePaneDescription".GetLocalizedResource(); + => Strings.CloseActivePaneDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.DualPane; public HotKey HotKey - => new(Keys.W, KeyModifiers.CtrlShift); + => new(Keys.W, KeyModifiers.CtrlAlt); public RichGlyph Glyph - => new("\uE89F"); + => new(themedIconStyle: "App.ThemedIcons.PanelLeftClose"); public bool IsExecutable => ContentPageContext.IsMultiPaneActive; diff --git a/src/Files.App/Actions/Navigation/CloseAllTabsAction.cs b/src/Files.App/Actions/Navigation/CloseAllTabsAction.cs new file mode 100644 index 000000000000..36b4f4ddf3bc --- /dev/null +++ b/src/Files.App/Actions/Navigation/CloseAllTabsAction.cs @@ -0,0 +1,38 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class CloseAllTabsAction : CloseTabBaseAction + { + public override string Label + => Strings.CloseAllTabs.GetLocalizedResource(); + + public override string Description + => Strings.CloseAllTabsDescription.GetLocalizedResource(); + + public override HotKey HotKey + => new(Keys.W, KeyModifiers.CtrlShift); + + public CloseAllTabsAction() + { + } + + protected override bool GetIsExecutable() + { + return + context.Control is not null && + context.TabCount > 0 && + context.CurrentTabItem is not null; + } + + public override Task ExecuteAsync(object? parameter = null) + { + if (context.Control is not null) + MultitaskingTabsHelpers.CloseAllTabs(context.Control); + + return Task.CompletedTask; + } + } +} diff --git a/src/Files.App/Actions/Navigation/CloseOtherTabsCurrentAction.cs b/src/Files.App/Actions/Navigation/CloseOtherTabsCurrentAction.cs deleted file mode 100644 index 5174faf1f700..000000000000 --- a/src/Files.App/Actions/Navigation/CloseOtherTabsCurrentAction.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -namespace Files.App.Actions -{ - internal sealed class CloseOtherTabsCurrentAction : CloseTabBaseAction - { - public override string Label - => "CloseOtherTabs".GetLocalizedResource(); - - public override string Description - => "CloseOtherTabsCurrentDescription".GetLocalizedResource(); - - public CloseOtherTabsCurrentAction() - { - } - - public override Task ExecuteAsync(object? parameter = null) - { - if (context.Control is not null) - MultitaskingTabsHelpers.CloseOtherTabs(context.CurrentTabItem, context.Control); - - return Task.CompletedTask; - } - } -} diff --git a/src/Files.App/Actions/Navigation/CloseOtherTabsSelectedAction.cs b/src/Files.App/Actions/Navigation/CloseOtherTabsSelectedAction.cs index 5ed32c5ca104..efa757d56554 100644 --- a/src/Files.App/Actions/Navigation/CloseOtherTabsSelectedAction.cs +++ b/src/Files.App/Actions/Navigation/CloseOtherTabsSelectedAction.cs @@ -1,15 +1,16 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class CloseOtherTabsSelectedAction : CloseTabBaseAction + [GeneratedRichCommand] + internal sealed partial class CloseOtherTabsSelectedAction : CloseTabBaseAction { public override string Label - => "CloseOtherTabs".GetLocalizedResource(); + => Strings.CloseOtherTabs.GetLocalizedResource(); public override string Description - => "CloseOtherTabsSelectedDescription".GetLocalizedResource(); + => Strings.CloseOtherTabsSelectedDescription.GetLocalizedResource(); public CloseOtherTabsSelectedAction() { diff --git a/src/Files.App/Actions/Navigation/CloseSelectedTabAction.cs b/src/Files.App/Actions/Navigation/CloseSelectedTabAction.cs index f5d50be092a1..8463ae9b19e2 100644 --- a/src/Files.App/Actions/Navigation/CloseSelectedTabAction.cs +++ b/src/Files.App/Actions/Navigation/CloseSelectedTabAction.cs @@ -1,15 +1,16 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class CloseSelectedTabAction : CloseTabBaseAction + [GeneratedRichCommand] + internal sealed partial class CloseSelectedTabAction : CloseTabBaseAction { public override string Label - => "CloseTab".GetLocalizedResource(); + => Strings.CloseTab.GetLocalizedResource(); public override string Description - => "CloseSelectedTabDescription".GetLocalizedResource(); + => Strings.CloseSelectedTabDescription.GetLocalizedResource(); public override HotKey HotKey => new(Keys.W, KeyModifiers.Ctrl); diff --git a/src/Files.App/Actions/Navigation/CloseTabBaseAction.cs b/src/Files.App/Actions/Navigation/CloseTabBaseAction.cs index 14240d30eb33..38583a88553e 100644 --- a/src/Files.App/Actions/Navigation/CloseTabBaseAction.cs +++ b/src/Files.App/Actions/Navigation/CloseTabBaseAction.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { @@ -10,13 +10,16 @@ internal abstract class CloseTabBaseAction : ObservableObject, IAction public abstract string Label { get; } public abstract string Description { get; } - + + public virtual ActionCategory Category + => ActionCategory.Navigation; + public bool IsExecutable => GetIsExecutable(); public virtual HotKey HotKey => HotKey.None; - + public virtual HotKey SecondHotKey => HotKey.None; diff --git a/src/Files.App/Actions/Navigation/CloseTabsToTheLeftCurrentAction.cs b/src/Files.App/Actions/Navigation/CloseTabsToTheLeftCurrentAction.cs deleted file mode 100644 index f6bd4c7af187..000000000000 --- a/src/Files.App/Actions/Navigation/CloseTabsToTheLeftCurrentAction.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -namespace Files.App.Actions -{ - internal sealed class CloseTabsToTheLeftCurrentAction : CloseTabBaseAction - { - public override string Label - => "CloseTabsToTheLeft".GetLocalizedResource(); - - public override string Description - => "CloseTabsToTheLeftCurrentDescription".GetLocalizedResource(); - - public CloseTabsToTheLeftCurrentAction() - { - } - - public override Task ExecuteAsync(object? parameter = null) - { - MultitaskingTabsHelpers.CloseTabsToTheLeft(context.CurrentTabItem, context.Control!); - - return Task.CompletedTask; - } - - protected override bool GetIsExecutable() - { - return context.Control is not null && context.CurrentTabIndex > 0; - } - - protected override void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) - { - switch (e.PropertyName) - { - case nameof(IMultitaskingContext.Control): - case nameof(IMultitaskingContext.SelectedTabIndex): - OnPropertyChanged(nameof(IsExecutable)); - break; - } - } - } -} diff --git a/src/Files.App/Actions/Navigation/CloseTabsToTheLeftSelectedAction.cs b/src/Files.App/Actions/Navigation/CloseTabsToTheLeftSelectedAction.cs index 36c67a0344a9..4ca4e35a9fa4 100644 --- a/src/Files.App/Actions/Navigation/CloseTabsToTheLeftSelectedAction.cs +++ b/src/Files.App/Actions/Navigation/CloseTabsToTheLeftSelectedAction.cs @@ -1,15 +1,16 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class CloseTabsToTheLeftSelectedAction : CloseTabBaseAction + [GeneratedRichCommand] + internal sealed partial class CloseTabsToTheLeftSelectedAction : CloseTabBaseAction { public override string Label - => "CloseTabsToTheLeft".GetLocalizedResource(); + => Strings.CloseTabsToTheLeft.GetLocalizedResource(); public override string Description - => "CloseTabsToTheLeftSelectedDescription".GetLocalizedResource(); + => Strings.CloseTabsToTheLeftSelectedDescription.GetLocalizedResource(); public CloseTabsToTheLeftSelectedAction() { diff --git a/src/Files.App/Actions/Navigation/CloseTabsToTheRightCurrentAction.cs b/src/Files.App/Actions/Navigation/CloseTabsToTheRightCurrentAction.cs deleted file mode 100644 index 4c2c617650be..000000000000 --- a/src/Files.App/Actions/Navigation/CloseTabsToTheRightCurrentAction.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -namespace Files.App.Actions -{ - internal sealed class CloseTabsToTheRightCurrentAction : CloseTabBaseAction - { - public override string Label - => "CloseTabsToTheRight".GetLocalizedResource(); - - public override string Description - => "CloseTabsToTheRightCurrentDescription".GetLocalizedResource(); - - public CloseTabsToTheRightCurrentAction() - { - } - - public override Task ExecuteAsync(object? parameter = null) - { - MultitaskingTabsHelpers.CloseTabsToTheRight(context.CurrentTabItem, context.Control!); - - return Task.CompletedTask; - } - - protected override bool GetIsExecutable() - { - return context.Control is not null && context.CurrentTabIndex < context.TabCount - 1; - } - - protected override void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) - { - switch (e.PropertyName) - { - case nameof(IMultitaskingContext.Control): - case nameof(IMultitaskingContext.TabCount): - case nameof(IMultitaskingContext.SelectedTabIndex): - OnPropertyChanged(nameof(IsExecutable)); - break; - } - } - } -} diff --git a/src/Files.App/Actions/Navigation/CloseTabsToTheRightSelectedAction.cs b/src/Files.App/Actions/Navigation/CloseTabsToTheRightSelectedAction.cs index 18601b62d51a..a49b56b93d8d 100644 --- a/src/Files.App/Actions/Navigation/CloseTabsToTheRightSelectedAction.cs +++ b/src/Files.App/Actions/Navigation/CloseTabsToTheRightSelectedAction.cs @@ -1,15 +1,16 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class CloseTabsToTheRightSelectedAction : CloseTabBaseAction + [GeneratedRichCommand] + internal sealed partial class CloseTabsToTheRightSelectedAction : CloseTabBaseAction { public override string Label - => "CloseTabsToTheRight".GetLocalizedResource(); + => Strings.CloseTabsToTheRight.GetLocalizedResource(); public override string Description - => "CloseTabsToTheRightSelectedDescription".GetLocalizedResource(); + => Strings.CloseTabsToTheRightSelectedDescription.GetLocalizedResource(); public CloseTabsToTheRightSelectedAction() { diff --git a/src/Files.App/Actions/Navigation/DuplicateCurrentTabAction.cs b/src/Files.App/Actions/Navigation/DuplicateCurrentTabAction.cs deleted file mode 100644 index 05aa62328001..000000000000 --- a/src/Files.App/Actions/Navigation/DuplicateCurrentTabAction.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -namespace Files.App.Actions -{ - internal sealed class DuplicateCurrentTabAction : IAction - { - private readonly IMultitaskingContext context; - - public string Label - => "DuplicateTab".GetLocalizedResource(); - - public string Description - => "DuplicateCurrentTabDescription".GetLocalizedResource(); - - public DuplicateCurrentTabAction() - { - context = Ioc.Default.GetRequiredService(); - } - - public async Task ExecuteAsync(object? parameter = null) - { - var arguments = context.CurrentTabItem.NavigationParameter; - - if (arguments is null) - { - await NavigationHelpers.AddNewTabByPathAsync(typeof(ShellPanesPage), "Home", true); - } - else - { - await NavigationHelpers.AddNewTabByParamAsync( - arguments.InitialPageType, - arguments.NavigationParameter, - context.CurrentTabIndex + 1); - } - } - } -} diff --git a/src/Files.App/Actions/Navigation/DuplicateSelectedTabAction.cs b/src/Files.App/Actions/Navigation/DuplicateSelectedTabAction.cs index 2beeca6a74b2..550e2d16e798 100644 --- a/src/Files.App/Actions/Navigation/DuplicateSelectedTabAction.cs +++ b/src/Files.App/Actions/Navigation/DuplicateSelectedTabAction.cs @@ -1,17 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class DuplicateSelectedTabAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class DuplicateSelectedTabAction : ObservableObject, IAction { private readonly IMultitaskingContext context; public string Label - => "DuplicateTab".GetLocalizedResource(); + => Strings.DuplicateTab.GetLocalizedResource(); public string Description - => "DuplicateSelectedTabDescription".GetLocalizedResource(); + => Strings.DuplicateSelectedTabDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Navigation; public HotKey HotKey => new(Keys.K, KeyModifiers.CtrlShift); diff --git a/src/Files.App/Actions/Navigation/FocusOtherPane.cs b/src/Files.App/Actions/Navigation/FocusOtherPane.cs index a618d67999f6..1dfeebde9c9d 100644 --- a/src/Files.App/Actions/Navigation/FocusOtherPane.cs +++ b/src/Files.App/Actions/Navigation/FocusOtherPane.cs @@ -1,17 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class FocusOtherPaneAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class FocusOtherPaneAction : ObservableObject, IAction { private readonly IContentPageContext context; public string Label - => "FocusOtherPane".GetLocalizedResource(); + => Strings.FocusOtherPane.GetLocalizedResource(); public string Description - => "FocusOtherPaneDescription".GetLocalizedResource(); + => Strings.FocusOtherPaneDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.DualPane; public HotKey HotKey => new(Keys.Right, KeyModifiers.CtrlShift); diff --git a/src/Files.App/Actions/Navigation/NavigateBackAction.cs b/src/Files.App/Actions/Navigation/NavigateBackAction.cs index 86f69aa7a1dd..652f144fbf36 100644 --- a/src/Files.App/Actions/Navigation/NavigateBackAction.cs +++ b/src/Files.App/Actions/Navigation/NavigateBackAction.cs @@ -1,17 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class NavigateBackAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class NavigateBackAction : ObservableObject, IAction { private readonly IContentPageContext context; public string Label - => "Back".GetLocalizedResource(); + => Strings.Back.GetLocalizedResource(); public string Description - => "NavigateBackDescription".GetLocalizedResource(); + => Strings.NavigateBackDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Navigation; public HotKey HotKey => new(Keys.Left, KeyModifiers.Alt); diff --git a/src/Files.App/Actions/Navigation/NavigateForwardAction.cs b/src/Files.App/Actions/Navigation/NavigateForwardAction.cs index 697663d3e1c5..ab86785338b4 100644 --- a/src/Files.App/Actions/Navigation/NavigateForwardAction.cs +++ b/src/Files.App/Actions/Navigation/NavigateForwardAction.cs @@ -1,17 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class NavigateForwardAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class NavigateForwardAction : ObservableObject, IAction { private readonly IContentPageContext context; public string Label - => "Forward".GetLocalizedResource(); + => Strings.Forward.GetLocalizedResource(); public string Description - => "NavigateForwardDescription".GetLocalizedResource(); + => Strings.NavigateForwardDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Navigation; public HotKey HotKey => new(Keys.Right, KeyModifiers.Alt); diff --git a/src/Files.App/Actions/Navigation/NavigateHomeAction.cs b/src/Files.App/Actions/Navigation/NavigateHomeAction.cs new file mode 100644 index 000000000000..ba094deed96c --- /dev/null +++ b/src/Files.App/Actions/Navigation/NavigateHomeAction.cs @@ -0,0 +1,50 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class NavigateHomeAction : ObservableObject, IAction + { + private readonly IContentPageContext context; + + public string Label + => Strings.Home.GetLocalizedResource(); + + public string Description + => Strings.NavigateHomeDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Navigation; + + public RichGlyph Glyph + => new("\uE80F"); + + public bool IsExecutable + => context.PageType is not ContentPageTypes.Home; + + public NavigateHomeAction() + { + context = Ioc.Default.GetRequiredService(); + + context.PropertyChanged += Context_PropertyChanged; + } + + public Task ExecuteAsync(object? parameter = null) + { + context.ShellPage!.NavigateHome(); + + return Task.CompletedTask; + } + + private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + switch (e.PropertyName) + { + case nameof(IContentPageContext.PageType): + OnPropertyChanged(nameof(IsExecutable)); + break; + } + } + } +} diff --git a/src/Files.App/Actions/Navigation/NavigateUpAction.cs b/src/Files.App/Actions/Navigation/NavigateUpAction.cs index e134a26c04de..30237a06aacc 100644 --- a/src/Files.App/Actions/Navigation/NavigateUpAction.cs +++ b/src/Files.App/Actions/Navigation/NavigateUpAction.cs @@ -1,17 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class NavigateUpAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class NavigateUpAction : ObservableObject, IAction { private readonly IContentPageContext context; public string Label - => "Up".GetLocalizedResource(); + => Strings.Up.GetLocalizedResource(); public string Description - => "NavigateUpDescription".GetLocalizedResource(); + => Strings.NavigateUpDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Navigation; public HotKey HotKey => new(Keys.Up, KeyModifiers.Alt); diff --git a/src/Files.App/Actions/Navigation/NewTabAction.cs b/src/Files.App/Actions/Navigation/NewTabAction.cs index c644ded31f60..05d436047ffd 100644 --- a/src/Files.App/Actions/Navigation/NewTabAction.cs +++ b/src/Files.App/Actions/Navigation/NewTabAction.cs @@ -1,15 +1,19 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { + [GeneratedRichCommand] internal sealed class NewTabAction : IAction { public string Label - => "NewTab".GetLocalizedResource(); + => Strings.NewTab.GetLocalizedResource(); public string Description - => "NewTabDescription".GetLocalizedResource(); + => Strings.NewTabDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Navigation; public HotKey HotKey => new(Keys.T, KeyModifiers.Ctrl); diff --git a/src/Files.App/Actions/Navigation/NewWindowAction.cs b/src/Files.App/Actions/Navigation/NewWindowAction.cs index 2048cfde52ef..e538482af13f 100644 --- a/src/Files.App/Actions/Navigation/NewWindowAction.cs +++ b/src/Files.App/Actions/Navigation/NewWindowAction.cs @@ -1,21 +1,25 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { + [GeneratedRichCommand] internal sealed class NewWindowAction : IAction { public string Label - => "NewWindow".GetLocalizedResource(); + => Strings.NewWindow.GetLocalizedResource(); public string Description - => "NewWindowDescription".GetLocalizedResource(); + => Strings.NewWindowDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Navigation; public HotKey HotKey => new(Keys.N, KeyModifiers.Ctrl); public RichGlyph Glyph - => new(opacityStyle: "ColorIconOpenNewWindow"); + => new(themedIconStyle: "App.ThemedIcons.New.Window"); public NewWindowAction() { diff --git a/src/Files.App/Actions/Navigation/NextTabAction.cs b/src/Files.App/Actions/Navigation/NextTabAction.cs index 38eb469c817c..585169ff3c2b 100644 --- a/src/Files.App/Actions/Navigation/NextTabAction.cs +++ b/src/Files.App/Actions/Navigation/NextTabAction.cs @@ -1,20 +1,25 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; namespace Files.App.Actions { - internal sealed class NextTabAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class NextTabAction : ObservableObject, IAction { private readonly IMultitaskingContext multitaskingContext; + private readonly IContentPageContext contentPageContext = Ioc.Default.GetRequiredService(); public string Label - => "NextTab".GetLocalizedResource(); + => Strings.NextTab.GetLocalizedResource(); public string Description - => "NextTabDescription".GetLocalizedResource(); + => Strings.NextTabDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Navigation; public bool IsExecutable => multitaskingContext.TabCount > 1; @@ -36,8 +41,8 @@ public async Task ExecuteAsync(object? parameter = null) // Small delay for the UI to load await Task.Delay(500); - // Refocus on the file list - (multitaskingContext.CurrentTabItem.TabItemContent as Control)?.Focus(FocusState.Programmatic); + // Focus the content of the selected tab item (needed for keyboard navigation) + contentPageContext.ShellPage!.PaneHolder.FocusActivePane(); } private void MultitaskingContext_PropertyChanged(object? sender, PropertyChangedEventArgs e) diff --git a/src/Files.App/Actions/Navigation/OpenInNewPane/BaseOpenInNewPaneAction.cs b/src/Files.App/Actions/Navigation/OpenInNewPane/BaseOpenInNewPaneAction.cs index e6f73a419648..d4fa66e2a098 100644 --- a/src/Files.App/Actions/Navigation/OpenInNewPane/BaseOpenInNewPaneAction.cs +++ b/src/Files.App/Actions/Navigation/OpenInNewPane/BaseOpenInNewPaneAction.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { @@ -11,15 +11,18 @@ internal abstract class BaseOpenInNewPaneAction : ObservableObject, IAction protected ISidebarContext SidebarContext { get; } = Ioc.Default.GetRequiredService(); public string Label - => "OpenInNewPane".GetLocalizedResource(); + => Strings.OpenInNewPane.GetLocalizedResource(); public string Description - => "OpenDirectoryInNewPaneDescription".GetLocalizedResource(); + => Strings.OpenDirectoryInNewPaneDescription.GetLocalizedResource(); + + public virtual ActionCategory Category + => ActionCategory.DualPane; public virtual bool IsExecutable => + ContentPageContext.PageType != ContentPageTypes.RecycleBin && ContentPageContext.SelectedItem is not null && - ContentPageContext.SelectedItem.IsFolder && - UserSettingsService.GeneralSettingsService.ShowOpenInNewPane; + ContentPageContext.SelectedItem.IsFolder; public virtual bool IsAccessibleGlobally => true; diff --git a/src/Files.App/Actions/Navigation/OpenInNewPane/OpenInNewPaneAction.cs b/src/Files.App/Actions/Navigation/OpenInNewPane/OpenInNewPaneAction.cs index f995dcaa3c51..ddb604db588d 100644 --- a/src/Files.App/Actions/Navigation/OpenInNewPane/OpenInNewPaneAction.cs +++ b/src/Files.App/Actions/Navigation/OpenInNewPane/OpenInNewPaneAction.cs @@ -1,9 +1,10 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class OpenInNewPaneAction : BaseOpenInNewPaneAction + [GeneratedRichCommand] + internal sealed partial class OpenInNewPaneAction : BaseOpenInNewPaneAction { } } diff --git a/src/Files.App/Actions/Navigation/OpenInNewPane/OpenInNewPaneFromHomeAction.cs b/src/Files.App/Actions/Navigation/OpenInNewPane/OpenInNewPaneFromHomeAction.cs index e946e6875e00..84a3bceecc47 100644 --- a/src/Files.App/Actions/Navigation/OpenInNewPane/OpenInNewPaneFromHomeAction.cs +++ b/src/Files.App/Actions/Navigation/OpenInNewPane/OpenInNewPaneFromHomeAction.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class OpenInNewPaneFromHomeAction : BaseOpenInNewPaneAction + [GeneratedRichCommand] + internal sealed partial class OpenInNewPaneFromHomeAction : BaseOpenInNewPaneAction { public override bool IsExecutable => - UserSettingsService.GeneralSettingsService.ShowOpenInNewPane && HomePageContext.IsAnyItemRightClicked && HomePageContext.RightClickedItem is not null && (HomePageContext.RightClickedItem is WidgetFileTagCardItem fileTagItem diff --git a/src/Files.App/Actions/Navigation/OpenInNewPane/OpenInNewPaneFromSidebarAction.cs b/src/Files.App/Actions/Navigation/OpenInNewPane/OpenInNewPaneFromSidebarAction.cs index 78280d31a0e2..2eccb829566a 100644 --- a/src/Files.App/Actions/Navigation/OpenInNewPane/OpenInNewPaneFromSidebarAction.cs +++ b/src/Files.App/Actions/Navigation/OpenInNewPane/OpenInNewPaneFromSidebarAction.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class OpenInNewPaneFromSidebarAction : BaseOpenInNewPaneAction + [GeneratedRichCommand] + internal sealed partial class OpenInNewPaneFromSidebarAction : BaseOpenInNewPaneAction { public override bool IsExecutable => - UserSettingsService.GeneralSettingsService.ShowOpenInNewPane && SidebarContext.IsItemRightClicked && SidebarContext.RightClickedItem is not null && SidebarContext.RightClickedItem.MenuOptions.IsLocationItem; diff --git a/src/Files.App/Actions/Navigation/OpenInNewTab/BaseOpenInNewTabAction.cs b/src/Files.App/Actions/Navigation/OpenInNewTab/BaseOpenInNewTabAction.cs index 7f5ca8c797ae..0d0f3743f7e2 100644 --- a/src/Files.App/Actions/Navigation/OpenInNewTab/BaseOpenInNewTabAction.cs +++ b/src/Files.App/Actions/Navigation/OpenInNewTab/BaseOpenInNewTabAction.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { @@ -11,13 +11,16 @@ internal abstract class BaseOpenInNewTabAction : ObservableObject, IAction protected ISidebarContext SidebarContext { get; } = Ioc.Default.GetRequiredService(); public string Label - => "OpenInNewTab".GetLocalizedResource(); + => Strings.OpenInNewTab.GetLocalizedResource(); public string Description - => "OpenDirectoryInNewTabDescription".GetLocalizedResource(); + => Strings.OpenDirectoryInNewTabDescription.GetLocalizedResource(); + + public virtual ActionCategory Category + => ActionCategory.Navigation; public RichGlyph Glyph - => new(opacityStyle: "ColorIconOpenInNewTab"); + => new(themedIconStyle: "App.ThemedIcons.OpenInTab"); public virtual bool IsAccessibleGlobally => true; @@ -25,10 +28,10 @@ public virtual bool IsAccessibleGlobally public virtual bool IsExecutable => ContentPageContext.ShellPage is not null && ContentPageContext.ShellPage.SlimContentPage is not null && + ContentPageContext.PageType != ContentPageTypes.RecycleBin && ContentPageContext.SelectedItems.Count is not 0 && ContentPageContext.SelectedItems.Count <= 5 && - ContentPageContext.SelectedItems.Count(x => x.IsFolder) == ContentPageContext.SelectedItems.Count && - UserSettingsService.GeneralSettingsService.ShowOpenInNewTab; + ContentPageContext.SelectedItems.Count(x => x.IsFolder) == ContentPageContext.SelectedItems.Count; public BaseOpenInNewTabAction() { @@ -43,7 +46,7 @@ await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(async () => { await NavigationHelpers.AddNewTabByPathAsync( typeof(ShellPanesPage), - (listedItem as ShortcutItem)?.TargetPath ?? listedItem.ItemPath, + (listedItem as IShortcutItem)?.TargetPath ?? listedItem.ItemPath, false); }, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low); diff --git a/src/Files.App/Actions/Navigation/OpenInNewTab/OpenInNewTabAction.cs b/src/Files.App/Actions/Navigation/OpenInNewTab/OpenInNewTabAction.cs index e3ec0926b93d..6811dd928031 100644 --- a/src/Files.App/Actions/Navigation/OpenInNewTab/OpenInNewTabAction.cs +++ b/src/Files.App/Actions/Navigation/OpenInNewTab/OpenInNewTabAction.cs @@ -1,9 +1,10 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class OpenInNewTabAction : BaseOpenInNewTabAction + [GeneratedRichCommand] + internal sealed partial class OpenInNewTabAction : BaseOpenInNewTabAction { } } diff --git a/src/Files.App/Actions/Navigation/OpenInNewTab/OpenInNewTabFromHomeAction.cs b/src/Files.App/Actions/Navigation/OpenInNewTab/OpenInNewTabFromHomeAction.cs index c4648ba08754..b68c5eca6c30 100644 --- a/src/Files.App/Actions/Navigation/OpenInNewTab/OpenInNewTabFromHomeAction.cs +++ b/src/Files.App/Actions/Navigation/OpenInNewTab/OpenInNewTabFromHomeAction.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class OpenInNewTabFromHomeAction : BaseOpenInNewTabAction + [GeneratedRichCommand] + internal sealed partial class OpenInNewTabFromHomeAction : BaseOpenInNewTabAction { public override bool IsExecutable => - UserSettingsService.GeneralSettingsService.ShowOpenInNewTab && HomePageContext.IsAnyItemRightClicked && HomePageContext.RightClickedItem is not null && (HomePageContext.RightClickedItem is WidgetFileTagCardItem fileTagItem @@ -24,7 +24,7 @@ public override async Task ExecuteAsync(object? parameter = null) if (await DriveHelpers.CheckEmptyDrive(HomePageContext.RightClickedItem!.Path)) return; - await NavigationHelpers.OpenPathInNewTab(HomePageContext.RightClickedItem!.Path ?? string.Empty, false); + await NavigationHelpers.OpenPathInNewTab(HomePageContext.RightClickedItem!.Path ?? string.Empty); } protected override void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) diff --git a/src/Files.App/Actions/Navigation/OpenInNewTab/OpenInNewTabFromSidebarAction.cs b/src/Files.App/Actions/Navigation/OpenInNewTab/OpenInNewTabFromSidebarAction.cs index 20947764c9a9..e9bf87960abe 100644 --- a/src/Files.App/Actions/Navigation/OpenInNewTab/OpenInNewTabFromSidebarAction.cs +++ b/src/Files.App/Actions/Navigation/OpenInNewTab/OpenInNewTabFromSidebarAction.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class OpenInNewTabFromSidebarAction : BaseOpenInNewTabAction + [GeneratedRichCommand] + internal sealed partial class OpenInNewTabFromSidebarAction : BaseOpenInNewTabAction { public override bool IsExecutable => - UserSettingsService.GeneralSettingsService.ShowOpenInNewTab && SidebarContext.IsItemRightClicked && SidebarContext.RightClickedItem is not null && SidebarContext.RightClickedItem.MenuOptions.IsLocationItem; @@ -22,7 +22,7 @@ public override async Task ExecuteAsync(object? parameter = null) if (await DriveHelpers.CheckEmptyDrive(SidebarContext.RightClickedItem!.Path)) return; - await NavigationHelpers.OpenPathInNewTab(SidebarContext.RightClickedItem!.Path ?? string.Empty, false); + await NavigationHelpers.OpenPathInNewTab(SidebarContext.RightClickedItem!.Path ?? string.Empty); } protected override void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) diff --git a/src/Files.App/Actions/Navigation/OpenInNewWindow/BaseOpenInNewWindowAction.cs b/src/Files.App/Actions/Navigation/OpenInNewWindow/BaseOpenInNewWindowAction.cs index 428447962740..299be266b895 100644 --- a/src/Files.App/Actions/Navigation/OpenInNewWindow/BaseOpenInNewWindowAction.cs +++ b/src/Files.App/Actions/Navigation/OpenInNewWindow/BaseOpenInNewWindowAction.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.System; @@ -13,16 +13,19 @@ internal abstract class BaseOpenInNewWindowAction : ObservableObject, IAction protected ISidebarContext SidebarContext { get; } = Ioc.Default.GetRequiredService(); public string Label - => "OpenInNewWindow".GetLocalizedResource(); + => Strings.OpenInNewWindow.GetLocalizedResource(); public string Description - => "OpenInNewWindowDescription".GetLocalizedResource(); + => Strings.OpenInNewWindowDescription.GetLocalizedResource(); + + public virtual ActionCategory Category + => ActionCategory.Navigation; public virtual HotKey HotKey => new(Keys.Enter, KeyModifiers.CtrlAlt); public RichGlyph Glyph - => new(opacityStyle: "ColorIconOpenInNewWindow"); + => new(themedIconStyle: "App.ThemedIcons.OpenInWindow"); public virtual bool IsAccessibleGlobally => true; @@ -30,10 +33,10 @@ public virtual bool IsAccessibleGlobally public virtual bool IsExecutable => ContentPageContext.ShellPage is not null && ContentPageContext.ShellPage.SlimContentPage is not null && + ContentPageContext.PageType != ContentPageTypes.RecycleBin && ContentPageContext.SelectedItems.Count is not 0 && ContentPageContext.SelectedItems.Count <= 5 && - ContentPageContext.SelectedItems.Count(x => x.IsFolder) == ContentPageContext.SelectedItems.Count && - UserSettingsService.GeneralSettingsService.ShowOpenInNewWindow; + ContentPageContext.SelectedItems.Count(x => x.IsFolder) == ContentPageContext.SelectedItems.Count; public BaseOpenInNewWindowAction() { @@ -49,8 +52,8 @@ public virtual async Task ExecuteAsync(object? parameter = null) foreach (ListedItem listedItem in items) { - var selectedItemPath = (listedItem as ShortcutItem)?.TargetPath ?? listedItem.ItemPath; - var folderUri = new Uri($"files-uwp:?folder={@selectedItemPath}"); + var selectedItemPath = (listedItem as IShortcutItem)?.TargetPath ?? listedItem.ItemPath; + var folderUri = new Uri($"files-dev:?folder={@selectedItemPath}"); await Launcher.LaunchUriAsync(folderUri); } diff --git a/src/Files.App/Actions/Navigation/OpenInNewWindow/OpenInNewWindowAction.cs b/src/Files.App/Actions/Navigation/OpenInNewWindow/OpenInNewWindowAction.cs index b556f25ccb34..1a50cc9cea7e 100644 --- a/src/Files.App/Actions/Navigation/OpenInNewWindow/OpenInNewWindowAction.cs +++ b/src/Files.App/Actions/Navigation/OpenInNewWindow/OpenInNewWindowAction.cs @@ -1,9 +1,10 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class OpenInNewWindowAction : BaseOpenInNewWindowAction + [GeneratedRichCommand] + internal sealed partial class OpenInNewWindowAction : BaseOpenInNewWindowAction { } } diff --git a/src/Files.App/Actions/Navigation/OpenInNewWindow/OpenInNewWindowFromHomeAction.cs b/src/Files.App/Actions/Navigation/OpenInNewWindow/OpenInNewWindowFromHomeAction.cs index 2b419f3f6fe2..2e3625f43020 100644 --- a/src/Files.App/Actions/Navigation/OpenInNewWindow/OpenInNewWindowFromHomeAction.cs +++ b/src/Files.App/Actions/Navigation/OpenInNewWindow/OpenInNewWindowFromHomeAction.cs @@ -1,15 +1,15 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class OpenInNewWindowFromHomeAction : BaseOpenInNewWindowAction + [GeneratedRichCommand] + internal sealed partial class OpenInNewWindowFromHomeAction : BaseOpenInNewWindowAction { public override HotKey HotKey => HotKey.None; public override bool IsExecutable => - UserSettingsService.GeneralSettingsService.ShowOpenInNewWindow && HomePageContext.IsAnyItemRightClicked && HomePageContext.RightClickedItem is not null && (HomePageContext.RightClickedItem is WidgetFileTagCardItem fileTagItem diff --git a/src/Files.App/Actions/Navigation/OpenInNewWindow/OpenInNewWindowFromSidebarAction.cs b/src/Files.App/Actions/Navigation/OpenInNewWindow/OpenInNewWindowFromSidebarAction.cs index de2f8ab18417..9bb54c1b2090 100644 --- a/src/Files.App/Actions/Navigation/OpenInNewWindow/OpenInNewWindowFromSidebarAction.cs +++ b/src/Files.App/Actions/Navigation/OpenInNewWindow/OpenInNewWindowFromSidebarAction.cs @@ -1,15 +1,15 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class OpenInNewWindowFromSidebarAction : BaseOpenInNewWindowAction + [GeneratedRichCommand] + internal sealed partial class OpenInNewWindowFromSidebarAction : BaseOpenInNewWindowAction { public override HotKey HotKey => HotKey.None; public override bool IsExecutable => - UserSettingsService.GeneralSettingsService.ShowOpenInNewWindow && SidebarContext.IsItemRightClicked && SidebarContext.RightClickedItem is not null && SidebarContext.RightClickedItem.MenuOptions.IsLocationItem; diff --git a/src/Files.App/Actions/Navigation/PreviousTabAction.cs b/src/Files.App/Actions/Navigation/PreviousTabAction.cs index e1f008895d90..f0ff278c4483 100644 --- a/src/Files.App/Actions/Navigation/PreviousTabAction.cs +++ b/src/Files.App/Actions/Navigation/PreviousTabAction.cs @@ -1,20 +1,25 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; namespace Files.App.Actions { - internal sealed class PreviousTabAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class PreviousTabAction : ObservableObject, IAction { private readonly IMultitaskingContext multitaskingContext; + private readonly IContentPageContext contentPageContext = Ioc.Default.GetRequiredService(); public string Label - => "PreviousTab".GetLocalizedResource(); + => Strings.PreviousTab.GetLocalizedResource(); public string Description - => "PreviousTabDescription".GetLocalizedResource(); + => Strings.PreviousTabDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Navigation; public bool IsExecutable => multitaskingContext.TabCount > 1; @@ -39,8 +44,8 @@ public async Task ExecuteAsync(object? parameter = null) // Small delay for the UI to load await Task.Delay(500); - // Refocus on the file list - (multitaskingContext.CurrentTabItem.TabItemContent as Control)?.Focus(FocusState.Programmatic); + // Focus the content of the selected tab item (needed for keyboard navigation) + contentPageContext.ShellPage!.PaneHolder.FocusActivePane(); } private void MultitaskingContext_PropertyChanged(object? sender, PropertyChangedEventArgs e) diff --git a/src/Files.App/Actions/Navigation/ReopenClosedTabAction.cs b/src/Files.App/Actions/Navigation/ReopenClosedTabAction.cs index 076ec3b38680..f22f381e65e2 100644 --- a/src/Files.App/Actions/Navigation/ReopenClosedTabAction.cs +++ b/src/Files.App/Actions/Navigation/ReopenClosedTabAction.cs @@ -1,19 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.App.UserControls.TabBar; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class ReopenClosedTabAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class ReopenClosedTabAction : ObservableObject, IAction { private readonly IMultitaskingContext context; public string Label - => "ReopenClosedTab".GetLocalizedResource(); + => Strings.ReopenClosedTab.GetLocalizedResource(); public string Description - => "ReopenClosedTabDescription".GetLocalizedResource(); + => Strings.ReopenClosedTabDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Navigation; public HotKey HotKey => new(Keys.T, KeyModifiers.CtrlShift); diff --git a/src/Files.App/Actions/Navigation/SplitPaneHorizontallyAction.cs b/src/Files.App/Actions/Navigation/SplitPaneHorizontallyAction.cs new file mode 100644 index 000000000000..5e1f39cdf295 --- /dev/null +++ b/src/Files.App/Actions/Navigation/SplitPaneHorizontallyAction.cs @@ -0,0 +1,53 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class SplitPaneHorizontallyAction : ObservableObject, IAction + { + private readonly IContentPageContext ContentPageContext = Ioc.Default.GetRequiredService(); + + public string Label + => Strings.SplitPaneHorizontally.GetLocalizedResource(); + + public string Description + => Strings.SplitPaneHorizontallyDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.DualPane; + + public HotKey HotKey + => new(Keys.H, KeyModifiers.AltShift); + + public RichGlyph Glyph + => new(themedIconStyle: "App.ThemedIcons.Panes.Horizontal"); + + public bool IsExecutable => + ContentPageContext.IsMultiPaneAvailable && + !ContentPageContext.IsMultiPaneActive; + + public SplitPaneHorizontallyAction() + { + ContentPageContext.PropertyChanged += ContentPageContext_PropertyChanged; + } + + public Task ExecuteAsync(object? parameter = null) + { + ContentPageContext.ShellPage!.PaneHolder.OpenSecondaryPane(ContentPageContext.ShellPage!.ShellViewModel.WorkingDirectory, ShellPaneArrangement.Horizontal); + + return Task.CompletedTask; + } + + private void ContentPageContext_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + switch (e.PropertyName) + { + case nameof(IContentPageContext.IsMultiPaneAvailable): + case nameof(IContentPageContext.IsMultiPaneActive): + OnPropertyChanged(nameof(IsExecutable)); + break; + } + } + } +} diff --git a/src/Files.App/Actions/Navigation/SplitPaneVerticallyAction.cs b/src/Files.App/Actions/Navigation/SplitPaneVerticallyAction.cs new file mode 100644 index 000000000000..b00e481df066 --- /dev/null +++ b/src/Files.App/Actions/Navigation/SplitPaneVerticallyAction.cs @@ -0,0 +1,53 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class SplitPaneVerticallyAction : ObservableObject, IAction + { + private readonly IContentPageContext ContentPageContext = Ioc.Default.GetRequiredService(); + + public string Label + => Strings.SplitPaneVertically.GetLocalizedResource(); + + public string Description + => Strings.AddVerticalPaneDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.DualPane; + + public HotKey HotKey + => new(Keys.V, KeyModifiers.AltShift); + + public RichGlyph Glyph + => new(themedIconStyle: "App.ThemedIcons.Panes.Vertical"); + + public bool IsExecutable => + ContentPageContext.IsMultiPaneAvailable && + !ContentPageContext.IsMultiPaneActive; + + public SplitPaneVerticallyAction() + { + ContentPageContext.PropertyChanged += ContentPageContext_PropertyChanged; + } + + public Task ExecuteAsync(object? parameter = null) + { + ContentPageContext.ShellPage!.PaneHolder.OpenSecondaryPane(ContentPageContext.ShellPage!.ShellViewModel.WorkingDirectory, ShellPaneArrangement.Vertical); + + return Task.CompletedTask; + } + + private void ContentPageContext_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + switch (e.PropertyName) + { + case nameof(IContentPageContext.IsMultiPaneAvailable): + case nameof(IContentPageContext.IsMultiPaneActive): + OnPropertyChanged(nameof(IsExecutable)); + break; + } + } + } +} diff --git a/src/Files.App/Actions/Open/EditInNotepadAction.cs b/src/Files.App/Actions/Open/EditInNotepadAction.cs index 05b168e0fae0..7ae57f7f3db2 100644 --- a/src/Files.App/Actions/Open/EditInNotepadAction.cs +++ b/src/Files.App/Actions/Open/EditInNotepadAction.cs @@ -1,19 +1,23 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.Shared.Helpers; namespace Files.App.Actions { - internal sealed class EditInNotepadAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class EditInNotepadAction : ObservableObject, IAction { private readonly IContentPageContext context; public string Label - => "EditInNotepad".GetLocalizedResource(); + => Strings.EditInNotepad.GetLocalizedResource(); public string Description - => "EditInNotepadDescription".GetLocalizedResource(); + => Strings.EditInNotepadDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Open; public RichGlyph Glyph => new("\uE70F"); @@ -33,7 +37,7 @@ public EditInNotepadAction() public Task ExecuteAsync(object? parameter = null) { - return Task.WhenAll(context.SelectedItems.Select(item => Win32Helper.RunPowershellCommandAsync($"notepad '{item.ItemPath}\'", false))); + return Task.WhenAll(context.SelectedItems.Select(item => Win32Helper.RunPowershellCommandAsync($"notepad '{item.ItemPath}\'", PowerShellExecutionOptions.Hidden))); } private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) @@ -46,4 +50,4 @@ private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) } } } -} \ No newline at end of file +} diff --git a/src/Files.App/Actions/Open/OpenClassicPropertiesAction.cs b/src/Files.App/Actions/Open/OpenClassicPropertiesAction.cs new file mode 100644 index 000000000000..4b62a46297c8 --- /dev/null +++ b/src/Files.App/Actions/Open/OpenClassicPropertiesAction.cs @@ -0,0 +1,80 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using System.Runtime.InteropServices; +using Windows.Win32; +using Windows.Win32.UI.Shell; + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class OpenClassicPropertiesAction : ObservableObject, IAction + { + private readonly IContentPageContext context; + + public string Label + => Strings.OpenClassicProperties.GetLocalizedResource(); + + public string Description + => Strings.OpenClassicPropertiesDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Open; + + public RichGlyph Glyph + => new(themedIconStyle: "App.ThemedIcons.Properties"); + + public HotKey HotKey + => new(Keys.Enter, KeyModifiers.AltShift); + + public bool IsExecutable => + context.PageType is not ContentPageTypes.Home && + (context.HasSelection && context.SelectedItems.Count == 1 || + !context.HasSelection && context.PageType is not ContentPageTypes.SearchResults); + + public OpenClassicPropertiesAction() + { + context = Ioc.Default.GetRequiredService(); + + context.PropertyChanged += Context_PropertyChanged; + } + + public Task ExecuteAsync(object? parameter = null) + { + if (context.HasSelection && context?.SelectedItem?.ItemPath is not null) + ExecuteShellCommand(context.SelectedItem.ItemPath); + else if (context?.Folder?.ItemPath is not null) + ExecuteShellCommand(context.Folder.ItemPath); + + return Task.CompletedTask; + } + + private unsafe void ExecuteShellCommand(string itemPath) + { + SHELLEXECUTEINFOW info = default; + info.cbSize = (uint)Marshal.SizeOf(info); + info.nShow = 5; // SW_SHOW + info.fMask = 0x0000000C; // SEE_MASK_INVOKEIDLIST + + fixed (char* cVerb = "properties", lpFile = itemPath) + { + info.lpVerb = cVerb; + info.lpFile = lpFile; + + PInvoke.ShellExecuteEx(ref info); + } + } + + private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + switch (e.PropertyName) + { + case nameof(IContentPageContext.PageType): + case nameof(IContentPageContext.HasSelection): + case nameof(IContentPageContext.Folder): + OnPropertyChanged(nameof(IsExecutable)); + break; + } + } + } +} diff --git a/src/Files.App/Actions/Open/OpenCommandPaletteAction.cs b/src/Files.App/Actions/Open/OpenCommandPaletteAction.cs index 4d61f1022bc9..10f2985ddeac 100644 --- a/src/Files.App/Actions/Open/OpenCommandPaletteAction.cs +++ b/src/Files.App/Actions/Open/OpenCommandPaletteAction.cs @@ -1,17 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { + [GeneratedRichCommand] internal sealed class OpenCommandPaletteAction : IAction { private readonly IContentPageContext _context; public string Label - => "CommandPalette".GetLocalizedResource(); + => Strings.CommandPalette.GetLocalizedResource(); public string Description - => "OpenCommandPaletteDescription".GetLocalizedResource(); + => Strings.OpenCommandPaletteDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Open; public HotKey HotKey => new(Keys.P, KeyModifiers.CtrlShift); @@ -23,7 +27,7 @@ public OpenCommandPaletteAction() public Task ExecuteAsync(object? parameter = null) { - _context.ShellPage?.ToolbarViewModel.OpenCommandPalette(); + _context.ShellPage?.ToolbarViewModel.SwitchToCommandPaletteMode(); return Task.CompletedTask; } diff --git a/src/Files.App/Actions/Open/OpenInIDEAction.cs b/src/Files.App/Actions/Open/OpenInIDEAction.cs new file mode 100644 index 000000000000..9c461c1ae737 --- /dev/null +++ b/src/Files.App/Actions/Open/OpenInIDEAction.cs @@ -0,0 +1,68 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class OpenInIDEAction : ObservableObject, IAction + { + private readonly IDevToolsSettingsService _devToolsSettingsService; + + private readonly IContentPageContext _context; + + public string Label + => string.Format( + Strings.OpenInIDE.GetLocalizedResource(), + _devToolsSettingsService.IDEName); + + public string Description + => string.Format( + Strings.OpenInIDEDescription.GetLocalizedResource(), + _devToolsSettingsService.IDEName); + + public ActionCategory Category + => ActionCategory.Open; + + public bool IsExecutable => + _context.Folder is not null && + !string.IsNullOrWhiteSpace(_devToolsSettingsService.IDEPath); + + public OpenInIDEAction() + { + _devToolsSettingsService = Ioc.Default.GetRequiredService(); + _context = Ioc.Default.GetRequiredService(); + _context.PropertyChanged += Context_PropertyChanged; + _devToolsSettingsService.PropertyChanged += DevSettings_PropertyChanged; + } + + public async Task ExecuteAsync(object? parameter = null) + { + var res = await Win32Helper.RunPowershellCommandAsync( + $"& \'{_devToolsSettingsService.IDEPath}\' \'{_context.ShellPage?.ShellViewModel.WorkingDirectory}\'", + PowerShellExecutionOptions.Hidden + ); + + if (!res) + await DynamicDialogFactory.ShowFor_IDEErrorDialog(_devToolsSettingsService.IDEName); + } + + private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(IContentPageContext.Folder)) + OnPropertyChanged(nameof(IsExecutable)); + } + + private void DevSettings_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(IDevToolsSettingsService.IDEPath)) + { + OnPropertyChanged(nameof(IsExecutable)); + } + else if (e.PropertyName == nameof(IDevToolsSettingsService.IDEName)) + { + OnPropertyChanged(nameof(Label)); + OnPropertyChanged(nameof(Description)); + } + } + } +} diff --git a/src/Files.App/Actions/Open/OpenInVSCodeAction.cs b/src/Files.App/Actions/Open/OpenInVSCodeAction.cs deleted file mode 100644 index a65e5b11eb00..000000000000 --- a/src/Files.App/Actions/Open/OpenInVSCodeAction.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.App.Utils.Shell; - -namespace Files.App.Actions -{ - internal sealed class OpenInVSCodeAction : ObservableObject, IAction - { - private readonly IContentPageContext _context; - - private readonly bool _isVSCodeInstalled; - - public string Label - => "OpenInVSCode".GetLocalizedResource(); - - public string Description - => "OpenInVSCodeDescription".GetLocalizedResource(); - - public bool IsExecutable => - _isVSCodeInstalled && - _context.Folder is not null; - - public OpenInVSCodeAction() - { - _context = Ioc.Default.GetRequiredService(); - - _isVSCodeInstalled = SoftwareHelpers.IsVSCodeInstalled(); - if (_isVSCodeInstalled) - _context.PropertyChanged += Context_PropertyChanged; - } - - public Task ExecuteAsync(object? parameter = null) - { - return Win32Helper.RunPowershellCommandAsync($"code \'{_context.ShellPage?.ShellViewModel.WorkingDirectory}\'", false); - } - - private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) - { - if (e.PropertyName == nameof(IContentPageContext.Folder)) - OnPropertyChanged(nameof(IsExecutable)); - } - } -} diff --git a/src/Files.App/Actions/Open/OpenLogFileAction.cs b/src/Files.App/Actions/Open/OpenLogFileAction.cs new file mode 100644 index 000000000000..0229d686da6b --- /dev/null +++ b/src/Files.App/Actions/Open/OpenLogFileAction.cs @@ -0,0 +1,63 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media; +using Windows.Foundation.Metadata; +using Windows.Storage; +using Windows.System; + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class OpenLogFileAction : IAction + { + public string Label + => Strings.OpenLogFile.GetLocalizedResource(); + + public string Description + => Strings.OpenLogFileDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Open; + + public HotKey HotKey + => new(Keys.OemPeriod, KeyModifiers.Ctrl); + + public async Task ExecuteAsync(object? parameter = null) + { + try + { + var debugFile = await ApplicationData.Current.LocalFolder.TryGetItemAsync("debug.log") as StorageFile; + + if (debugFile != null && !await Launcher.LaunchFileAsync(debugFile)) + { + // Fallback to Process.Start if Launcher fails + using var process = Process.Start(new ProcessStartInfo(debugFile.Path) + { + UseShellExecute = true, + Verb = "open" + }); + } + } + catch (Exception ex) + { + // Only show the error dialog if no other popups are open + if (!VisualTreeHelper.GetOpenPopupsForXamlRoot(MainWindow.Instance.Content.XamlRoot).Any()) + { + var errorDialog = new ContentDialog() + { + Title = Strings.FailedToOpenLogFile.GetLocalizedResource(), + Content = ex.Message, + PrimaryButtonText = Strings.OK.GetLocalizedResource(), + }; + + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + errorDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; + + await errorDialog.TryShowAsync(); + } + } + } + } +} diff --git a/src/Files.App/Actions/Open/OpenLogFileLocationAction.cs b/src/Files.App/Actions/Open/OpenLogFileLocationAction.cs new file mode 100644 index 000000000000..6a114c7f9936 --- /dev/null +++ b/src/Files.App/Actions/Open/OpenLogFileLocationAction.cs @@ -0,0 +1,29 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Windows.Storage; +using Windows.System; + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class OpenLogFileLocationAction : IAction + { + public string Label + => Strings.OpenLogLocation.GetLocalizedResource(); + + public string Description + => Strings.OpenLogFileLocationDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Open; + + public HotKey HotKey + => new(Keys.OemPeriod, KeyModifiers.CtrlShift); + + public async Task ExecuteAsync(object? parameter = null) + { + await Launcher.LaunchFolderAsync(ApplicationData.Current.LocalFolder).AsTask(); + } + } +} diff --git a/src/Files.App/Actions/Open/OpenPropertiesAction.cs b/src/Files.App/Actions/Open/OpenPropertiesAction.cs index 5628eafec65e..1fd76d57a57f 100644 --- a/src/Files.App/Actions/Open/OpenPropertiesAction.cs +++ b/src/Files.App/Actions/Open/OpenPropertiesAction.cs @@ -1,27 +1,37 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class OpenPropertiesAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class OpenPropertiesAction : ObservableObject, IAction { private readonly IContentPageContext context; public string Label - => "OpenProperties".GetLocalizedResource(); + => Strings.OpenProperties.GetLocalizedResource(); public string Description - => "OpenPropertiesDescription".GetLocalizedResource(); + => Strings.OpenPropertiesDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Open; public RichGlyph Glyph - => new(opacityStyle: "ColorIconProperties"); + => new(themedIconStyle: "App.ThemedIcons.Properties"); + + public string AutomationId + => "InnerNavigationToolbarPropertiesButton"; + + public string AccessKey + => "O"; public HotKey HotKey => new(Keys.Enter, KeyModifiers.Alt); public bool IsExecutable => context.PageType is not ContentPageTypes.Home && - !(context.PageType is ContentPageTypes.SearchResults && + !(context.PageType is ContentPageTypes.SearchResults && !context.HasSelection); public OpenPropertiesAction() @@ -50,7 +60,7 @@ private void OpenPropertiesFromItemContextMenuFlyout(object? _, object e) var page = context.ShellPage?.SlimContentPage; if (page is not null) page.ItemContextMenuFlyout.Closed -= OpenPropertiesFromItemContextMenuFlyout; - + FilePropertiesHelpers.OpenPropertiesWindow(context.ShellPage!); } @@ -59,7 +69,7 @@ private void OpenPropertiesFromBaseContextMenuFlyout(object? _, object e) var page = context.ShellPage?.SlimContentPage; if (page is not null) page.BaseContextMenuFlyout.Closed -= OpenPropertiesFromBaseContextMenuFlyout; - + FilePropertiesHelpers.OpenPropertiesWindow(context.ShellPage!); } diff --git a/src/Files.App/Actions/Open/OpenReleaseNotesAction.cs b/src/Files.App/Actions/Open/OpenReleaseNotesAction.cs new file mode 100644 index 000000000000..bdc3352c5999 --- /dev/null +++ b/src/Files.App/Actions/Open/OpenReleaseNotesAction.cs @@ -0,0 +1,47 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class OpenReleaseNotesAction : ObservableObject, IAction + { + private readonly IContentPageContext context = Ioc.Default.GetRequiredService(); + private readonly IUpdateService UpdateService = Ioc.Default.GetRequiredService(); + + public string Label + => Strings.ReleaseNotes.GetLocalizedResource(); + + public string Description + => Strings.ReleaseNotesDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Open; + + public RichGlyph Glyph + => new(themedIconStyle: "App.ThemedIcons.AppUpdatedBox"); + + public bool IsExecutable + => UpdateService.AreReleaseNotesAvailable; + + public OpenReleaseNotesAction() + { + UpdateService.PropertyChanged += UpdateService_PropertyChanged; + } + + public Task ExecuteAsync(object? parameter = null) + { + return NavigationHelpers.OpenPathInNewTab("ReleaseNotes", true); + } + + private void UpdateService_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + switch (e.PropertyName) + { + case nameof(IUpdateService.AreReleaseNotesAvailable): + OnPropertyChanged(nameof(IsExecutable)); + break; + } + } + } +} diff --git a/src/Files.App/Actions/Open/OpenRepoInIDEAction.cs b/src/Files.App/Actions/Open/OpenRepoInIDEAction.cs new file mode 100644 index 000000000000..d030e18f0c63 --- /dev/null +++ b/src/Files.App/Actions/Open/OpenRepoInIDEAction.cs @@ -0,0 +1,65 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class OpenRepoInIDEAction : ObservableObject, IAction + { + private readonly IDevToolsSettingsService _devToolsSettingsService; + + private readonly IContentPageContext _context; + + public string Label + => string.Format(Strings.OpenRepoInIDE.GetLocalizedResource(), _devToolsSettingsService.IDEName); + + public string Description + => string.Format(Strings.OpenRepoInIDEDescription.GetLocalizedResource(), _devToolsSettingsService.IDEName); + + public ActionCategory Category + => ActionCategory.Open; + + public bool IsExecutable => + _context.Folder is not null && + _context.ShellPage!.InstanceViewModel.IsGitRepository && + !string.IsNullOrWhiteSpace(_devToolsSettingsService.IDEPath); + + public OpenRepoInIDEAction() + { + _context = Ioc.Default.GetRequiredService(); + _devToolsSettingsService = Ioc.Default.GetRequiredService(); + _context.PropertyChanged += Context_PropertyChanged; + _devToolsSettingsService.PropertyChanged += DevSettings_PropertyChanged; + } + + public async Task ExecuteAsync(object? parameter = null) + { + var res = await Win32Helper.RunPowershellCommandAsync( + $"& \'{_devToolsSettingsService.IDEPath}\' \'{_context.ShellPage!.InstanceViewModel.GitRepositoryPath}\'", + PowerShellExecutionOptions.Hidden + ); + + if (!res) + await DynamicDialogFactory.ShowFor_IDEErrorDialog(_devToolsSettingsService.IDEName); + } + + private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(IContentPageContext.Folder)) + OnPropertyChanged(nameof(IsExecutable)); + } + + private void DevSettings_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(IDevToolsSettingsService.IDEPath)) + { + OnPropertyChanged(nameof(IsExecutable)); + } + else if (e.PropertyName == nameof(IDevToolsSettingsService.IDEName)) + { + OnPropertyChanged(nameof(Label)); + OnPropertyChanged(nameof(Description)); + } + } + } +} diff --git a/src/Files.App/Actions/Open/OpenRepoInVSCodeAction.cs b/src/Files.App/Actions/Open/OpenRepoInVSCodeAction.cs deleted file mode 100644 index 0a0866244095..000000000000 --- a/src/Files.App/Actions/Open/OpenRepoInVSCodeAction.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.App.Utils.Shell; - -namespace Files.App.Actions -{ - internal sealed class OpenRepoInVSCodeAction : ObservableObject, IAction - { - private readonly IContentPageContext _context; - - private readonly bool _isVSCodeInstalled; - - public string Label - => "OpenRepoInVSCode".GetLocalizedResource(); - - public string Description - => "OpenRepoInVSCodeDescription".GetLocalizedResource(); - - public bool IsExecutable => - _isVSCodeInstalled && - _context.Folder is not null && - _context.ShellPage!.InstanceViewModel.IsGitRepository; - - public OpenRepoInVSCodeAction() - { - _context = Ioc.Default.GetRequiredService(); - - _isVSCodeInstalled = SoftwareHelpers.IsVSCodeInstalled(); - if (_isVSCodeInstalled) - _context.PropertyChanged += Context_PropertyChanged; - } - - public Task ExecuteAsync(object? parameter = null) - { - return Win32Helper.RunPowershellCommandAsync($"code \'{_context.ShellPage!.InstanceViewModel.GitRepositoryPath}\'", false); - } - - private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) - { - if (e.PropertyName == nameof(IContentPageContext.Folder)) - OnPropertyChanged(nameof(IsExecutable)); - } - } -} diff --git a/src/Files.App/Actions/Open/OpenSettingsAction.cs b/src/Files.App/Actions/Open/OpenSettingsAction.cs index cc0888fa4a44..9d201028788a 100644 --- a/src/Files.App/Actions/Open/OpenSettingsAction.cs +++ b/src/Files.App/Actions/Open/OpenSettingsAction.cs @@ -1,26 +1,38 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Files.App.Dialogs; namespace Files.App.Actions { - internal sealed class OpenSettingsAction : BaseUIAction, IAction + [GeneratedRichCommand] + internal sealed partial class OpenSettingsAction : BaseUIAction, IAction { private readonly IDialogService dialogService = Ioc.Default.GetRequiredService(); private readonly SettingsDialogViewModel viewModel = new(); public string Label - => "Settings".GetLocalizedResource(); + => Strings.Settings.GetLocalizedResource(); public string Description - => "OpenSettingsDescription".GetLocalizedResource(); + => Strings.OpenSettingsDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Open; public HotKey HotKey => new(Keys.OemComma, KeyModifiers.Ctrl); + public RichGlyph Glyph + => new(themedIconStyle: "App.ThemedIcons.Settings"); + public Task ExecuteAsync(object? parameter = null) { var dialog = dialogService.GetDialog(viewModel); + if (parameter is not null && parameter is SettingsNavigationParams navParams) + ((SettingsDialog)dialog).NavigateTo(navParams); + return dialog.TryShowAsync(); } } diff --git a/src/Files.App/Actions/Open/OpenSettingsFileAction.cs b/src/Files.App/Actions/Open/OpenSettingsFileAction.cs new file mode 100644 index 000000000000..1563947d0b73 --- /dev/null +++ b/src/Files.App/Actions/Open/OpenSettingsFileAction.cs @@ -0,0 +1,58 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media; +using Windows.Foundation.Metadata; +using Windows.Storage; +using Windows.System; + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class OpenSettingsFileAction : IAction + { + public string Label + => Strings.EditSettingsFile.GetLocalizedResource(); + + public string Description + => Strings.EditSettingsFileDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Open; + + public HotKey HotKey + => new(Keys.OemComma, KeyModifiers.CtrlShift); + + public RichGlyph Glyph + => new("\uE8DA"); + + public async Task ExecuteAsync(object? parameter = null) + { + try + { + var settingsJsonFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri($"ms-appdata:///local/{Constants.LocalSettings.SettingsFolderName}/{Constants.LocalSettings.UserSettingsFileName}")); + if (!await Launcher.LaunchFileAsync(settingsJsonFile)) + await ContextMenu.InvokeVerb("open", settingsJsonFile.Path); + } + catch (Exception ex) + { + // Only show the error dialog if no other popups are open + if (!VisualTreeHelper.GetOpenPopupsForXamlRoot(MainWindow.Instance.Content.XamlRoot).Any()) + { + var errorDialog = new ContentDialog() + { + Title = Strings.FailedToOpenSettingsFile.GetLocalizedResource(), + Content = ex.Message, + PrimaryButtonText = Strings.OK.GetLocalizedResource(), + }; + + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + errorDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; + + await errorDialog.TryShowAsync(); + } + } + } + } +} diff --git a/src/Files.App/Actions/Open/OpenStorageSenseAction.cs b/src/Files.App/Actions/Open/OpenStorageSenseAction.cs new file mode 100644 index 000000000000..4b54f44bf30a --- /dev/null +++ b/src/Files.App/Actions/Open/OpenStorageSenseAction.cs @@ -0,0 +1,48 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal partial class OpenStorageSenseAction : ObservableObject, IAction + { + private readonly IContentPageContext context = Ioc.Default.GetRequiredService(); + private readonly DrivesViewModel drivesViewModel = Ioc.Default.GetRequiredService(); + + public virtual string Label + => Strings.Cleanup.GetLocalizedResource(); + + public virtual string Description + => Strings.OpenStorageSenseDescription.GetLocalizedResource(); + + public virtual ActionCategory Category + => ActionCategory.Open; + + public virtual bool IsExecutable => + context.HasItem && + !context.HasSelection && + drivesViewModel.Drives + .Cast() + .FirstOrDefault(x => string.Equals(x.Path, context.Folder?.ItemPath)) is DriveItem driveItem && + driveItem.Type != DriveType.Network; + + public virtual bool IsAccessibleGlobally + => true; + + public OpenStorageSenseAction() + { + context.PropertyChanged += Context_PropertyChanged; + } + + public virtual Task ExecuteAsync(object? parameter = null) + { + return StorageSenseHelper.OpenStorageSenseAsync(context.Folder?.ItemPath ?? string.Empty); + } + + public void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName is nameof(IContentPageContext.HasItem)) + OnPropertyChanged(nameof(IsExecutable)); + } + } +} diff --git a/src/Files.App/Actions/Open/OpenStorageSenseFromHomeAction.cs b/src/Files.App/Actions/Open/OpenStorageSenseFromHomeAction.cs new file mode 100644 index 000000000000..212dc08a42d7 --- /dev/null +++ b/src/Files.App/Actions/Open/OpenStorageSenseFromHomeAction.cs @@ -0,0 +1,29 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class OpenStorageSenseFromHomeAction : OpenStorageSenseAction + { + private IHomePageContext HomePageContext { get; } = Ioc.Default.GetRequiredService(); + private readonly DrivesViewModel drivesViewModel = Ioc.Default.GetRequiredService(); + + public override bool IsExecutable => + HomePageContext.IsAnyItemRightClicked && + HomePageContext.RightClickedItem is not null && + HomePageContext.RightClickedItem.Path is not null && + drivesViewModel.Drives + .Cast() + .FirstOrDefault(x => string.Equals(x.Path, HomePageContext.RightClickedItem.Path)) is DriveItem driveItem && + driveItem.Type != DriveType.Network; + + public override bool IsAccessibleGlobally + => false; + + public override Task ExecuteAsync(object? parameter = null) + { + return StorageSenseHelper.OpenStorageSenseAsync(HomePageContext?.RightClickedItem?.Path ?? string.Empty); + } + } +} diff --git a/src/Files.App/Actions/Open/OpenStorageSenseFromSidebarAction.cs b/src/Files.App/Actions/Open/OpenStorageSenseFromSidebarAction.cs new file mode 100644 index 000000000000..0f5f5e0f98c0 --- /dev/null +++ b/src/Files.App/Actions/Open/OpenStorageSenseFromSidebarAction.cs @@ -0,0 +1,29 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class OpenStorageSenseFromSidebarAction : OpenStorageSenseAction + { + private ISidebarContext SidebarContext { get; } = Ioc.Default.GetRequiredService(); + private readonly DrivesViewModel drivesViewModel = Ioc.Default.GetRequiredService(); + + public override bool IsExecutable => + SidebarContext.IsItemRightClicked && + SidebarContext.RightClickedItem is not null && + SidebarContext.RightClickedItem.Path is not null && + drivesViewModel.Drives + .Cast() + .FirstOrDefault(x => string.Equals(x.Path, SidebarContext.RightClickedItem.Path)) is DriveItem driveItem && + driveItem.Type != DriveType.Network; + + public override bool IsAccessibleGlobally + => false; + + public override Task ExecuteAsync(object? parameter = null) + { + return StorageSenseHelper.OpenStorageSenseAsync(SidebarContext?.RightClickedItem?.Path ?? string.Empty); + } + } +} \ No newline at end of file diff --git a/src/Files.App/Actions/Open/OpenTerminalAction.cs b/src/Files.App/Actions/Open/OpenTerminalAction.cs index 19704354434c..8dc53e54592b 100644 --- a/src/Files.App/Actions/Open/OpenTerminalAction.cs +++ b/src/Files.App/Actions/Open/OpenTerminalAction.cs @@ -1,20 +1,30 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. +using System.IO; using System.Text; using Windows.Storage; namespace Files.App.Actions { - internal class OpenTerminalAction : ObservableObject, IAction + [GeneratedRichCommand] + internal partial class OpenTerminalAction : ObservableObject, IAction { private readonly IContentPageContext context; + private const string TerminalExecutable = "wt.exe"; + private static readonly bool isWindowsTerminalAvailable = IsExecutableOnPath(TerminalExecutable); + + protected bool IsWindowsTerminalAvailable + => isWindowsTerminalAvailable; public virtual string Label - => "OpenTerminal".GetLocalizedResource(); + => Strings.OpenTerminal.GetLocalizedResource(); public virtual string Description - => "OpenTerminalDescription".GetLocalizedResource(); + => Strings.OpenTerminalDescription.GetLocalizedResource(); + + public virtual ActionCategory Category + => ActionCategory.Open; public virtual HotKey HotKey => new(Keys.Oem3, KeyModifiers.Ctrl); @@ -22,8 +32,11 @@ public virtual HotKey HotKey public RichGlyph Glyph => new("\uE756"); - public bool IsExecutable - => GetIsExecutable(); + public virtual bool IsExecutable + => IsWindowsTerminalAvailable && GetIsExecutable(); + + public virtual bool IsAccessibleGlobally + => true; public OpenTerminalAction() { @@ -34,6 +47,9 @@ public OpenTerminalAction() public Task ExecuteAsync(object? parameter = null) { + if (!IsWindowsTerminalAvailable) + return Task.CompletedTask; + var terminalStartInfo = GetProcessStartInfo(); if (terminalStartInfo is not null) { @@ -54,6 +70,9 @@ public Task ExecuteAsync(object? parameter = null) protected virtual ProcessStartInfo? GetProcessStartInfo() { + if (!IsWindowsTerminalAvailable) + return null; + var paths = GetPaths(); if (paths.Length is 0) return null; @@ -69,12 +88,13 @@ public Task ExecuteAsync(object? parameter = null) return new() { - FileName = "wt.exe", - Arguments = args.ToString() + FileName = TerminalExecutable, + Arguments = args.ToString(), + UseShellExecute = true }; } - protected string[] GetPaths() + protected virtual string[] GetPaths() { if (context.HasSelection) { @@ -91,9 +111,31 @@ protected string[] GetPaths() return []; } + private static bool IsExecutableOnPath(string executableName) + { + try + { + var path = Environment.GetEnvironmentVariable("PATH"); + if (string.IsNullOrWhiteSpace(path)) + return false; + + foreach (var folder in path.Split(Path.PathSeparator, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)) + { + var candidate = Path.Combine(folder, executableName); + if (File.Exists(candidate)) + return true; + } + } + catch + { + } + + return false; + } + private bool GetIsExecutable() { - if (context.PageType is ContentPageTypes.None or ContentPageTypes.Home or ContentPageTypes.RecycleBin or ContentPageTypes.ZipFolder) + if (context.PageType is ContentPageTypes.None or ContentPageTypes.Home or ContentPageTypes.RecycleBin or ContentPageTypes.ZipFolder or ContentPageTypes.ReleaseNotes or ContentPageTypes.Settings) return false; var isFolderNull = context.Folder is null; diff --git a/src/Files.App/Actions/Open/OpenTerminalAsAdminAction.cs b/src/Files.App/Actions/Open/OpenTerminalAsAdminAction.cs index c629e8fda55f..0d00ce8f364a 100644 --- a/src/Files.App/Actions/Open/OpenTerminalAsAdminAction.cs +++ b/src/Files.App/Actions/Open/OpenTerminalAsAdminAction.cs @@ -1,15 +1,16 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class OpenTerminalAsAdminAction : OpenTerminalAction + [GeneratedRichCommand] + internal sealed partial class OpenTerminalAsAdminAction : OpenTerminalAction { public override string Label - => "OpenTerminalAsAdmin".GetLocalizedResource(); + => Strings.OpenTerminalAsAdmin.GetLocalizedResource(); public override string Description - => "OpenTerminalAsAdminDescription".GetLocalizedResource(); + => Strings.OpenTerminalAsAdminDescription.GetLocalizedResource(); public override HotKey HotKey => new(Keys.Oem3, KeyModifiers.CtrlShift); diff --git a/src/Files.App/Actions/Open/OpenTerminalFromHomeAction.cs b/src/Files.App/Actions/Open/OpenTerminalFromHomeAction.cs new file mode 100644 index 000000000000..af948cc67ee6 --- /dev/null +++ b/src/Files.App/Actions/Open/OpenTerminalFromHomeAction.cs @@ -0,0 +1,41 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class OpenTerminalFromHomeAction : OpenTerminalAction + { + private IHomePageContext HomePageContext { get; } = Ioc.Default.GetRequiredService(); + + public override string Label + => Strings.OpenTerminal.GetLocalizedResource(); + + public override string Description + => Strings.OpenTerminalDescription.GetLocalizedResource(); + + public override bool IsExecutable => + IsWindowsTerminalAvailable && + HomePageContext.IsAnyItemRightClicked && + HomePageContext.RightClickedItem is not null && + (HomePageContext.RightClickedItem is WidgetFileTagCardItem fileTagItem + ? fileTagItem.IsFolder + : true) && + HomePageContext.RightClickedItem.Path is not null && + HomePageContext.RightClickedItem.Path != Constants.UserEnvironmentPaths.RecycleBinPath; + + public override bool IsAccessibleGlobally + => false; + + public override HotKey HotKey + => HotKey.None; + + protected override string[] GetPaths() + { + if (HomePageContext.IsAnyItemRightClicked && HomePageContext.RightClickedItem?.Path is not null) + return [HomePageContext.RightClickedItem.Path]; + + return []; + } + } +} diff --git a/src/Files.App/Actions/Open/OpenTerminalFromSidebarAction.cs b/src/Files.App/Actions/Open/OpenTerminalFromSidebarAction.cs new file mode 100644 index 000000000000..f957e83910a4 --- /dev/null +++ b/src/Files.App/Actions/Open/OpenTerminalFromSidebarAction.cs @@ -0,0 +1,38 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class OpenTerminalFromSidebarAction : OpenTerminalAction + { + private ISidebarContext SidebarContext { get; } = Ioc.Default.GetRequiredService(); + + public override string Label + => Strings.OpenTerminal.GetLocalizedResource(); + + public override string Description + => Strings.OpenTerminalDescription.GetLocalizedResource(); + + public override bool IsExecutable => + IsWindowsTerminalAvailable && + SidebarContext.IsItemRightClicked && + SidebarContext.RightClickedItem is not null && + SidebarContext.RightClickedItem.MenuOptions.ShowShellItems && + !SidebarContext.RightClickedItem.MenuOptions.ShowEmptyRecycleBin; + + public override bool IsAccessibleGlobally + => false; + + public override HotKey HotKey + => HotKey.None; + + protected override string[] GetPaths() + { + if (SidebarContext.IsItemRightClicked && SidebarContext.RightClickedItem is not null) + return [SidebarContext.RightClickedItem.Path]; + + return []; + } + } +} diff --git a/src/Files.App/Actions/Show/ToggleDetailsPaneAction.cs b/src/Files.App/Actions/Show/ToggleDetailsPaneAction.cs index ffe700944b14..ac8d8ace16cf 100644 --- a/src/Files.App/Actions/Show/ToggleDetailsPaneAction.cs +++ b/src/Files.App/Actions/Show/ToggleDetailsPaneAction.cs @@ -1,37 +1,39 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class ToggleDetailsPaneAction : ObservableObject, IToggleAction + [GeneratedRichCommand] + internal sealed partial class ToggleDetailsPaneAction : ObservableObject, IAction { - private readonly InfoPaneViewModel viewModel; + private readonly InfoPaneViewModel infoPaneViewModel = Ioc.Default.GetRequiredService(); private readonly IInfoPaneSettingsService infoPaneSettingsService = Ioc.Default.GetRequiredService(); public string Label - => "ToggleDetailsPane".GetLocalizedResource(); + => Strings.ToggleDetailsPane.GetLocalizedResource(); public string Description - => "ToggleDetailsPaneDescription".GetLocalizedResource(); + => Strings.ToggleDetailsPaneDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Show; public RichGlyph Glyph - => new(opacityStyle: "ColorIconRightPane"); + => new(themedIconStyle: "App.ThemedIcons.PanelRight"); - public HotKey HotKey - => new(Keys.D, KeyModifiers.CtrlAlt); + public bool IsAccessibleGlobally + => false; - public bool IsOn - => viewModel.IsEnabled; + public bool IsExecutable + => infoPaneViewModel.IsEnabled; public ToggleDetailsPaneAction() { - viewModel = Ioc.Default.GetRequiredService(); - viewModel.PropertyChanged += ViewModel_PropertyChanged; + infoPaneViewModel.PropertyChanged += ViewModel_PropertyChanged; } public Task ExecuteAsync(object? parameter = null) { - viewModel.IsEnabled = true; infoPaneSettingsService.SelectedTab = InfoPaneTabs.Details; return Task.CompletedTask; @@ -40,7 +42,7 @@ public Task ExecuteAsync(object? parameter = null) private void ViewModel_PropertyChanged(object? sender, PropertyChangedEventArgs e) { if (e.PropertyName is nameof(InfoPaneViewModel.IsEnabled)) - OnPropertyChanged(nameof(IsOn)); + OnPropertyChanged(nameof(IsExecutable)); } } } diff --git a/src/Files.App/Actions/Show/ToggleDotFilesSettingAction.cs b/src/Files.App/Actions/Show/ToggleDotFilesSettingAction.cs new file mode 100644 index 000000000000..7743fe3beae5 --- /dev/null +++ b/src/Files.App/Actions/Show/ToggleDotFilesSettingAction.cs @@ -0,0 +1,43 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class ToggleDotFilesSettingAction : ObservableObject, IToggleAction + { + private readonly IFoldersSettingsService FoldersSettingsService; + + public string Label + => Strings.ShowDotFiles.GetLocalizedResource(); + + public string Description + => Strings.ToggleDotFilesSettingDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Show; + + public bool IsOn + => FoldersSettingsService.ShowDotFiles; + + public ToggleDotFilesSettingAction() + { + FoldersSettingsService = Ioc.Default.GetRequiredService(); + + FoldersSettingsService.PropertyChanged += Settings_PropertyChanged; + } + + public Task ExecuteAsync(object? parameter = null) + { + FoldersSettingsService.ShowDotFiles = !FoldersSettingsService.ShowDotFiles; + + return Task.CompletedTask; + } + + private void Settings_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName is nameof(IFoldersSettingsService.ShowDotFiles)) + OnPropertyChanged(nameof(IsOn)); + } + } +} diff --git a/src/Files.App/Actions/Show/ToggleDualPaneAction.cs b/src/Files.App/Actions/Show/ToggleDualPaneAction.cs new file mode 100644 index 000000000000..60c6e8fc50ea --- /dev/null +++ b/src/Files.App/Actions/Show/ToggleDualPaneAction.cs @@ -0,0 +1,53 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class ToggleDualPaneAction : ObservableObject, IToggleAction + { + private readonly IContentPageContext ContentPageContext = Ioc.Default.GetRequiredService(); + private readonly IGeneralSettingsService generalSettingsService = Ioc.Default.GetRequiredService(); + + public string Label + => Strings.ToggleDualPane.GetLocalizedResource(); + + public string Description + => Strings.ToggleDualPaneDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.DualPane; + + public HotKey HotKey + => new(Keys.S, KeyModifiers.CtrlShift); + + public bool IsOn + => ContentPageContext.IsMultiPaneActive; + + public ToggleDualPaneAction() + { + ContentPageContext.PropertyChanged += ContentPageContext_PropertyChanged; + } + + public Task ExecuteAsync(object? parameter = null) + { + if (IsOn) + ContentPageContext.ShellPage?.PaneHolder.CloseOtherPane(); + else + ContentPageContext.ShellPage?.PaneHolder.OpenSecondaryPane(ContentPageContext.ShellPage!.ShellViewModel.WorkingDirectory, generalSettingsService.ShellPaneArrangementOption); + + return Task.CompletedTask; + } + + private void ContentPageContext_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + switch (e.PropertyName) + { + case nameof(IContentPageContext.ShellPage): + case nameof(IContentPageContext.IsMultiPaneActive): + OnPropertyChanged(nameof(IsOn)); + break; + } + } + } +} diff --git a/src/Files.App/Actions/Show/ToggleFilterHeaderAction.cs b/src/Files.App/Actions/Show/ToggleFilterHeaderAction.cs new file mode 100644 index 000000000000..ef39911eea13 --- /dev/null +++ b/src/Files.App/Actions/Show/ToggleFilterHeaderAction.cs @@ -0,0 +1,57 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class ToggleFilterHeaderAction : ObservableObject, IToggleAction + { + private readonly IGeneralSettingsService generalSettingsService = Ioc.Default.GetRequiredService(); + private readonly IContentPageContext ContentPageContext = Ioc.Default.GetRequiredService(); + + public string Label + => Strings.ToggleFilterHeader.GetLocalizedResource(); + + public string Description + => Strings.ToggleFilterHeaderDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Show; + + public RichGlyph Glyph + => new(themedIconStyle: "App.ThemedIcons.Filter"); + + public HotKey HotKey + => new(Keys.F, KeyModifiers.CtrlShift); + + public bool IsOn + => generalSettingsService.ShowFilterHeader; + + public ToggleFilterHeaderAction() + { + generalSettingsService.PropertyChanged += GeneralSettingsService_PropertyChanged; + } + + public Task ExecuteAsync(object? parameter = null) + { + generalSettingsService.ShowFilterHeader = !IsOn; + + // Only attempt to focus if there's an active shell page + if (ContentPageContext.ShellPage is not null) + { + if (IsOn) + ContentPageContext.ShellPage.ShellViewModel.InvokeFocusFilterHeader(); + else + ContentPageContext.ShellPage.PaneHolder.FocusActivePane(); + } + + return Task.CompletedTask; + } + + private void GeneralSettingsService_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName is nameof(GeneralSettingsService.ShowFilterHeader)) + OnPropertyChanged(nameof(IsOn)); + } + } +} diff --git a/src/Files.App/Actions/Show/ToggleInfoPaneAction.cs b/src/Files.App/Actions/Show/ToggleInfoPaneAction.cs index 4ba858dff593..e94c95655047 100644 --- a/src/Files.App/Actions/Show/ToggleInfoPaneAction.cs +++ b/src/Files.App/Actions/Show/ToggleInfoPaneAction.cs @@ -1,20 +1,24 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class ToggleInfoPaneAction : ObservableObject, IToggleAction + [GeneratedRichCommand] + internal sealed partial class ToggleInfoPaneAction : ObservableObject, IToggleAction { private readonly InfoPaneViewModel viewModel; public string Label - => "ToggleInfoPane".GetLocalizedResource(); + => Strings.ToggleInfoPane.GetLocalizedResource(); public string Description - => "ToggleInfoPaneDescription".GetLocalizedResource(); + => Strings.ToggleInfoPaneDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Show; public RichGlyph Glyph - => new(opacityStyle: "ColorIconRightPane"); + => new(themedIconStyle: "App.ThemedIcons.PanelRight"); public HotKey HotKey => new(Keys.I, KeyModifiers.CtrlAlt); diff --git a/src/Files.App/Actions/Show/TogglePreviewPaneAction.cs b/src/Files.App/Actions/Show/TogglePreviewPaneAction.cs index 6585d16cabb6..717500c346ab 100644 --- a/src/Files.App/Actions/Show/TogglePreviewPaneAction.cs +++ b/src/Files.App/Actions/Show/TogglePreviewPaneAction.cs @@ -1,37 +1,39 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class TogglePreviewPaneAction : ObservableObject, IToggleAction + [GeneratedRichCommand] + internal sealed partial class TogglePreviewPaneAction : ObservableObject, IAction { - private readonly InfoPaneViewModel viewModel; + private readonly InfoPaneViewModel infoPaneViewModel = Ioc.Default.GetRequiredService(); private readonly IInfoPaneSettingsService infoPaneSettingsService = Ioc.Default.GetRequiredService(); public string Label - => "TogglePreviewPane".GetLocalizedResource(); + => Strings.TogglePreviewPane.GetLocalizedResource(); public string Description - => "TogglePreviewPaneDescription".GetLocalizedResource(); + => Strings.TogglePreviewPaneDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Show; public RichGlyph Glyph - => new(opacityStyle: "ColorIconRightPane"); + => new(themedIconStyle: "App.ThemedIcons.PanelRight"); - public HotKey HotKey - => new(Keys.P, KeyModifiers.CtrlAlt); + public bool IsAccessibleGlobally + => false; - public bool IsOn - => viewModel.IsEnabled; + public bool IsExecutable + => infoPaneViewModel.IsEnabled; public TogglePreviewPaneAction() { - viewModel = Ioc.Default.GetRequiredService(); - viewModel.PropertyChanged += ViewModel_PropertyChanged; + infoPaneViewModel.PropertyChanged += ViewModel_PropertyChanged; } public Task ExecuteAsync(object? parameter = null) { - viewModel.IsEnabled = true; infoPaneSettingsService.SelectedTab = InfoPaneTabs.Preview; return Task.CompletedTask; @@ -40,7 +42,7 @@ public Task ExecuteAsync(object? parameter = null) private void ViewModel_PropertyChanged(object? sender, PropertyChangedEventArgs e) { if (e.PropertyName is nameof(InfoPaneViewModel.IsEnabled)) - OnPropertyChanged(nameof(IsOn)); + OnPropertyChanged(nameof(IsExecutable)); } } } diff --git a/src/Files.App/Actions/Show/ToggleShelfPaneAction.cs b/src/Files.App/Actions/Show/ToggleShelfPaneAction.cs new file mode 100644 index 000000000000..26df6649a6be --- /dev/null +++ b/src/Files.App/Actions/Show/ToggleShelfPaneAction.cs @@ -0,0 +1,52 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class ToggleShelfPaneAction : ObservableObject, IToggleAction + { + private readonly IGeneralSettingsService generalSettingsService = Ioc.Default.GetRequiredService(); + + public string Label + => Strings.ToggleShelfPane.GetLocalizedResource(); + + public string Description + => Strings.ToggleShelfPaneDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Show; + + public RichGlyph Glyph + => new(themedIconStyle: "App.ThemedIcons.Shelf"); + + // TODO Remove IsAccessibleGlobally when shelf feature is ready + public bool IsAccessibleGlobally + => AppLifecycleHelper.AppEnvironment is AppEnvironment.Dev; + + // TODO Remove IsExecutable when shelf feature is ready + public bool IsExecutable + => AppLifecycleHelper.AppEnvironment is AppEnvironment.Dev; + + public bool IsOn + => generalSettingsService.ShowShelfPane; + + public ToggleShelfPaneAction() + { + generalSettingsService.PropertyChanged += GeneralSettingsService_PropertyChanged; + } + + public Task ExecuteAsync(object? parameter = null) + { + generalSettingsService.ShowShelfPane = !IsOn; + + return Task.CompletedTask; + } + + private void GeneralSettingsService_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName is nameof(GeneralSettingsService.ShowShelfPane)) + OnPropertyChanged(nameof(IsOn)); + } + } +} diff --git a/src/Files.App/Actions/Show/ToggleShowFileExtensionsAction.cs b/src/Files.App/Actions/Show/ToggleShowFileExtensionsAction.cs index e6ebec244196..d4b7aee8891a 100644 --- a/src/Files.App/Actions/Show/ToggleShowFileExtensionsAction.cs +++ b/src/Files.App/Actions/Show/ToggleShowFileExtensionsAction.cs @@ -1,17 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class ToggleShowFileExtensionsAction : ObservableObject, IToggleAction + [GeneratedRichCommand] + internal sealed partial class ToggleShowFileExtensionsAction : ObservableObject, IToggleAction { private readonly IFoldersSettingsService settings; public string Label - => "ShowFileExtensions".GetLocalizedResource(); + => Strings.ShowFileExtensions.GetLocalizedResource(); public string Description - => "ToggleShowFileExtensionsDescription".GetLocalizedResource(); + => Strings.ToggleShowFileExtensionsDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Show; public bool IsOn => settings.ShowFileExtensions; diff --git a/src/Files.App/Actions/Show/ToggleShowHiddenItemsAction.cs b/src/Files.App/Actions/Show/ToggleShowHiddenItemsAction.cs index ccb62c0662f4..6120151e1929 100644 --- a/src/Files.App/Actions/Show/ToggleShowHiddenItemsAction.cs +++ b/src/Files.App/Actions/Show/ToggleShowHiddenItemsAction.cs @@ -1,17 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class ToggleShowHiddenItemsAction : ObservableObject, IToggleAction + [GeneratedRichCommand] + internal sealed partial class ToggleShowHiddenItemsAction : ObservableObject, IToggleAction { private readonly IFoldersSettingsService settings; public string Label - => "ShowHiddenItems".GetLocalizedResource(); + => Strings.HiddenItems.GetLocalizedResource(); public string Description - => "ToggleShowHiddenItemsDescription".GetLocalizedResource(); + => Strings.ToggleShowHiddenItemsDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Show; public HotKey HotKey => new(Keys.H, KeyModifiers.Ctrl); diff --git a/src/Files.App/Actions/Show/ToggleSidebarAction.cs b/src/Files.App/Actions/Show/ToggleSidebarAction.cs new file mode 100644 index 000000000000..d81dd8751059 --- /dev/null +++ b/src/Files.App/Actions/Show/ToggleSidebarAction.cs @@ -0,0 +1,50 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Files.App.Controls; +using Files.App.ViewModels.UserControls; + +namespace Files.App.Actions +{ + [GeneratedRichCommand] + internal sealed partial class ToggleSidebarAction : ObservableObject, IToggleAction + { + private IAppearanceSettingsService AppearanceSettingsService { get; } = Ioc.Default.GetRequiredService(); + + public string Label + => Strings.ToggleSidebar.GetLocalizedResource(); + + public string Description + => Strings.ToggleSidebarDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Show; + + public HotKey HotKey + => new(Keys.B, KeyModifiers.Ctrl); + + public bool IsOn + => AppearanceSettingsService.IsSidebarOpen; + + public ToggleSidebarAction() + { + AppearanceSettingsService.PropertyChanged += AppearanceSettingsService_PropertyChanged; + } + + public Task ExecuteAsync(object? parameter = null) + { + var sidebarViewModel = Ioc.Default.GetRequiredService(); + sidebarViewModel.SidebarDisplayMode = IsOn + ? SidebarDisplayMode.Compact + : SidebarDisplayMode.Expanded; + + return Task.CompletedTask; + } + + private void AppearanceSettingsService_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName is nameof(AppearanceSettingsService.IsSidebarOpen)) + OnPropertyChanged(nameof(IsOn)); + } + } +} diff --git a/src/Files.App/Actions/Show/ToggleToolbarAction.cs b/src/Files.App/Actions/Show/ToggleToolbarAction.cs index a027cb4941ab..4e85daa54a19 100644 --- a/src/Files.App/Actions/Show/ToggleToolbarAction.cs +++ b/src/Files.App/Actions/Show/ToggleToolbarAction.cs @@ -1,17 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class ToggleToolbarAction : ObservableObject, IToggleAction + [GeneratedRichCommand] + internal sealed partial class ToggleToolbarAction : ObservableObject, IToggleAction { private IAppearanceSettingsService AppearanceSettingsService { get; } = Ioc.Default.GetRequiredService(); public string Label - => "ToggleToolbar".GetLocalizedResource(); + => Strings.ToggleToolbar.GetLocalizedResource(); public string Description - => "ToggleToolbar".GetLocalizedResource(); + => Strings.ToggleToolbarDescription.GetLocalizedResource(); + + public ActionCategory Category + => ActionCategory.Show; public HotKey HotKey => new(Keys.B, KeyModifiers.CtrlShift); diff --git a/src/Files.App/Actions/Sidebar/CopyItemFromSidebarAction.cs b/src/Files.App/Actions/Sidebar/CopyItemFromSidebarAction.cs new file mode 100644 index 000000000000..acacbd8f2edd --- /dev/null +++ b/src/Files.App/Actions/Sidebar/CopyItemFromSidebarAction.cs @@ -0,0 +1,127 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.Extensions.Logging; +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 ActionCategory Category + => ActionCategory.FileSystem; + + public RichGlyph Glyph + => new(themedIconStyle: "App.ThemedIcons.Copy"); + + public bool IsExecutable + => GetIsExecutable(); + + public bool IsAccessibleGlobally + => false; + + public CopyItemFromSidebarAction() + { + context = Ioc.Default.GetRequiredService(); + SidebarContext = Ioc.Default.GetRequiredService(); + } + + 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)!; + 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); + } + } +} diff --git a/src/Files.App/Actions/Sidebar/PinFolderToSidebarAction.cs b/src/Files.App/Actions/Sidebar/PinFolderToSidebarAction.cs index dda75fed7bf7..5c8dd8479fe8 100644 --- a/src/Files.App/Actions/Sidebar/PinFolderToSidebarAction.cs +++ b/src/Files.App/Actions/Sidebar/PinFolderToSidebarAction.cs @@ -1,23 +1,27 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.Storage; namespace Files.App.Actions { - internal sealed class PinFolderToSidebarAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class PinFolderToSidebarAction : ObservableObject, IAction { private readonly IContentPageContext context; private readonly IQuickAccessService service; public string Label - => "PinFolderToSidebar".GetLocalizedResource(); + => Strings.PinFolderToSidebar.GetLocalizedResource(); public string Description - => "PinFolderToSidebarDescription".GetLocalizedResource(); + => Strings.PinFolderToSidebarDescription.GetLocalizedFormatResource(context.HasSelection ? context.SelectedItems.Count : 1); + + public ActionCategory Category + => ActionCategory.FileSystem; public RichGlyph Glyph - => new(opacityStyle: "Icons.Pin.16x16"); + => new(themedIconStyle: "App.ThemedIcons.FavoritePin"); public bool IsExecutable => GetIsExecutable(); @@ -47,6 +51,9 @@ public async Task ExecuteAsync(object? parameter = null) private bool GetIsExecutable() { + if (context.PageType == ContentPageTypes.RecycleBin) + return false; + string[] pinnedFolders = [.. App.QuickAccessManager.Model.PinnedFolders]; return context.HasSelection diff --git a/src/Files.App/Actions/Sidebar/UnpinFolderToSidebarAction.cs b/src/Files.App/Actions/Sidebar/UnpinFolderToSidebarAction.cs index fd738d74983d..71ca41553269 100644 --- a/src/Files.App/Actions/Sidebar/UnpinFolderToSidebarAction.cs +++ b/src/Files.App/Actions/Sidebar/UnpinFolderToSidebarAction.cs @@ -1,21 +1,25 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { - internal sealed class UnpinFolderFromSidebarAction : ObservableObject, IAction + [GeneratedRichCommand] + internal sealed partial class UnpinFolderFromSidebarAction : ObservableObject, IAction { private readonly IContentPageContext context; private readonly IQuickAccessService service; public string Label - => "UnpinFolderFromSidebar".GetLocalizedResource(); + => Strings.UnpinFolderFromSidebar.GetLocalizedResource(); public string Description - => "UnpinFolderFromSidebarDescription".GetLocalizedResource(); + => Strings.UnpinFolderFromSidebarDescription.GetLocalizedFormatResource(context.HasSelection ? context.SelectedItems.Count : 1); + + public ActionCategory Category + => ActionCategory.FileSystem; public RichGlyph Glyph - => new(opacityStyle: "Icons.Unpin.16x16"); + => new(themedIconStyle: "App.ThemedIcons.FavoritePinRemove"); public bool IsExecutable => GetIsExecutable(); diff --git a/src/Files.App/Actions/Start/PinToStartAction.cs b/src/Files.App/Actions/Start/PinToStartAction.cs index 6f18eaecfff3..e49cebe4f484 100644 --- a/src/Files.App/Actions/Start/PinToStartAction.cs +++ b/src/Files.App/Actions/Start/PinToStartAction.cs @@ -1,11 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using CommunityToolkit.Mvvm.DependencyInjection; -using Files.Core.Storage; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { + [GeneratedRichCommand] internal sealed class PinToStartAction : IAction { private IStorageService StorageService { get; } = Ioc.Default.GetRequiredService(); @@ -15,13 +13,16 @@ internal sealed class PinToStartAction : IAction public IContentPageContext context; public string Label - => "PinItemToStart/Text".GetLocalizedResource(); + => Strings.PinItemToStart_Text.GetLocalizedResource(); public string Description - => "PinToStartDescription".GetLocalizedResource(); + => Strings.PinToStartDescription.GetLocalizedFormatResource(context.HasSelection ? context.SelectedItems.Count : 1); public RichGlyph Glyph - => new(opacityStyle: "Icons.Pin.16x16"); + => new(themedIconStyle: "App.ThemedIcons.FavoritePin"); + + public ActionCategory Category + => ActionCategory.Start; public bool IsExecutable => context.ShellPage is not null; @@ -37,20 +38,26 @@ public async Task ExecuteAsync(object? parameter = null) { foreach (ListedItem listedItem in context.ShellPage.SlimContentPage.SelectedItems) { - IStorable storable = listedItem.IsFolder switch + await SafetyExtensions.IgnoreExceptions(async () => { - true => await StorageService.GetFolderAsync(listedItem.ItemPath), - _ => await StorageService.GetFileAsync((listedItem as ShortcutItem)?.TargetPath ?? listedItem.ItemPath) - }; - await StartMenuService.PinAsync(storable, listedItem.Name); + IStorable storable = listedItem.IsFolder switch + { + true => await StorageService.GetFolderAsync(listedItem.ItemPath), + _ => await StorageService.GetFileAsync((listedItem as IShortcutItem)?.TargetPath ?? listedItem.ItemPath) + }; + await StartMenuService.PinAsync(storable, listedItem.Name); + }); } } else if (context.ShellPage?.ShellViewModel?.CurrentFolder is not null) { - var currentFolder = context.ShellPage.ShellViewModel.CurrentFolder; - var folder = await StorageService.GetFolderAsync(currentFolder.ItemPath); + await SafetyExtensions.IgnoreExceptions(async () => + { + var currentFolder = context.ShellPage.ShellViewModel.CurrentFolder; + var folder = await StorageService.GetFolderAsync(currentFolder.ItemPath); - await StartMenuService.PinAsync(folder, currentFolder.Name); + await StartMenuService.PinAsync(folder, currentFolder.Name); + }); } } } diff --git a/src/Files.App/Actions/Start/UnpinFromStartAction.cs b/src/Files.App/Actions/Start/UnpinFromStartAction.cs index dec43e0247e9..01467af64155 100644 --- a/src/Files.App/Actions/Start/UnpinFromStartAction.cs +++ b/src/Files.App/Actions/Start/UnpinFromStartAction.cs @@ -1,10 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.Core.Storage; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Actions { + [GeneratedRichCommand] internal sealed class UnpinFromStartAction : IAction { private IStorageService StorageService { get; } = Ioc.Default.GetRequiredService(); @@ -14,13 +13,16 @@ internal sealed class UnpinFromStartAction : IAction public IContentPageContext context; public string Label - => "UnpinItemFromStart/Text".GetLocalizedResource(); + => Strings.UnpinItemFromStart_Text.GetLocalizedResource(); public string Description - => "UnpinFromStartDescription".GetLocalizedResource(); + => Strings.UnpinFromStartDescription.GetLocalizedFormatResource(context.HasSelection ? context.SelectedItems.Count : 1); public RichGlyph Glyph - => new(opacityStyle: "Icons.Unpin.16x16"); + => new(themedIconStyle: "App.ThemedIcons.FavoritePinRemove"); + + public ActionCategory Category + => ActionCategory.Start; public UnpinFromStartAction() { @@ -33,20 +35,26 @@ public async Task ExecuteAsync(object? parameter = null) { foreach (ListedItem listedItem in context.ShellPage?.SlimContentPage.SelectedItems) { - IStorable storable = listedItem.IsFolder switch + await SafetyExtensions.IgnoreExceptions(async () => { - true => await StorageService.GetFolderAsync(listedItem.ItemPath), - _ => await StorageService.GetFileAsync((listedItem as ShortcutItem)?.TargetPath ?? listedItem.ItemPath) - }; - await StartMenuService.UnpinAsync(storable); + IStorable storable = listedItem.IsFolder switch + { + true => await StorageService.GetFolderAsync(listedItem.ItemPath), + _ => await StorageService.GetFileAsync((listedItem as IShortcutItem)?.TargetPath ?? listedItem.ItemPath) + }; + await StartMenuService.UnpinAsync(storable); + }); } } else { - var currentFolder = context.ShellPage.ShellViewModel.CurrentFolder; - var folder = await StorageService.GetFolderAsync(currentFolder.ItemPath); + await SafetyExtensions.IgnoreExceptions(async () => + { + var currentFolder = context.ShellPage.ShellViewModel.CurrentFolder; + var folder = await StorageService.GetFolderAsync(currentFolder.ItemPath); - await StartMenuService.UnpinAsync(folder); + await StartMenuService.UnpinAsync(folder); + }); } } } diff --git a/src/Files.App/App.xaml b/src/Files.App/App.xaml index c5e84ee6abd2..3b068bc4e279 100644 --- a/src/Files.App/App.xaml +++ b/src/Files.App/App.xaml @@ -1,4 +1,4 @@ - + + True + @@ -31,14 +33,34 @@ + + + + + + + + + + + + - @@ -60,6 +82,7 @@ #0070CB + @@ -77,6 +100,7 @@ #50C0FF + @@ -94,6 +118,7 @@ #50C0FF + diff --git a/src/Files.App/App.xaml.cs b/src/Files.App/App.xaml.cs index 3e9cf91b98a1..06ead247de72 100644 --- a/src/Files.App/App.xaml.cs +++ b/src/Files.App/App.xaml.cs @@ -1,7 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using CommunityToolkit.WinUI.Helpers; using Files.App.Helpers.Application; using Microsoft.Extensions.Logging; using Microsoft.UI.Xaml; @@ -18,7 +17,7 @@ namespace Files.App /// public partial class App : Application { - private static SystemTrayIcon? SystemTrayIcon { get; set; } + public static SystemTrayIcon? SystemTrayIcon { get; private set; } public static TaskCompletionSource? SplashScreenLoadingTCS { get; private set; } public static string? OutputPath { get; set; } @@ -39,7 +38,6 @@ public static CommandBarFlyout? LastOpenedFlyout public static QuickAccessManager QuickAccessManager { get; private set; } = null!; public static StorageHistoryWrapper HistoryWrapper { get; private set; } = null!; public static FileTagsManager FileTagsManager { get; private set; } = null!; - public static RecentItems RecentItemsManager { get; private set; } = null!; public static LibraryManager LibraryManager { get; private set; } = null!; public static AppModel AppModel { get; private set; } = null!; public static ILogger Logger { get; private set; } = null!; @@ -82,18 +80,13 @@ async Task ActivateAsync() MainWindow.Instance.ShowSplashScreen(); } - // Start tracking app usage - if (appActivationArguments.Data is Windows.ApplicationModel.Activation.IActivatedEventArgs activationEventArgs) - SystemInformation.Instance.TrackAppUse(activationEventArgs); - // Configure the DI (dependency injection) container var host = AppLifecycleHelper.ConfigureHost(); Ioc.Default.ConfigureServices(host.Services); -#if STORE || STABLE || PREVIEW // Configure Sentry - AppLifecycleHelper.ConfigureSentry(); -#endif + if (AppLifecycleHelper.AppEnvironment is not AppEnvironment.Dev) + AppLifecycleHelper.ConfigureSentry(); var userSettingsService = Ioc.Default.GetRequiredService(); var isLeaveAppRunning = userSettingsService.GeneralSettingsService.LeaveAppRunning; @@ -114,7 +107,6 @@ async Task ActivateAsync() QuickAccessManager = Ioc.Default.GetRequiredService(); HistoryWrapper = Ioc.Default.GetRequiredService(); FileTagsManager = Ioc.Default.GetRequiredService(); - RecentItemsManager = Ioc.Default.GetRequiredService(); LibraryManager = Ioc.Default.GetRequiredService(); Logger = Ioc.Default.GetRequiredService>(); AppModel = Ioc.Default.GetRequiredService(); @@ -132,14 +124,18 @@ async Task ActivateAsync() SplashScreenLoadingTCS = null; // Create a system tray icon - SystemTrayIcon = new SystemTrayIcon().Show(); + SystemTrayIcon = new SystemTrayIcon(); + if (userSettingsService.GeneralSettingsService.ShowSystemTrayIcon) + SystemTrayIcon.Show(); _ = MainWindow.Instance.InitializeApplicationAsync(appActivationArguments.Data); } else { // Create a system tray icon - SystemTrayIcon = new SystemTrayIcon().Show(); + SystemTrayIcon = new SystemTrayIcon(); + if (userSettingsService.GeneralSettingsService.ShowSystemTrayIcon) + SystemTrayIcon.Show(); // Sleep current instance Program.Pool = new(0, 1, $"Files-{AppLifecycleHelper.AppEnvironment}-Instance"); @@ -164,7 +160,10 @@ async Task ActivateAsync() public async Task OnActivatedAsync(AppActivationArguments activatedEventArgs) { var activatedEventArgsData = activatedEventArgs.Data; - Logger.LogInformation($"The app is being activated. Activation type: {activatedEventArgsData.GetType().Name}"); + + // Logger may not be initialized yet due to race condition during startup + if (Logger is not null) + Logger.LogInformation($"The app is being activated. Activation type: {activatedEventArgsData.GetType().Name}"); // InitializeApplication accesses UI, needs to be called on UI thread await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() @@ -176,6 +175,8 @@ await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() /// private void Window_Activated(object sender, WindowActivatedEventArgs args) { + Logger.LogInformation($"Window_Activated: State={args?.WindowActivationState.ToString()}"); + AppModel.IsMainWindowClosed = false; // TODO(s): Is this code still needed? @@ -197,6 +198,7 @@ private async void Window_Closed(object sender, WindowEventArgs args) // Save application state and stop any background activity IUserSettingsService userSettingsService = Ioc.Default.GetRequiredService(); StatusCenterViewModel statusCenterViewModel = Ioc.Default.GetRequiredService(); + ICommandManager commandManager = Ioc.Default.GetRequiredService(); // A Workaround for the crash (#10110) if (_LastOpenedFlyout?.IsOpen ?? false) @@ -208,7 +210,10 @@ private async void Window_Closed(object sender, WindowEventArgs args) } // Save the current tab list in case it was overwriten by another instance - AppLifecycleHelper.SaveSessionTabs(); + if (userSettingsService.GeneralSettingsService.ContinueLastSessionOnStartUp || userSettingsService.AppSettingsService.RestoreTabsOnStartup) + AppLifecycleHelper.SaveSessionTabs(); + else + await commandManager.CloseAllTabs.ExecuteAsync(); if (OutputPath is not null) { diff --git a/src/Files.App/Assets/AppTiles/Dev/BadgeLogo.scale-100.png b/src/Files.App/Assets/AppTiles/Dev/BadgeLogo.scale-100.png new file mode 100644 index 000000000000..bda7a6874f79 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/BadgeLogo.scale-100.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/BadgeLogo.scale-125.png b/src/Files.App/Assets/AppTiles/Dev/BadgeLogo.scale-125.png new file mode 100644 index 000000000000..41b2b38e06d4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/BadgeLogo.scale-125.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/BadgeLogo.scale-150.png b/src/Files.App/Assets/AppTiles/Dev/BadgeLogo.scale-150.png new file mode 100644 index 000000000000..47ff073edf18 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/BadgeLogo.scale-150.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/BadgeLogo.scale-200.png b/src/Files.App/Assets/AppTiles/Dev/BadgeLogo.scale-200.png new file mode 100644 index 000000000000..3c441f2c11e8 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/BadgeLogo.scale-200.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/BadgeLogo.scale-400.png b/src/Files.App/Assets/AppTiles/Dev/BadgeLogo.scale-400.png new file mode 100644 index 000000000000..7cfbee56c45f Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/BadgeLogo.scale-400.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Large310x310Logo.scale-100.png b/src/Files.App/Assets/AppTiles/Dev/Large310x310Logo.scale-100.png new file mode 100644 index 000000000000..523875c2ccd7 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Large310x310Logo.scale-100.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Large310x310Logo.scale-125.png b/src/Files.App/Assets/AppTiles/Dev/Large310x310Logo.scale-125.png new file mode 100644 index 000000000000..dff5c0cc1ad1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Large310x310Logo.scale-125.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Large310x310Logo.scale-150.png b/src/Files.App/Assets/AppTiles/Dev/Large310x310Logo.scale-150.png new file mode 100644 index 000000000000..4e12e9cc2a11 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Large310x310Logo.scale-150.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Large310x310Logo.scale-200.png b/src/Files.App/Assets/AppTiles/Dev/Large310x310Logo.scale-200.png new file mode 100644 index 000000000000..c85ea4348e0d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Large310x310Logo.scale-200.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Large310x310Logo.scale-400.png b/src/Files.App/Assets/AppTiles/Dev/Large310x310Logo.scale-400.png new file mode 100644 index 000000000000..6cbe6a57ea8d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Large310x310Logo.scale-400.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Logo.ico b/src/Files.App/Assets/AppTiles/Dev/Logo.ico new file mode 100644 index 000000000000..98f3727b2308 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Logo.ico differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Small71x71Logo.scale-100.png b/src/Files.App/Assets/AppTiles/Dev/Small71x71Logo.scale-100.png new file mode 100644 index 000000000000..95bc14f64e2c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Small71x71Logo.scale-100.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Small71x71Logo.scale-125.png b/src/Files.App/Assets/AppTiles/Dev/Small71x71Logo.scale-125.png new file mode 100644 index 000000000000..81d3a84cb8f6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Small71x71Logo.scale-125.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Small71x71Logo.scale-150.png b/src/Files.App/Assets/AppTiles/Dev/Small71x71Logo.scale-150.png new file mode 100644 index 000000000000..7a3d6c22115c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Small71x71Logo.scale-150.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Small71x71Logo.scale-200.png b/src/Files.App/Assets/AppTiles/Dev/Small71x71Logo.scale-200.png new file mode 100644 index 000000000000..bbf0746a24e6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Small71x71Logo.scale-200.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Small71x71Logo.scale-400.png b/src/Files.App/Assets/AppTiles/Dev/Small71x71Logo.scale-400.png new file mode 100644 index 000000000000..ecfd04c965e6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Small71x71Logo.scale-400.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/SplashScreen.scale-100.png b/src/Files.App/Assets/AppTiles/Dev/SplashScreen.scale-100.png new file mode 100644 index 000000000000..e2da0955c627 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/SplashScreen.scale-100.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/SplashScreen.scale-125.png b/src/Files.App/Assets/AppTiles/Dev/SplashScreen.scale-125.png new file mode 100644 index 000000000000..0237c2053327 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/SplashScreen.scale-125.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/SplashScreen.scale-150.png b/src/Files.App/Assets/AppTiles/Dev/SplashScreen.scale-150.png new file mode 100644 index 000000000000..5619589cf30c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/SplashScreen.scale-150.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/SplashScreen.scale-200.png b/src/Files.App/Assets/AppTiles/Dev/SplashScreen.scale-200.png new file mode 100644 index 000000000000..5ac05e815421 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/SplashScreen.scale-200.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/SplashScreen.scale-400.png b/src/Files.App/Assets/AppTiles/Dev/SplashScreen.scale-400.png new file mode 100644 index 000000000000..adc8a67049f3 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/SplashScreen.scale-400.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square150x150Logo.scale-100.png b/src/Files.App/Assets/AppTiles/Dev/Square150x150Logo.scale-100.png new file mode 100644 index 000000000000..74c08f1da750 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square150x150Logo.scale-100.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square150x150Logo.scale-125.png b/src/Files.App/Assets/AppTiles/Dev/Square150x150Logo.scale-125.png new file mode 100644 index 000000000000..39d665683dea Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square150x150Logo.scale-125.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square150x150Logo.scale-150.png b/src/Files.App/Assets/AppTiles/Dev/Square150x150Logo.scale-150.png new file mode 100644 index 000000000000..48fd285b79b8 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square150x150Logo.scale-150.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square150x150Logo.scale-200.png b/src/Files.App/Assets/AppTiles/Dev/Square150x150Logo.scale-200.png new file mode 100644 index 000000000000..f79182d0f14a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square150x150Logo.scale-200.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square150x150Logo.scale-400.png b/src/Files.App/Assets/AppTiles/Dev/Square150x150Logo.scale-400.png new file mode 100644 index 000000000000..88cfbd54ae1e Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square150x150Logo.scale-400.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.scale-100.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.scale-100.png new file mode 100644 index 000000000000..5826220d631d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.scale-100.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.scale-125.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.scale-125.png new file mode 100644 index 000000000000..080838b62849 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.scale-125.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.scale-150.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.scale-150.png new file mode 100644 index 000000000000..e1be33904044 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.scale-150.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.scale-200.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.scale-200.png new file mode 100644 index 000000000000..e565c8eb078e Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.scale-200.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.scale-400.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.scale-400.png new file mode 100644 index 000000000000..38080ad84d65 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.scale-400.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-16.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-16.png new file mode 100644 index 000000000000..377e1bbd59cb Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-16.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-16_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-16_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..377e1bbd59cb Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-16_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-16_altform-unplated.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-16_altform-unplated.png new file mode 100644 index 000000000000..377e1bbd59cb Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-16_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-20.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-20.png new file mode 100644 index 000000000000..b5ed3aa5a2de Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-20.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-20_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-20_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..b5ed3aa5a2de Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-20_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-20_altform-unplated.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-20_altform-unplated.png new file mode 100644 index 000000000000..b5ed3aa5a2de Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-20_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-24.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-24.png new file mode 100644 index 000000000000..97abe1f300f1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-24.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-24_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-24_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..97abe1f300f1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-24_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-24_altform-unplated.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-24_altform-unplated.png new file mode 100644 index 000000000000..97abe1f300f1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-24_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-256.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-256.png new file mode 100644 index 000000000000..199cb2454649 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-256.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-256_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-256_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..199cb2454649 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-256_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-256_altform-unplated.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-256_altform-unplated.png new file mode 100644 index 000000000000..199cb2454649 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-256_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-30.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-30.png new file mode 100644 index 000000000000..281f999fdbf1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-30.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-30_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-30_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..281f999fdbf1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-30_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-30_altform-unplated.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-30_altform-unplated.png new file mode 100644 index 000000000000..281f999fdbf1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-30_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-32.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-32.png new file mode 100644 index 000000000000..d88ca7c86b65 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-32.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-32_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-32_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..d88ca7c86b65 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-32_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-32_altform-unplated.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-32_altform-unplated.png new file mode 100644 index 000000000000..d88ca7c86b65 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-32_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-36.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-36.png new file mode 100644 index 000000000000..350b04166891 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-36.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-36_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-36_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..350b04166891 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-36_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-36_altform-unplated.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-36_altform-unplated.png new file mode 100644 index 000000000000..350b04166891 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-36_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-40.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-40.png new file mode 100644 index 000000000000..09ac5961d8d1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-40.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-40_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-40_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..09ac5961d8d1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-40_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-40_altform-unplated.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-40_altform-unplated.png new file mode 100644 index 000000000000..09ac5961d8d1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-40_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-48.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-48.png new file mode 100644 index 000000000000..2af2c82a970f Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-48.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-48_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-48_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..2af2c82a970f Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-48_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-48_altform-unplated.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-48_altform-unplated.png new file mode 100644 index 000000000000..2af2c82a970f Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-48_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-60.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-60.png new file mode 100644 index 000000000000..042ec132a7ac Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-60.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-60_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-60_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..042ec132a7ac Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-60_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-60_altform-unplated.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-60_altform-unplated.png new file mode 100644 index 000000000000..042ec132a7ac Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-60_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-64.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-64.png new file mode 100644 index 000000000000..41ce469f8bff Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-64.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-64_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-64_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..41ce469f8bff Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-64_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-64_altform-unplated.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-64_altform-unplated.png new file mode 100644 index 000000000000..41ce469f8bff Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-64_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-72.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-72.png new file mode 100644 index 000000000000..fff7d6f13ce4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-72.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-72_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-72_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..fff7d6f13ce4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-72_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-72_altform-unplated.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-72_altform-unplated.png new file mode 100644 index 000000000000..fff7d6f13ce4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-72_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-80.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-80.png new file mode 100644 index 000000000000..1f2dcaf0cd1d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-80.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-80_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-80_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..1f2dcaf0cd1d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-80_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-80_altform-unplated.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-80_altform-unplated.png new file mode 100644 index 000000000000..1f2dcaf0cd1d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-80_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-96.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-96.png new file mode 100644 index 000000000000..41b02f0a83ea Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-96.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-96_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-96_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..41b02f0a83ea Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-96_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-96_altform-unplated.png b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-96_altform-unplated.png new file mode 100644 index 000000000000..41b02f0a83ea Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Square44x44Logo.targetsize-96_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/StoreLogo.scale-100.png b/src/Files.App/Assets/AppTiles/Dev/StoreLogo.scale-100.png new file mode 100644 index 000000000000..239660ccd980 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/StoreLogo.scale-100.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/StoreLogo.scale-125.png b/src/Files.App/Assets/AppTiles/Dev/StoreLogo.scale-125.png new file mode 100644 index 000000000000..31cccd74e4b5 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/StoreLogo.scale-125.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/StoreLogo.scale-150.png b/src/Files.App/Assets/AppTiles/Dev/StoreLogo.scale-150.png new file mode 100644 index 000000000000..ba0c748d0715 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/StoreLogo.scale-150.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/StoreLogo.scale-200.png b/src/Files.App/Assets/AppTiles/Dev/StoreLogo.scale-200.png new file mode 100644 index 000000000000..11897364595a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/StoreLogo.scale-200.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/StoreLogo.scale-400.png b/src/Files.App/Assets/AppTiles/Dev/StoreLogo.scale-400.png new file mode 100644 index 000000000000..63a608acfc13 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/StoreLogo.scale-400.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Wide310x150Logo.scale-100.png b/src/Files.App/Assets/AppTiles/Dev/Wide310x150Logo.scale-100.png new file mode 100644 index 000000000000..243d3fea7627 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Wide310x150Logo.scale-100.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Wide310x150Logo.scale-125.png b/src/Files.App/Assets/AppTiles/Dev/Wide310x150Logo.scale-125.png new file mode 100644 index 000000000000..23df4869b6f6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Wide310x150Logo.scale-125.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Wide310x150Logo.scale-150.png b/src/Files.App/Assets/AppTiles/Dev/Wide310x150Logo.scale-150.png new file mode 100644 index 000000000000..f38eb79287bd Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Wide310x150Logo.scale-150.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Wide310x150Logo.scale-200.png b/src/Files.App/Assets/AppTiles/Dev/Wide310x150Logo.scale-200.png new file mode 100644 index 000000000000..aa9b9c37f080 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Wide310x150Logo.scale-200.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/Wide310x150Logo.scale-400.png b/src/Files.App/Assets/AppTiles/Dev/Wide310x150Logo.scale-400.png new file mode 100644 index 000000000000..8643ae3ffa3e Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/Wide310x150Logo.scale-400.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-100_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-100_contrast-black.png new file mode 100644 index 000000000000..bda7a6874f79 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-100_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-125_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-125_contrast-black.png new file mode 100644 index 000000000000..41b2b38e06d4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-125_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-150_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-150_contrast-black.png new file mode 100644 index 000000000000..47ff073edf18 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-150_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-200_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-200_contrast-black.png new file mode 100644 index 000000000000..3c441f2c11e8 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-200_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-400_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-400_contrast-black.png new file mode 100644 index 000000000000..7cfbee56c45f Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/BadgeLogo.scale-400_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-100_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-100_contrast-black.png new file mode 100644 index 000000000000..d35527b1c783 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-100_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-125_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-125_contrast-black.png new file mode 100644 index 000000000000..24f3b97e230d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-125_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-150_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-150_contrast-black.png new file mode 100644 index 000000000000..39ad1fef5a1d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-150_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-200_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-200_contrast-black.png new file mode 100644 index 000000000000..0cdbdd73195e Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-200_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-400_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-400_contrast-black.png new file mode 100644 index 000000000000..dc15d44caf23 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Large310x310Logo.scale-400_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-100_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-100_contrast-black.png new file mode 100644 index 000000000000..406ebe3eadbf Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-100_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-125_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-125_contrast-black.png new file mode 100644 index 000000000000..2ed83a68dd3a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-125_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-150_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-150_contrast-black.png new file mode 100644 index 000000000000..ef115293fdac Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-150_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-200_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-200_contrast-black.png new file mode 100644 index 000000000000..f9080d578545 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-200_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-400_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-400_contrast-black.png new file mode 100644 index 000000000000..e73c070f148c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Small71x71Logo.scale-400_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-100_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-100_contrast-black.png new file mode 100644 index 000000000000..e62085049275 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-100_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-125_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-125_contrast-black.png new file mode 100644 index 000000000000..1b67dd42baf3 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-125_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-150_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-150_contrast-black.png new file mode 100644 index 000000000000..b0a4b2a01821 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-150_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-200_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-200_contrast-black.png new file mode 100644 index 000000000000..78f35751ff93 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-200_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-400_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-400_contrast-black.png new file mode 100644 index 000000000000..0cb1ed63039c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/SplashScreen.scale-400_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-100_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-100_contrast-black.png new file mode 100644 index 000000000000..fb98b59ecfcd Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-100_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-125_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-125_contrast-black.png new file mode 100644 index 000000000000..6f013f45dacc Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-125_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-150_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-150_contrast-black.png new file mode 100644 index 000000000000..f8eda1303e82 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-150_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-200_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-200_contrast-black.png new file mode 100644 index 000000000000..abedac07f4ee Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-200_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-400_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-400_contrast-black.png new file mode 100644 index 000000000000..59aa8844a57b Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square150x150Logo.scale-400_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-100_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-100_contrast-black.png new file mode 100644 index 000000000000..ae842ab9fb92 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-100_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-125_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-125_contrast-black.png new file mode 100644 index 000000000000..d02a4c8eb68c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-125_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-150_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-150_contrast-black.png new file mode 100644 index 000000000000..a36e609502e6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-150_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-200_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-200_contrast-black.png new file mode 100644 index 000000000000..0727fa2193c5 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-200_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-400_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-400_contrast-black.png new file mode 100644 index 000000000000..6d28a39609c3 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.scale-400_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..24469e643d42 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-16_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-16_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..24469e643d42 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-16_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-16_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-16_contrast-black.png new file mode 100644 index 000000000000..24469e643d42 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-16_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..7145f452aba5 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-20_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-20_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..7145f452aba5 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-20_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-20_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-20_contrast-black.png new file mode 100644 index 000000000000..7145f452aba5 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-20_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..bda7a6874f79 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-24_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-24_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..bda7a6874f79 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-24_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-24_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-24_contrast-black.png new file mode 100644 index 000000000000..bda7a6874f79 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-24_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..54dfbe8167dd Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-256_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-256_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..54dfbe8167dd Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-256_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-256_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-256_contrast-black.png new file mode 100644 index 000000000000..54dfbe8167dd Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-256_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..75333e356bad Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-30_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-30_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..75333e356bad Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-30_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-30_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-30_contrast-black.png new file mode 100644 index 000000000000..75333e356bad Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-30_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..e345f65be678 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-32_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-32_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..e345f65be678 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-32_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-32_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-32_contrast-black.png new file mode 100644 index 000000000000..e345f65be678 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-32_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..272eac61f47d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-36_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-36_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..272eac61f47d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-36_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-36_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-36_contrast-black.png new file mode 100644 index 000000000000..272eac61f47d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-36_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..b5639901c417 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-40_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-40_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..b5639901c417 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-40_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-40_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-40_contrast-black.png new file mode 100644 index 000000000000..b5639901c417 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-40_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..0c7761f9f06d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-48_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-48_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..0c7761f9f06d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-48_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-48_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-48_contrast-black.png new file mode 100644 index 000000000000..0c7761f9f06d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-48_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..cadf1d40fc0c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-60_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-60_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..cadf1d40fc0c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-60_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-60_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-60_contrast-black.png new file mode 100644 index 000000000000..cadf1d40fc0c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-60_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..fd23de936e05 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-64_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-64_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..fd23de936e05 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-64_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-64_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-64_contrast-black.png new file mode 100644 index 000000000000..fd23de936e05 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-64_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..ffbaaf22aca1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-72_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-72_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..ffbaaf22aca1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-72_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-72_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-72_contrast-black.png new file mode 100644 index 000000000000..ffbaaf22aca1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-72_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..e92bdf508ba4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-80_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-80_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..e92bdf508ba4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-80_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-80_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-80_contrast-black.png new file mode 100644 index 000000000000..e92bdf508ba4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-80_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..0da5b25a7229 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-96_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-96_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..0da5b25a7229 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-96_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-96_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-96_contrast-black.png new file mode 100644 index 000000000000..0da5b25a7229 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Square44x44Logo.targetsize-96_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-100_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-100_contrast-black.png new file mode 100644 index 000000000000..a5046bfe71e9 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-100_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-125_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-125_contrast-black.png new file mode 100644 index 000000000000..ac714773f784 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-125_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-150_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-150_contrast-black.png new file mode 100644 index 000000000000..bb4ef9ce9e71 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-150_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-200_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-200_contrast-black.png new file mode 100644 index 000000000000..7c3e89c31344 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-200_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-400_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-400_contrast-black.png new file mode 100644 index 000000000000..2c8420ef2a4e Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/StoreLogo.scale-400_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-100_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-100_contrast-black.png new file mode 100644 index 000000000000..a947f66cf972 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-100_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-125_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-125_contrast-black.png new file mode 100644 index 000000000000..88e878b71298 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-125_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-150_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-150_contrast-black.png new file mode 100644 index 000000000000..b50f680d2860 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-150_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-200_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-200_contrast-black.png new file mode 100644 index 000000000000..fc56c94b0650 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-200_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-400_contrast-black.png b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-400_contrast-black.png new file mode 100644 index 000000000000..e7596242f0c4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-black/Wide310x150Logo.scale-400_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-100_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-100_contrast-white.png new file mode 100644 index 000000000000..0f45c05c17c6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-100_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-125_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-125_contrast-white.png new file mode 100644 index 000000000000..9281b8bcdb1f Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-125_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-150_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-150_contrast-white.png new file mode 100644 index 000000000000..4bf079789a50 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-150_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-200_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-200_contrast-white.png new file mode 100644 index 000000000000..a902298666ed Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-200_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-400_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-400_contrast-white.png new file mode 100644 index 000000000000..9a6e5184b2f3 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/BadgeLogo.scale-400_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-100_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-100_contrast-white.png new file mode 100644 index 000000000000..36d7d0aab514 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-100_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-125_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-125_contrast-white.png new file mode 100644 index 000000000000..86dabc9b29c7 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-125_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-150_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-150_contrast-white.png new file mode 100644 index 000000000000..442c06009bb7 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-150_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-200_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-200_contrast-white.png new file mode 100644 index 000000000000..9ae7f647bf6a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-200_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-400_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-400_contrast-white.png new file mode 100644 index 000000000000..4aa2e8ad7bc3 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Large310x310Logo.scale-400_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-100_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-100_contrast-white.png new file mode 100644 index 000000000000..8dead135538f Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-100_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-125_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-125_contrast-white.png new file mode 100644 index 000000000000..692fc38de02e Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-125_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-150_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-150_contrast-white.png new file mode 100644 index 000000000000..8f920ed685c8 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-150_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-200_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-200_contrast-white.png new file mode 100644 index 000000000000..f2b30c482b17 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-200_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-400_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-400_contrast-white.png new file mode 100644 index 000000000000..0915eacd859b Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Small71x71Logo.scale-400_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-100_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-100_contrast-white.png new file mode 100644 index 000000000000..24cbf599fe92 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-100_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-125_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-125_contrast-white.png new file mode 100644 index 000000000000..ac5ab7b98a66 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-125_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-150_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-150_contrast-white.png new file mode 100644 index 000000000000..a8c2127c4fd3 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-150_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-200_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-200_contrast-white.png new file mode 100644 index 000000000000..db7de583a768 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-200_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-400_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-400_contrast-white.png new file mode 100644 index 000000000000..e4b1173e644b Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/SplashScreen.scale-400_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-100_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-100_contrast-white.png new file mode 100644 index 000000000000..b3d2450434aa Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-100_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-125_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-125_contrast-white.png new file mode 100644 index 000000000000..a6008a4174f4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-125_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-150_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-150_contrast-white.png new file mode 100644 index 000000000000..3246749c9b9b Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-150_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-200_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-200_contrast-white.png new file mode 100644 index 000000000000..1a45ec4b165b Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-200_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-400_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-400_contrast-white.png new file mode 100644 index 000000000000..e4acb4d164e3 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square150x150Logo.scale-400_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-100_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-100_contrast-white.png new file mode 100644 index 000000000000..75f09d4ea9dd Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-100_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-125_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-125_contrast-white.png new file mode 100644 index 000000000000..8d737adc2b90 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-125_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-150_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-150_contrast-white.png new file mode 100644 index 000000000000..3dce40cf8b89 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-150_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-200_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-200_contrast-white.png new file mode 100644 index 000000000000..3a6a5a205a95 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-200_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-400_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-400_contrast-white.png new file mode 100644 index 000000000000..2ece25f9a2a0 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.scale-400_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..2859902e0152 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-16_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-16_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..2859902e0152 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-16_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-16_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-16_contrast-white.png new file mode 100644 index 000000000000..2859902e0152 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-16_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..baa6a5f959e6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-20_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-20_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..baa6a5f959e6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-20_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-20_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-20_contrast-white.png new file mode 100644 index 000000000000..baa6a5f959e6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-20_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..0f45c05c17c6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-24_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-24_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..0f45c05c17c6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-24_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-24_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-24_contrast-white.png new file mode 100644 index 000000000000..0f45c05c17c6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-24_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..5249bb6d2c42 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-256_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-256_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..5249bb6d2c42 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-256_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-256_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-256_contrast-white.png new file mode 100644 index 000000000000..5249bb6d2c42 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-256_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..8213d8129a2a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-30_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-30_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..8213d8129a2a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-30_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-30_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-30_contrast-white.png new file mode 100644 index 000000000000..8213d8129a2a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-30_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..22b3d22828b8 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-32_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-32_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..22b3d22828b8 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-32_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-32_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-32_contrast-white.png new file mode 100644 index 000000000000..22b3d22828b8 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-32_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..67b694ac9b35 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-36_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-36_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..67b694ac9b35 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-36_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-36_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-36_contrast-white.png new file mode 100644 index 000000000000..67b694ac9b35 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-36_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..f54acadee418 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-40_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-40_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..f54acadee418 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-40_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-40_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-40_contrast-white.png new file mode 100644 index 000000000000..f54acadee418 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-40_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..670aa904fce4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-48_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-48_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..670aa904fce4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-48_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-48_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-48_contrast-white.png new file mode 100644 index 000000000000..670aa904fce4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-48_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..4d3dfcf49d5c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-60_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-60_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..4d3dfcf49d5c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-60_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-60_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-60_contrast-white.png new file mode 100644 index 000000000000..4d3dfcf49d5c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-60_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..7669c9559bc9 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-64_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-64_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..7669c9559bc9 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-64_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-64_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-64_contrast-white.png new file mode 100644 index 000000000000..7669c9559bc9 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-64_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..4d56c856558a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-72_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-72_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..4d56c856558a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-72_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-72_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-72_contrast-white.png new file mode 100644 index 000000000000..4d56c856558a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-72_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..20546d38e1a4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-80_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-80_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..20546d38e1a4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-80_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-80_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-80_contrast-white.png new file mode 100644 index 000000000000..20546d38e1a4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-80_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..7f188522f6cf Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-96_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-96_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..7f188522f6cf Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-96_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-96_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-96_contrast-white.png new file mode 100644 index 000000000000..7f188522f6cf Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Square44x44Logo.targetsize-96_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-100_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-100_contrast-white.png new file mode 100644 index 000000000000..4c77cddf5219 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-100_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-125_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-125_contrast-white.png new file mode 100644 index 000000000000..4220bce64a9a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-125_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-150_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-150_contrast-white.png new file mode 100644 index 000000000000..475d3c25ae25 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-150_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-200_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-200_contrast-white.png new file mode 100644 index 000000000000..6a080facf779 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-200_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-400_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-400_contrast-white.png new file mode 100644 index 000000000000..768b28fb7658 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/StoreLogo.scale-400_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-100_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-100_contrast-white.png new file mode 100644 index 000000000000..1cf33f8cbbc0 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-100_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-125_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-125_contrast-white.png new file mode 100644 index 000000000000..3e0123187fb8 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-125_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-150_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-150_contrast-white.png new file mode 100644 index 000000000000..c9313f1d6310 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-150_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-200_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-200_contrast-white.png new file mode 100644 index 000000000000..30b1102c0f11 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-200_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-400_contrast-white.png b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-400_contrast-white.png new file mode 100644 index 000000000000..1de222267920 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Dev/contrast-white/Wide310x150Logo.scale-400_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/BadgeLogo.scale-100.png b/src/Files.App/Assets/AppTiles/Preview/BadgeLogo.scale-100.png new file mode 100644 index 000000000000..bda7a6874f79 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/BadgeLogo.scale-100.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/BadgeLogo.scale-125.png b/src/Files.App/Assets/AppTiles/Preview/BadgeLogo.scale-125.png new file mode 100644 index 000000000000..41b2b38e06d4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/BadgeLogo.scale-125.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/BadgeLogo.scale-150.png b/src/Files.App/Assets/AppTiles/Preview/BadgeLogo.scale-150.png new file mode 100644 index 000000000000..47ff073edf18 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/BadgeLogo.scale-150.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/BadgeLogo.scale-200.png b/src/Files.App/Assets/AppTiles/Preview/BadgeLogo.scale-200.png new file mode 100644 index 000000000000..3c441f2c11e8 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/BadgeLogo.scale-200.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/BadgeLogo.scale-400.png b/src/Files.App/Assets/AppTiles/Preview/BadgeLogo.scale-400.png new file mode 100644 index 000000000000..7cfbee56c45f Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/BadgeLogo.scale-400.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Large310x310Logo.scale-100.png b/src/Files.App/Assets/AppTiles/Preview/Large310x310Logo.scale-100.png new file mode 100644 index 000000000000..9b1f7c8121e7 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Large310x310Logo.scale-100.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Large310x310Logo.scale-125.png b/src/Files.App/Assets/AppTiles/Preview/Large310x310Logo.scale-125.png new file mode 100644 index 000000000000..f9ef55ad94c1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Large310x310Logo.scale-125.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Large310x310Logo.scale-150.png b/src/Files.App/Assets/AppTiles/Preview/Large310x310Logo.scale-150.png new file mode 100644 index 000000000000..6ec6ccb6c444 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Large310x310Logo.scale-150.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Large310x310Logo.scale-200.png b/src/Files.App/Assets/AppTiles/Preview/Large310x310Logo.scale-200.png new file mode 100644 index 000000000000..ed45b78af321 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Large310x310Logo.scale-200.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Large310x310Logo.scale-400.png b/src/Files.App/Assets/AppTiles/Preview/Large310x310Logo.scale-400.png new file mode 100644 index 000000000000..cc9b50755a51 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Large310x310Logo.scale-400.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Logo.ico b/src/Files.App/Assets/AppTiles/Preview/Logo.ico new file mode 100644 index 000000000000..66120ee28879 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Logo.ico differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Small71x71Logo.scale-100.png b/src/Files.App/Assets/AppTiles/Preview/Small71x71Logo.scale-100.png new file mode 100644 index 000000000000..b9434f995a64 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Small71x71Logo.scale-100.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Small71x71Logo.scale-125.png b/src/Files.App/Assets/AppTiles/Preview/Small71x71Logo.scale-125.png new file mode 100644 index 000000000000..5a12406f66f8 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Small71x71Logo.scale-125.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Small71x71Logo.scale-150.png b/src/Files.App/Assets/AppTiles/Preview/Small71x71Logo.scale-150.png new file mode 100644 index 000000000000..96ea9502d518 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Small71x71Logo.scale-150.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Small71x71Logo.scale-200.png b/src/Files.App/Assets/AppTiles/Preview/Small71x71Logo.scale-200.png new file mode 100644 index 000000000000..230be5b3f167 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Small71x71Logo.scale-200.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Small71x71Logo.scale-400.png b/src/Files.App/Assets/AppTiles/Preview/Small71x71Logo.scale-400.png new file mode 100644 index 000000000000..6f4cc6b1025c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Small71x71Logo.scale-400.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/SplashScreen.scale-100.png b/src/Files.App/Assets/AppTiles/Preview/SplashScreen.scale-100.png new file mode 100644 index 000000000000..7c6296d1b8a2 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/SplashScreen.scale-100.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/SplashScreen.scale-125.png b/src/Files.App/Assets/AppTiles/Preview/SplashScreen.scale-125.png new file mode 100644 index 000000000000..2e90b78d7c58 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/SplashScreen.scale-125.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/SplashScreen.scale-150.png b/src/Files.App/Assets/AppTiles/Preview/SplashScreen.scale-150.png new file mode 100644 index 000000000000..8b85cdaf63f8 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/SplashScreen.scale-150.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/SplashScreen.scale-200.png b/src/Files.App/Assets/AppTiles/Preview/SplashScreen.scale-200.png new file mode 100644 index 000000000000..704d62bc1f6a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/SplashScreen.scale-200.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/SplashScreen.scale-400.png b/src/Files.App/Assets/AppTiles/Preview/SplashScreen.scale-400.png new file mode 100644 index 000000000000..88a837085a62 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/SplashScreen.scale-400.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square150x150Logo.scale-100.png b/src/Files.App/Assets/AppTiles/Preview/Square150x150Logo.scale-100.png new file mode 100644 index 000000000000..3ec339ea3ec1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square150x150Logo.scale-100.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square150x150Logo.scale-125.png b/src/Files.App/Assets/AppTiles/Preview/Square150x150Logo.scale-125.png new file mode 100644 index 000000000000..fab5faa4e1a9 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square150x150Logo.scale-125.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square150x150Logo.scale-150.png b/src/Files.App/Assets/AppTiles/Preview/Square150x150Logo.scale-150.png new file mode 100644 index 000000000000..44bd8a7dbd19 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square150x150Logo.scale-150.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square150x150Logo.scale-200.png b/src/Files.App/Assets/AppTiles/Preview/Square150x150Logo.scale-200.png new file mode 100644 index 000000000000..78feca2f7adc Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square150x150Logo.scale-200.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square150x150Logo.scale-400.png b/src/Files.App/Assets/AppTiles/Preview/Square150x150Logo.scale-400.png new file mode 100644 index 000000000000..4b2efaee2c1d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square150x150Logo.scale-400.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.scale-100.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.scale-100.png new file mode 100644 index 000000000000..4062fef9afbe Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.scale-100.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.scale-125.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.scale-125.png new file mode 100644 index 000000000000..64f0d0cb8f42 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.scale-125.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.scale-150.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.scale-150.png new file mode 100644 index 000000000000..430ea50d295b Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.scale-150.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.scale-200.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.scale-200.png new file mode 100644 index 000000000000..d74d8a29a91c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.scale-200.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.scale-400.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.scale-400.png new file mode 100644 index 000000000000..8cea9beabe1e Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.scale-400.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-16.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-16.png new file mode 100644 index 000000000000..811da535a167 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-16.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-16_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-16_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..811da535a167 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-16_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-16_altform-unplated.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-16_altform-unplated.png new file mode 100644 index 000000000000..811da535a167 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-16_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-20.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-20.png new file mode 100644 index 000000000000..433a4db51b94 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-20.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-20_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-20_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..433a4db51b94 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-20_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-20_altform-unplated.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-20_altform-unplated.png new file mode 100644 index 000000000000..433a4db51b94 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-20_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-24.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-24.png new file mode 100644 index 000000000000..d9731d121bc5 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-24.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-24_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-24_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..d9731d121bc5 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-24_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-24_altform-unplated.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-24_altform-unplated.png new file mode 100644 index 000000000000..d9731d121bc5 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-24_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-256.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-256.png new file mode 100644 index 000000000000..18148720233c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-256.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-256_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-256_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..18148720233c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-256_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-256_altform-unplated.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-256_altform-unplated.png new file mode 100644 index 000000000000..18148720233c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-256_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-30.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-30.png new file mode 100644 index 000000000000..ced5e5993e3d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-30.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-30_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-30_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..ced5e5993e3d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-30_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-30_altform-unplated.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-30_altform-unplated.png new file mode 100644 index 000000000000..ced5e5993e3d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-30_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-32.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-32.png new file mode 100644 index 000000000000..69d86cb08699 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-32.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-32_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-32_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..69d86cb08699 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-32_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-32_altform-unplated.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-32_altform-unplated.png new file mode 100644 index 000000000000..69d86cb08699 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-32_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-36.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-36.png new file mode 100644 index 000000000000..9b8ea3f742e4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-36.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-36_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-36_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..9b8ea3f742e4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-36_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-36_altform-unplated.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-36_altform-unplated.png new file mode 100644 index 000000000000..9b8ea3f742e4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-36_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-40.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-40.png new file mode 100644 index 000000000000..49471ceeb7a3 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-40.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-40_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-40_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..49471ceeb7a3 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-40_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-40_altform-unplated.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-40_altform-unplated.png new file mode 100644 index 000000000000..49471ceeb7a3 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-40_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-48.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-48.png new file mode 100644 index 000000000000..f508b8564ba6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-48.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-48_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-48_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..f508b8564ba6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-48_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-48_altform-unplated.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-48_altform-unplated.png new file mode 100644 index 000000000000..f508b8564ba6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-48_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-60.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-60.png new file mode 100644 index 000000000000..91cb4b8391b3 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-60.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-60_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-60_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..91cb4b8391b3 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-60_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-60_altform-unplated.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-60_altform-unplated.png new file mode 100644 index 000000000000..91cb4b8391b3 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-60_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-64.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-64.png new file mode 100644 index 000000000000..e497a04fec5e Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-64.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-64_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-64_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..e497a04fec5e Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-64_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-64_altform-unplated.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-64_altform-unplated.png new file mode 100644 index 000000000000..e497a04fec5e Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-64_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-72.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-72.png new file mode 100644 index 000000000000..ec0256916c55 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-72.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-72_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-72_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..ec0256916c55 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-72_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-72_altform-unplated.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-72_altform-unplated.png new file mode 100644 index 000000000000..ec0256916c55 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-72_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-80.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-80.png new file mode 100644 index 000000000000..ac83aefb9521 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-80.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-80_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-80_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..ac83aefb9521 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-80_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-80_altform-unplated.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-80_altform-unplated.png new file mode 100644 index 000000000000..ac83aefb9521 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-80_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-96.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-96.png new file mode 100644 index 000000000000..479318659074 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-96.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-96_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-96_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..479318659074 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-96_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-96_altform-unplated.png b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-96_altform-unplated.png new file mode 100644 index 000000000000..479318659074 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Square44x44Logo.targetsize-96_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/StoreLogo.scale-100.png b/src/Files.App/Assets/AppTiles/Preview/StoreLogo.scale-100.png new file mode 100644 index 000000000000..e396280e264a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/StoreLogo.scale-100.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/StoreLogo.scale-125.png b/src/Files.App/Assets/AppTiles/Preview/StoreLogo.scale-125.png new file mode 100644 index 000000000000..d8fb0b78baa5 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/StoreLogo.scale-125.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/StoreLogo.scale-150.png b/src/Files.App/Assets/AppTiles/Preview/StoreLogo.scale-150.png new file mode 100644 index 000000000000..9f9e366a35f8 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/StoreLogo.scale-150.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/StoreLogo.scale-200.png b/src/Files.App/Assets/AppTiles/Preview/StoreLogo.scale-200.png new file mode 100644 index 000000000000..114a1985eab1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/StoreLogo.scale-200.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/StoreLogo.scale-400.png b/src/Files.App/Assets/AppTiles/Preview/StoreLogo.scale-400.png new file mode 100644 index 000000000000..a811f91cc2c5 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/StoreLogo.scale-400.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Wide310x150Logo.scale-100.png b/src/Files.App/Assets/AppTiles/Preview/Wide310x150Logo.scale-100.png new file mode 100644 index 000000000000..5e63a1d7b457 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Wide310x150Logo.scale-100.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Wide310x150Logo.scale-125.png b/src/Files.App/Assets/AppTiles/Preview/Wide310x150Logo.scale-125.png new file mode 100644 index 000000000000..04332b033dea Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Wide310x150Logo.scale-125.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Wide310x150Logo.scale-150.png b/src/Files.App/Assets/AppTiles/Preview/Wide310x150Logo.scale-150.png new file mode 100644 index 000000000000..88b16d09017e Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Wide310x150Logo.scale-150.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Wide310x150Logo.scale-200.png b/src/Files.App/Assets/AppTiles/Preview/Wide310x150Logo.scale-200.png new file mode 100644 index 000000000000..21641a0e8371 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Wide310x150Logo.scale-200.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/Wide310x150Logo.scale-400.png b/src/Files.App/Assets/AppTiles/Preview/Wide310x150Logo.scale-400.png new file mode 100644 index 000000000000..b7382c6f01d0 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/Wide310x150Logo.scale-400.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-100_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-100_contrast-black.png new file mode 100644 index 000000000000..bda7a6874f79 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-100_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-125_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-125_contrast-black.png new file mode 100644 index 000000000000..41b2b38e06d4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-125_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-150_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-150_contrast-black.png new file mode 100644 index 000000000000..47ff073edf18 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-150_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-200_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-200_contrast-black.png new file mode 100644 index 000000000000..3c441f2c11e8 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-200_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-400_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-400_contrast-black.png new file mode 100644 index 000000000000..7cfbee56c45f Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/BadgeLogo.scale-400_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-100_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-100_contrast-black.png new file mode 100644 index 000000000000..d35527b1c783 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-100_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-125_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-125_contrast-black.png new file mode 100644 index 000000000000..24f3b97e230d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-125_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-150_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-150_contrast-black.png new file mode 100644 index 000000000000..39ad1fef5a1d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-150_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-200_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-200_contrast-black.png new file mode 100644 index 000000000000..0cdbdd73195e Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-200_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-400_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-400_contrast-black.png new file mode 100644 index 000000000000..dc15d44caf23 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Large310x310Logo.scale-400_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-100_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-100_contrast-black.png new file mode 100644 index 000000000000..406ebe3eadbf Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-100_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-125_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-125_contrast-black.png new file mode 100644 index 000000000000..2ed83a68dd3a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-125_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-150_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-150_contrast-black.png new file mode 100644 index 000000000000..ef115293fdac Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-150_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-200_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-200_contrast-black.png new file mode 100644 index 000000000000..f9080d578545 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-200_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-400_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-400_contrast-black.png new file mode 100644 index 000000000000..e73c070f148c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Small71x71Logo.scale-400_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-100_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-100_contrast-black.png new file mode 100644 index 000000000000..e62085049275 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-100_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-125_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-125_contrast-black.png new file mode 100644 index 000000000000..1b67dd42baf3 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-125_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-150_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-150_contrast-black.png new file mode 100644 index 000000000000..b0a4b2a01821 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-150_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-200_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-200_contrast-black.png new file mode 100644 index 000000000000..78f35751ff93 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-200_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-400_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-400_contrast-black.png new file mode 100644 index 000000000000..0cb1ed63039c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/SplashScreen.scale-400_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-100_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-100_contrast-black.png new file mode 100644 index 000000000000..fb98b59ecfcd Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-100_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-125_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-125_contrast-black.png new file mode 100644 index 000000000000..6f013f45dacc Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-125_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-150_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-150_contrast-black.png new file mode 100644 index 000000000000..f8eda1303e82 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-150_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-200_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-200_contrast-black.png new file mode 100644 index 000000000000..abedac07f4ee Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-200_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-400_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-400_contrast-black.png new file mode 100644 index 000000000000..59aa8844a57b Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square150x150Logo.scale-400_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-100_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-100_contrast-black.png new file mode 100644 index 000000000000..ae842ab9fb92 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-100_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-125_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-125_contrast-black.png new file mode 100644 index 000000000000..d02a4c8eb68c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-125_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-150_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-150_contrast-black.png new file mode 100644 index 000000000000..a36e609502e6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-150_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-200_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-200_contrast-black.png new file mode 100644 index 000000000000..0727fa2193c5 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-200_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-400_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-400_contrast-black.png new file mode 100644 index 000000000000..6d28a39609c3 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.scale-400_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..24469e643d42 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-16_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-16_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..24469e643d42 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-16_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-16_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-16_contrast-black.png new file mode 100644 index 000000000000..24469e643d42 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-16_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..7145f452aba5 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-20_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-20_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..7145f452aba5 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-20_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-20_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-20_contrast-black.png new file mode 100644 index 000000000000..7145f452aba5 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-20_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..bda7a6874f79 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-24_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-24_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..bda7a6874f79 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-24_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-24_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-24_contrast-black.png new file mode 100644 index 000000000000..bda7a6874f79 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-24_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..54dfbe8167dd Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-256_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-256_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..54dfbe8167dd Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-256_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-256_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-256_contrast-black.png new file mode 100644 index 000000000000..54dfbe8167dd Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-256_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..75333e356bad Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-30_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-30_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..75333e356bad Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-30_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-30_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-30_contrast-black.png new file mode 100644 index 000000000000..75333e356bad Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-30_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..e345f65be678 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-32_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-32_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..e345f65be678 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-32_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-32_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-32_contrast-black.png new file mode 100644 index 000000000000..e345f65be678 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-32_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..272eac61f47d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-36_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-36_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..272eac61f47d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-36_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-36_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-36_contrast-black.png new file mode 100644 index 000000000000..272eac61f47d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-36_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..b5639901c417 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-40_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-40_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..b5639901c417 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-40_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-40_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-40_contrast-black.png new file mode 100644 index 000000000000..b5639901c417 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-40_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..0c7761f9f06d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-48_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-48_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..0c7761f9f06d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-48_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-48_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-48_contrast-black.png new file mode 100644 index 000000000000..0c7761f9f06d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-48_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..cadf1d40fc0c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-60_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-60_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..cadf1d40fc0c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-60_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-60_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-60_contrast-black.png new file mode 100644 index 000000000000..cadf1d40fc0c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-60_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..fd23de936e05 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-64_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-64_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..fd23de936e05 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-64_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-64_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-64_contrast-black.png new file mode 100644 index 000000000000..fd23de936e05 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-64_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..ffbaaf22aca1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-72_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-72_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..ffbaaf22aca1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-72_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-72_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-72_contrast-black.png new file mode 100644 index 000000000000..ffbaaf22aca1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-72_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..e92bdf508ba4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-80_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-80_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..e92bdf508ba4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-80_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-80_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-80_contrast-black.png new file mode 100644 index 000000000000..e92bdf508ba4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-80_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..0da5b25a7229 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-96_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-96_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..0da5b25a7229 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-96_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-96_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-96_contrast-black.png new file mode 100644 index 000000000000..0da5b25a7229 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Square44x44Logo.targetsize-96_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-100_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-100_contrast-black.png new file mode 100644 index 000000000000..a5046bfe71e9 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-100_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-125_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-125_contrast-black.png new file mode 100644 index 000000000000..ac714773f784 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-125_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-150_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-150_contrast-black.png new file mode 100644 index 000000000000..bb4ef9ce9e71 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-150_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-200_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-200_contrast-black.png new file mode 100644 index 000000000000..7c3e89c31344 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-200_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-400_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-400_contrast-black.png new file mode 100644 index 000000000000..2c8420ef2a4e Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/StoreLogo.scale-400_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-100_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-100_contrast-black.png new file mode 100644 index 000000000000..a947f66cf972 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-100_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-125_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-125_contrast-black.png new file mode 100644 index 000000000000..88e878b71298 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-125_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-150_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-150_contrast-black.png new file mode 100644 index 000000000000..b50f680d2860 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-150_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-200_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-200_contrast-black.png new file mode 100644 index 000000000000..fc56c94b0650 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-200_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-400_contrast-black.png b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-400_contrast-black.png new file mode 100644 index 000000000000..e7596242f0c4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-black/Wide310x150Logo.scale-400_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-100_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-100_contrast-white.png new file mode 100644 index 000000000000..0f45c05c17c6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-100_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-125_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-125_contrast-white.png new file mode 100644 index 000000000000..9281b8bcdb1f Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-125_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-150_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-150_contrast-white.png new file mode 100644 index 000000000000..4bf079789a50 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-150_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-200_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-200_contrast-white.png new file mode 100644 index 000000000000..a902298666ed Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-200_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-400_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-400_contrast-white.png new file mode 100644 index 000000000000..9a6e5184b2f3 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/BadgeLogo.scale-400_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-100_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-100_contrast-white.png new file mode 100644 index 000000000000..36d7d0aab514 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-100_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-125_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-125_contrast-white.png new file mode 100644 index 000000000000..86dabc9b29c7 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-125_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-150_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-150_contrast-white.png new file mode 100644 index 000000000000..442c06009bb7 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-150_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-200_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-200_contrast-white.png new file mode 100644 index 000000000000..9ae7f647bf6a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-200_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-400_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-400_contrast-white.png new file mode 100644 index 000000000000..4aa2e8ad7bc3 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Large310x310Logo.scale-400_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-100_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-100_contrast-white.png new file mode 100644 index 000000000000..8dead135538f Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-100_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-125_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-125_contrast-white.png new file mode 100644 index 000000000000..692fc38de02e Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-125_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-150_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-150_contrast-white.png new file mode 100644 index 000000000000..8f920ed685c8 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-150_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-200_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-200_contrast-white.png new file mode 100644 index 000000000000..f2b30c482b17 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-200_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-400_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-400_contrast-white.png new file mode 100644 index 000000000000..0915eacd859b Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Small71x71Logo.scale-400_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-100_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-100_contrast-white.png new file mode 100644 index 000000000000..24cbf599fe92 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-100_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-125_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-125_contrast-white.png new file mode 100644 index 000000000000..ac5ab7b98a66 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-125_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-150_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-150_contrast-white.png new file mode 100644 index 000000000000..a8c2127c4fd3 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-150_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-200_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-200_contrast-white.png new file mode 100644 index 000000000000..db7de583a768 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-200_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-400_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-400_contrast-white.png new file mode 100644 index 000000000000..e4b1173e644b Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/SplashScreen.scale-400_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-100_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-100_contrast-white.png new file mode 100644 index 000000000000..b3d2450434aa Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-100_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-125_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-125_contrast-white.png new file mode 100644 index 000000000000..a6008a4174f4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-125_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-150_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-150_contrast-white.png new file mode 100644 index 000000000000..3246749c9b9b Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-150_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-200_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-200_contrast-white.png new file mode 100644 index 000000000000..1a45ec4b165b Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-200_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-400_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-400_contrast-white.png new file mode 100644 index 000000000000..e4acb4d164e3 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square150x150Logo.scale-400_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-100_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-100_contrast-white.png new file mode 100644 index 000000000000..75f09d4ea9dd Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-100_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-125_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-125_contrast-white.png new file mode 100644 index 000000000000..8d737adc2b90 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-125_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-150_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-150_contrast-white.png new file mode 100644 index 000000000000..3dce40cf8b89 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-150_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-200_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-200_contrast-white.png new file mode 100644 index 000000000000..3a6a5a205a95 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-200_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-400_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-400_contrast-white.png new file mode 100644 index 000000000000..2ece25f9a2a0 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.scale-400_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..2859902e0152 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-16_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-16_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..2859902e0152 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-16_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-16_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-16_contrast-white.png new file mode 100644 index 000000000000..2859902e0152 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-16_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..baa6a5f959e6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-20_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-20_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..baa6a5f959e6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-20_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-20_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-20_contrast-white.png new file mode 100644 index 000000000000..baa6a5f959e6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-20_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..0f45c05c17c6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-24_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-24_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..0f45c05c17c6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-24_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-24_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-24_contrast-white.png new file mode 100644 index 000000000000..0f45c05c17c6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-24_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..5249bb6d2c42 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-256_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-256_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..5249bb6d2c42 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-256_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-256_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-256_contrast-white.png new file mode 100644 index 000000000000..5249bb6d2c42 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-256_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..8213d8129a2a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-30_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-30_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..8213d8129a2a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-30_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-30_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-30_contrast-white.png new file mode 100644 index 000000000000..8213d8129a2a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-30_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..22b3d22828b8 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-32_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-32_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..22b3d22828b8 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-32_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-32_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-32_contrast-white.png new file mode 100644 index 000000000000..22b3d22828b8 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-32_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..67b694ac9b35 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-36_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-36_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..67b694ac9b35 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-36_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-36_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-36_contrast-white.png new file mode 100644 index 000000000000..67b694ac9b35 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-36_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..f54acadee418 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-40_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-40_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..f54acadee418 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-40_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-40_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-40_contrast-white.png new file mode 100644 index 000000000000..f54acadee418 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-40_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..670aa904fce4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-48_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-48_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..670aa904fce4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-48_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-48_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-48_contrast-white.png new file mode 100644 index 000000000000..670aa904fce4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-48_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..4d3dfcf49d5c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-60_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-60_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..4d3dfcf49d5c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-60_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-60_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-60_contrast-white.png new file mode 100644 index 000000000000..4d3dfcf49d5c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-60_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..7669c9559bc9 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-64_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-64_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..7669c9559bc9 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-64_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-64_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-64_contrast-white.png new file mode 100644 index 000000000000..7669c9559bc9 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-64_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..4d56c856558a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-72_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-72_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..4d56c856558a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-72_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-72_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-72_contrast-white.png new file mode 100644 index 000000000000..4d56c856558a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-72_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..20546d38e1a4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-80_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-80_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..20546d38e1a4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-80_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-80_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-80_contrast-white.png new file mode 100644 index 000000000000..20546d38e1a4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-80_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..7f188522f6cf Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-96_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-96_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..7f188522f6cf Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-96_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-96_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-96_contrast-white.png new file mode 100644 index 000000000000..7f188522f6cf Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Square44x44Logo.targetsize-96_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-100_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-100_contrast-white.png new file mode 100644 index 000000000000..4c77cddf5219 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-100_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-125_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-125_contrast-white.png new file mode 100644 index 000000000000..4220bce64a9a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-125_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-150_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-150_contrast-white.png new file mode 100644 index 000000000000..475d3c25ae25 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-150_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-200_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-200_contrast-white.png new file mode 100644 index 000000000000..6a080facf779 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-200_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-400_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-400_contrast-white.png new file mode 100644 index 000000000000..768b28fb7658 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/StoreLogo.scale-400_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-100_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-100_contrast-white.png new file mode 100644 index 000000000000..1cf33f8cbbc0 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-100_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-125_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-125_contrast-white.png new file mode 100644 index 000000000000..3e0123187fb8 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-125_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-150_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-150_contrast-white.png new file mode 100644 index 000000000000..c9313f1d6310 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-150_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-200_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-200_contrast-white.png new file mode 100644 index 000000000000..30b1102c0f11 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-200_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-400_contrast-white.png b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-400_contrast-white.png new file mode 100644 index 000000000000..1de222267920 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Preview/contrast-white/Wide310x150Logo.scale-400_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/BadgeLogo.scale-100.png b/src/Files.App/Assets/AppTiles/Release/BadgeLogo.scale-100.png new file mode 100644 index 000000000000..bda7a6874f79 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/BadgeLogo.scale-100.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/BadgeLogo.scale-125.png b/src/Files.App/Assets/AppTiles/Release/BadgeLogo.scale-125.png new file mode 100644 index 000000000000..41b2b38e06d4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/BadgeLogo.scale-125.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/BadgeLogo.scale-150.png b/src/Files.App/Assets/AppTiles/Release/BadgeLogo.scale-150.png new file mode 100644 index 000000000000..47ff073edf18 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/BadgeLogo.scale-150.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/BadgeLogo.scale-200.png b/src/Files.App/Assets/AppTiles/Release/BadgeLogo.scale-200.png new file mode 100644 index 000000000000..3c441f2c11e8 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/BadgeLogo.scale-200.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/BadgeLogo.scale-400.png b/src/Files.App/Assets/AppTiles/Release/BadgeLogo.scale-400.png new file mode 100644 index 000000000000..7cfbee56c45f Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/BadgeLogo.scale-400.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Large310x310Logo.scale-100.png b/src/Files.App/Assets/AppTiles/Release/Large310x310Logo.scale-100.png new file mode 100644 index 000000000000..1d24d097acb5 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Large310x310Logo.scale-100.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Large310x310Logo.scale-125.png b/src/Files.App/Assets/AppTiles/Release/Large310x310Logo.scale-125.png new file mode 100644 index 000000000000..e922b63f971d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Large310x310Logo.scale-125.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Large310x310Logo.scale-150.png b/src/Files.App/Assets/AppTiles/Release/Large310x310Logo.scale-150.png new file mode 100644 index 000000000000..0fed62e4cf12 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Large310x310Logo.scale-150.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Large310x310Logo.scale-200.png b/src/Files.App/Assets/AppTiles/Release/Large310x310Logo.scale-200.png new file mode 100644 index 000000000000..7dfc67b49b82 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Large310x310Logo.scale-200.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Large310x310Logo.scale-400.png b/src/Files.App/Assets/AppTiles/Release/Large310x310Logo.scale-400.png new file mode 100644 index 000000000000..bf4ca7907bf7 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Large310x310Logo.scale-400.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Logo.ico b/src/Files.App/Assets/AppTiles/Release/Logo.ico new file mode 100644 index 000000000000..f908b13c300e Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Logo.ico differ diff --git a/src/Files.App/Assets/AppTiles/Release/Small71x71Logo.scale-100.png b/src/Files.App/Assets/AppTiles/Release/Small71x71Logo.scale-100.png new file mode 100644 index 000000000000..669b5fe8df34 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Small71x71Logo.scale-100.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Small71x71Logo.scale-125.png b/src/Files.App/Assets/AppTiles/Release/Small71x71Logo.scale-125.png new file mode 100644 index 000000000000..73a41173bab2 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Small71x71Logo.scale-125.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Small71x71Logo.scale-150.png b/src/Files.App/Assets/AppTiles/Release/Small71x71Logo.scale-150.png new file mode 100644 index 000000000000..931fb0442ba4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Small71x71Logo.scale-150.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Small71x71Logo.scale-200.png b/src/Files.App/Assets/AppTiles/Release/Small71x71Logo.scale-200.png new file mode 100644 index 000000000000..d5921b53f1d7 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Small71x71Logo.scale-200.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Small71x71Logo.scale-400.png b/src/Files.App/Assets/AppTiles/Release/Small71x71Logo.scale-400.png new file mode 100644 index 000000000000..5bebd58ff5aa Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Small71x71Logo.scale-400.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/SplashScreen.scale-100.png b/src/Files.App/Assets/AppTiles/Release/SplashScreen.scale-100.png new file mode 100644 index 000000000000..6fc7bccd95f2 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/SplashScreen.scale-100.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/SplashScreen.scale-125.png b/src/Files.App/Assets/AppTiles/Release/SplashScreen.scale-125.png new file mode 100644 index 000000000000..38e832b981ed Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/SplashScreen.scale-125.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/SplashScreen.scale-150.png b/src/Files.App/Assets/AppTiles/Release/SplashScreen.scale-150.png new file mode 100644 index 000000000000..1c245dd336ca Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/SplashScreen.scale-150.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/SplashScreen.scale-200.png b/src/Files.App/Assets/AppTiles/Release/SplashScreen.scale-200.png new file mode 100644 index 000000000000..ae95c71e5350 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/SplashScreen.scale-200.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/SplashScreen.scale-400.png b/src/Files.App/Assets/AppTiles/Release/SplashScreen.scale-400.png new file mode 100644 index 000000000000..c907f100eadd Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/SplashScreen.scale-400.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square150x150Logo.scale-100.png b/src/Files.App/Assets/AppTiles/Release/Square150x150Logo.scale-100.png new file mode 100644 index 000000000000..53ec68f39809 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square150x150Logo.scale-100.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square150x150Logo.scale-125.png b/src/Files.App/Assets/AppTiles/Release/Square150x150Logo.scale-125.png new file mode 100644 index 000000000000..ba6bead60484 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square150x150Logo.scale-125.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square150x150Logo.scale-150.png b/src/Files.App/Assets/AppTiles/Release/Square150x150Logo.scale-150.png new file mode 100644 index 000000000000..c36d377d7431 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square150x150Logo.scale-150.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square150x150Logo.scale-200.png b/src/Files.App/Assets/AppTiles/Release/Square150x150Logo.scale-200.png new file mode 100644 index 000000000000..096ad75b0772 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square150x150Logo.scale-200.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square150x150Logo.scale-400.png b/src/Files.App/Assets/AppTiles/Release/Square150x150Logo.scale-400.png new file mode 100644 index 000000000000..80e704bec92b Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square150x150Logo.scale-400.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.scale-100.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.scale-100.png new file mode 100644 index 000000000000..11d0f6a608f1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.scale-100.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.scale-125.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.scale-125.png new file mode 100644 index 000000000000..678be6f21b08 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.scale-125.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.scale-150.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.scale-150.png new file mode 100644 index 000000000000..cfd3790f4bfc Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.scale-150.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.scale-200.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.scale-200.png new file mode 100644 index 000000000000..f5227253c912 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.scale-200.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.scale-400.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.scale-400.png new file mode 100644 index 000000000000..84b986d778d8 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.scale-400.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-16.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-16.png new file mode 100644 index 000000000000..bcf50735b4f2 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-16.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-16_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-16_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..bcf50735b4f2 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-16_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-16_altform-unplated.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-16_altform-unplated.png new file mode 100644 index 000000000000..bcf50735b4f2 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-16_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-20.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-20.png new file mode 100644 index 000000000000..baa563451407 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-20.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-20_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-20_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..baa563451407 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-20_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-20_altform-unplated.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-20_altform-unplated.png new file mode 100644 index 000000000000..baa563451407 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-20_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-24.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-24.png new file mode 100644 index 000000000000..ec96d81116e6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-24.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-24_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-24_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..ec96d81116e6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-24_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-24_altform-unplated.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-24_altform-unplated.png new file mode 100644 index 000000000000..ec96d81116e6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-24_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-256.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-256.png new file mode 100644 index 000000000000..663d94605eb1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-256.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-256_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-256_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..663d94605eb1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-256_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-256_altform-unplated.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-256_altform-unplated.png new file mode 100644 index 000000000000..663d94605eb1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-256_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-30.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-30.png new file mode 100644 index 000000000000..b74104f3ea71 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-30.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-30_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-30_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..b74104f3ea71 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-30_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-30_altform-unplated.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-30_altform-unplated.png new file mode 100644 index 000000000000..b74104f3ea71 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-30_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-32.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-32.png new file mode 100644 index 000000000000..665cf5797a65 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-32.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-32_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-32_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..665cf5797a65 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-32_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-32_altform-unplated.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-32_altform-unplated.png new file mode 100644 index 000000000000..665cf5797a65 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-32_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-36.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-36.png new file mode 100644 index 000000000000..b659edfdc65b Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-36.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-36_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-36_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..b659edfdc65b Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-36_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-36_altform-unplated.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-36_altform-unplated.png new file mode 100644 index 000000000000..b659edfdc65b Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-36_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-40.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-40.png new file mode 100644 index 000000000000..133c3f75e349 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-40.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-40_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-40_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..133c3f75e349 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-40_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-40_altform-unplated.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-40_altform-unplated.png new file mode 100644 index 000000000000..133c3f75e349 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-40_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-48.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-48.png new file mode 100644 index 000000000000..22f375658f19 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-48.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-48_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-48_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..22f375658f19 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-48_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-48_altform-unplated.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-48_altform-unplated.png new file mode 100644 index 000000000000..22f375658f19 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-48_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-60.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-60.png new file mode 100644 index 000000000000..be25398f78ae Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-60.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-60_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-60_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..be25398f78ae Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-60_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-60_altform-unplated.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-60_altform-unplated.png new file mode 100644 index 000000000000..be25398f78ae Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-60_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-64.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-64.png new file mode 100644 index 000000000000..a183298a3f35 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-64.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-64_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-64_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..a183298a3f35 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-64_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-64_altform-unplated.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-64_altform-unplated.png new file mode 100644 index 000000000000..a183298a3f35 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-64_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-72.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-72.png new file mode 100644 index 000000000000..566fce9e80b6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-72.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-72_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-72_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..566fce9e80b6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-72_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-72_altform-unplated.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-72_altform-unplated.png new file mode 100644 index 000000000000..566fce9e80b6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-72_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-80.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-80.png new file mode 100644 index 000000000000..091604b046ae Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-80.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-80_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-80_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..091604b046ae Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-80_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-80_altform-unplated.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-80_altform-unplated.png new file mode 100644 index 000000000000..091604b046ae Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-80_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-96.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-96.png new file mode 100644 index 000000000000..753ee4350844 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-96.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-96_altform-lightunplated_theme-light.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-96_altform-lightunplated_theme-light.png new file mode 100644 index 000000000000..753ee4350844 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-96_altform-lightunplated_theme-light.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-96_altform-unplated.png b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-96_altform-unplated.png new file mode 100644 index 000000000000..753ee4350844 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Square44x44Logo.targetsize-96_altform-unplated.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/StoreLogo.scale-100.png b/src/Files.App/Assets/AppTiles/Release/StoreLogo.scale-100.png new file mode 100644 index 000000000000..1cbb2ab4ede1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/StoreLogo.scale-100.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/StoreLogo.scale-125.png b/src/Files.App/Assets/AppTiles/Release/StoreLogo.scale-125.png new file mode 100644 index 000000000000..7d70fbef3f1b Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/StoreLogo.scale-125.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/StoreLogo.scale-150.png b/src/Files.App/Assets/AppTiles/Release/StoreLogo.scale-150.png new file mode 100644 index 000000000000..f1f19654c40b Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/StoreLogo.scale-150.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/StoreLogo.scale-200.png b/src/Files.App/Assets/AppTiles/Release/StoreLogo.scale-200.png new file mode 100644 index 000000000000..fdc29fa60f06 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/StoreLogo.scale-200.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/StoreLogo.scale-400.png b/src/Files.App/Assets/AppTiles/Release/StoreLogo.scale-400.png new file mode 100644 index 000000000000..a26fa08c7c1a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/StoreLogo.scale-400.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Wide310x150Logo.scale-100.png b/src/Files.App/Assets/AppTiles/Release/Wide310x150Logo.scale-100.png new file mode 100644 index 000000000000..9bcbaacd205e Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Wide310x150Logo.scale-100.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Wide310x150Logo.scale-125.png b/src/Files.App/Assets/AppTiles/Release/Wide310x150Logo.scale-125.png new file mode 100644 index 000000000000..470bb05273ce Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Wide310x150Logo.scale-125.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Wide310x150Logo.scale-150.png b/src/Files.App/Assets/AppTiles/Release/Wide310x150Logo.scale-150.png new file mode 100644 index 000000000000..8b993e485000 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Wide310x150Logo.scale-150.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Wide310x150Logo.scale-200.png b/src/Files.App/Assets/AppTiles/Release/Wide310x150Logo.scale-200.png new file mode 100644 index 000000000000..65f718c6da8a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Wide310x150Logo.scale-200.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/Wide310x150Logo.scale-400.png b/src/Files.App/Assets/AppTiles/Release/Wide310x150Logo.scale-400.png new file mode 100644 index 000000000000..722985d2e013 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/Wide310x150Logo.scale-400.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-100_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-100_contrast-black.png new file mode 100644 index 000000000000..bda7a6874f79 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-100_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-125_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-125_contrast-black.png new file mode 100644 index 000000000000..41b2b38e06d4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-125_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-150_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-150_contrast-black.png new file mode 100644 index 000000000000..47ff073edf18 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-150_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-200_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-200_contrast-black.png new file mode 100644 index 000000000000..3c441f2c11e8 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-200_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-400_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-400_contrast-black.png new file mode 100644 index 000000000000..7cfbee56c45f Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/BadgeLogo.scale-400_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-100_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-100_contrast-black.png new file mode 100644 index 000000000000..d35527b1c783 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-100_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-125_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-125_contrast-black.png new file mode 100644 index 000000000000..24f3b97e230d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-125_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-150_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-150_contrast-black.png new file mode 100644 index 000000000000..39ad1fef5a1d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-150_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-200_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-200_contrast-black.png new file mode 100644 index 000000000000..0cdbdd73195e Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-200_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-400_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-400_contrast-black.png new file mode 100644 index 000000000000..dc15d44caf23 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Large310x310Logo.scale-400_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-100_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-100_contrast-black.png new file mode 100644 index 000000000000..406ebe3eadbf Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-100_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-125_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-125_contrast-black.png new file mode 100644 index 000000000000..2ed83a68dd3a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-125_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-150_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-150_contrast-black.png new file mode 100644 index 000000000000..ef115293fdac Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-150_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-200_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-200_contrast-black.png new file mode 100644 index 000000000000..f9080d578545 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-200_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-400_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-400_contrast-black.png new file mode 100644 index 000000000000..e73c070f148c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Small71x71Logo.scale-400_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-100_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-100_contrast-black.png new file mode 100644 index 000000000000..e62085049275 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-100_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-125_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-125_contrast-black.png new file mode 100644 index 000000000000..1b67dd42baf3 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-125_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-150_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-150_contrast-black.png new file mode 100644 index 000000000000..b0a4b2a01821 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-150_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-200_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-200_contrast-black.png new file mode 100644 index 000000000000..78f35751ff93 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-200_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-400_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-400_contrast-black.png new file mode 100644 index 000000000000..0cb1ed63039c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/SplashScreen.scale-400_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-100_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-100_contrast-black.png new file mode 100644 index 000000000000..fb98b59ecfcd Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-100_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-125_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-125_contrast-black.png new file mode 100644 index 000000000000..6f013f45dacc Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-125_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-150_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-150_contrast-black.png new file mode 100644 index 000000000000..f8eda1303e82 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-150_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-200_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-200_contrast-black.png new file mode 100644 index 000000000000..abedac07f4ee Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-200_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-400_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-400_contrast-black.png new file mode 100644 index 000000000000..59aa8844a57b Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square150x150Logo.scale-400_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-100_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-100_contrast-black.png new file mode 100644 index 000000000000..ae842ab9fb92 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-100_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-125_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-125_contrast-black.png new file mode 100644 index 000000000000..d02a4c8eb68c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-125_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-150_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-150_contrast-black.png new file mode 100644 index 000000000000..a36e609502e6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-150_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-200_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-200_contrast-black.png new file mode 100644 index 000000000000..0727fa2193c5 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-200_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-400_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-400_contrast-black.png new file mode 100644 index 000000000000..6d28a39609c3 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.scale-400_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..24469e643d42 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-16_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-16_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..24469e643d42 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-16_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-16_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-16_contrast-black.png new file mode 100644 index 000000000000..24469e643d42 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-16_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..7145f452aba5 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-20_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-20_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..7145f452aba5 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-20_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-20_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-20_contrast-black.png new file mode 100644 index 000000000000..7145f452aba5 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-20_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..bda7a6874f79 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-24_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-24_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..bda7a6874f79 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-24_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-24_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-24_contrast-black.png new file mode 100644 index 000000000000..bda7a6874f79 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-24_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..54dfbe8167dd Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-256_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-256_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..54dfbe8167dd Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-256_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-256_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-256_contrast-black.png new file mode 100644 index 000000000000..54dfbe8167dd Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-256_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..75333e356bad Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-30_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-30_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..75333e356bad Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-30_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-30_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-30_contrast-black.png new file mode 100644 index 000000000000..75333e356bad Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-30_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..e345f65be678 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-32_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-32_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..e345f65be678 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-32_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-32_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-32_contrast-black.png new file mode 100644 index 000000000000..e345f65be678 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-32_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..272eac61f47d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-36_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-36_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..272eac61f47d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-36_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-36_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-36_contrast-black.png new file mode 100644 index 000000000000..272eac61f47d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-36_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..b5639901c417 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-40_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-40_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..b5639901c417 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-40_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-40_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-40_contrast-black.png new file mode 100644 index 000000000000..b5639901c417 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-40_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..0c7761f9f06d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-48_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-48_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..0c7761f9f06d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-48_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-48_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-48_contrast-black.png new file mode 100644 index 000000000000..0c7761f9f06d Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-48_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..cadf1d40fc0c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-60_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-60_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..cadf1d40fc0c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-60_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-60_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-60_contrast-black.png new file mode 100644 index 000000000000..cadf1d40fc0c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-60_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..fd23de936e05 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-64_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-64_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..fd23de936e05 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-64_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-64_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-64_contrast-black.png new file mode 100644 index 000000000000..fd23de936e05 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-64_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..ffbaaf22aca1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-72_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-72_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..ffbaaf22aca1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-72_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-72_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-72_contrast-black.png new file mode 100644 index 000000000000..ffbaaf22aca1 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-72_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..e92bdf508ba4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-80_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-80_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..e92bdf508ba4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-80_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-80_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-80_contrast-black.png new file mode 100644 index 000000000000..e92bdf508ba4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-80_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-black.png new file mode 100644 index 000000000000..0da5b25a7229 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-96_altform-unplated_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-96_altform-unplated_contrast-black.png new file mode 100644 index 000000000000..0da5b25a7229 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-96_altform-unplated_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-96_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-96_contrast-black.png new file mode 100644 index 000000000000..0da5b25a7229 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Square44x44Logo.targetsize-96_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-100_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-100_contrast-black.png new file mode 100644 index 000000000000..a5046bfe71e9 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-100_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-125_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-125_contrast-black.png new file mode 100644 index 000000000000..ac714773f784 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-125_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-150_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-150_contrast-black.png new file mode 100644 index 000000000000..bb4ef9ce9e71 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-150_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-200_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-200_contrast-black.png new file mode 100644 index 000000000000..7c3e89c31344 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-200_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-400_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-400_contrast-black.png new file mode 100644 index 000000000000..2c8420ef2a4e Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/StoreLogo.scale-400_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-100_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-100_contrast-black.png new file mode 100644 index 000000000000..a947f66cf972 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-100_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-125_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-125_contrast-black.png new file mode 100644 index 000000000000..88e878b71298 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-125_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-150_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-150_contrast-black.png new file mode 100644 index 000000000000..b50f680d2860 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-150_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-200_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-200_contrast-black.png new file mode 100644 index 000000000000..fc56c94b0650 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-200_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-400_contrast-black.png b/src/Files.App/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-400_contrast-black.png new file mode 100644 index 000000000000..e7596242f0c4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-black/Wide310x150Logo.scale-400_contrast-black.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-100_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-100_contrast-white.png new file mode 100644 index 000000000000..0f45c05c17c6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-100_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-125_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-125_contrast-white.png new file mode 100644 index 000000000000..9281b8bcdb1f Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-125_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-150_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-150_contrast-white.png new file mode 100644 index 000000000000..4bf079789a50 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-150_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-200_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-200_contrast-white.png new file mode 100644 index 000000000000..a902298666ed Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-200_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-400_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-400_contrast-white.png new file mode 100644 index 000000000000..9a6e5184b2f3 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/BadgeLogo.scale-400_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-100_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-100_contrast-white.png new file mode 100644 index 000000000000..36d7d0aab514 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-100_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-125_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-125_contrast-white.png new file mode 100644 index 000000000000..86dabc9b29c7 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-125_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-150_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-150_contrast-white.png new file mode 100644 index 000000000000..442c06009bb7 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-150_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-200_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-200_contrast-white.png new file mode 100644 index 000000000000..9ae7f647bf6a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-200_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-400_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-400_contrast-white.png new file mode 100644 index 000000000000..4aa2e8ad7bc3 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Large310x310Logo.scale-400_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-100_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-100_contrast-white.png new file mode 100644 index 000000000000..8dead135538f Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-100_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-125_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-125_contrast-white.png new file mode 100644 index 000000000000..692fc38de02e Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-125_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-150_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-150_contrast-white.png new file mode 100644 index 000000000000..8f920ed685c8 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-150_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-200_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-200_contrast-white.png new file mode 100644 index 000000000000..f2b30c482b17 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-200_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-400_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-400_contrast-white.png new file mode 100644 index 000000000000..0915eacd859b Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Small71x71Logo.scale-400_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-100_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-100_contrast-white.png new file mode 100644 index 000000000000..24cbf599fe92 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-100_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-125_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-125_contrast-white.png new file mode 100644 index 000000000000..ac5ab7b98a66 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-125_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-150_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-150_contrast-white.png new file mode 100644 index 000000000000..a8c2127c4fd3 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-150_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-200_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-200_contrast-white.png new file mode 100644 index 000000000000..db7de583a768 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-200_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-400_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-400_contrast-white.png new file mode 100644 index 000000000000..e4b1173e644b Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/SplashScreen.scale-400_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-100_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-100_contrast-white.png new file mode 100644 index 000000000000..b3d2450434aa Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-100_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-125_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-125_contrast-white.png new file mode 100644 index 000000000000..a6008a4174f4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-125_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-150_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-150_contrast-white.png new file mode 100644 index 000000000000..3246749c9b9b Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-150_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-200_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-200_contrast-white.png new file mode 100644 index 000000000000..1a45ec4b165b Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-200_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-400_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-400_contrast-white.png new file mode 100644 index 000000000000..e4acb4d164e3 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square150x150Logo.scale-400_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-100_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-100_contrast-white.png new file mode 100644 index 000000000000..75f09d4ea9dd Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-100_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-125_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-125_contrast-white.png new file mode 100644 index 000000000000..8d737adc2b90 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-125_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-150_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-150_contrast-white.png new file mode 100644 index 000000000000..3dce40cf8b89 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-150_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-200_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-200_contrast-white.png new file mode 100644 index 000000000000..3a6a5a205a95 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-200_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-400_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-400_contrast-white.png new file mode 100644 index 000000000000..2ece25f9a2a0 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.scale-400_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..2859902e0152 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-16_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-16_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-16_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..2859902e0152 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-16_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-16_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-16_contrast-white.png new file mode 100644 index 000000000000..2859902e0152 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-16_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..baa6a5f959e6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-20_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-20_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-20_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..baa6a5f959e6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-20_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-20_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-20_contrast-white.png new file mode 100644 index 000000000000..baa6a5f959e6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-20_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..0f45c05c17c6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-24_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-24_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-24_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..0f45c05c17c6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-24_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-24_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-24_contrast-white.png new file mode 100644 index 000000000000..0f45c05c17c6 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-24_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..5249bb6d2c42 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-256_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-256_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-256_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..5249bb6d2c42 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-256_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-256_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-256_contrast-white.png new file mode 100644 index 000000000000..5249bb6d2c42 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-256_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..8213d8129a2a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-30_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-30_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-30_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..8213d8129a2a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-30_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-30_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-30_contrast-white.png new file mode 100644 index 000000000000..8213d8129a2a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-30_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..22b3d22828b8 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-32_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-32_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-32_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..22b3d22828b8 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-32_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-32_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-32_contrast-white.png new file mode 100644 index 000000000000..22b3d22828b8 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-32_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..67b694ac9b35 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-36_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-36_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-36_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..67b694ac9b35 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-36_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-36_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-36_contrast-white.png new file mode 100644 index 000000000000..67b694ac9b35 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-36_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..f54acadee418 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-40_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-40_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-40_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..f54acadee418 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-40_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-40_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-40_contrast-white.png new file mode 100644 index 000000000000..f54acadee418 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-40_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..670aa904fce4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-48_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-48_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-48_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..670aa904fce4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-48_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-48_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-48_contrast-white.png new file mode 100644 index 000000000000..670aa904fce4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-48_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..4d3dfcf49d5c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-60_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-60_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-60_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..4d3dfcf49d5c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-60_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-60_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-60_contrast-white.png new file mode 100644 index 000000000000..4d3dfcf49d5c Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-60_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..7669c9559bc9 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-64_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-64_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-64_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..7669c9559bc9 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-64_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-64_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-64_contrast-white.png new file mode 100644 index 000000000000..7669c9559bc9 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-64_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..4d56c856558a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-72_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-72_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-72_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..4d56c856558a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-72_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-72_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-72_contrast-white.png new file mode 100644 index 000000000000..4d56c856558a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-72_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..20546d38e1a4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-80_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-80_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-80_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..20546d38e1a4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-80_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-80_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-80_contrast-white.png new file mode 100644 index 000000000000..20546d38e1a4 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-80_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-white.png new file mode 100644 index 000000000000..7f188522f6cf Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-96_altform-lightunplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-96_altform-unplated_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-96_altform-unplated_contrast-white.png new file mode 100644 index 000000000000..7f188522f6cf Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-96_altform-unplated_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-96_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-96_contrast-white.png new file mode 100644 index 000000000000..7f188522f6cf Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Square44x44Logo.targetsize-96_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-100_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-100_contrast-white.png new file mode 100644 index 000000000000..4c77cddf5219 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-100_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-125_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-125_contrast-white.png new file mode 100644 index 000000000000..4220bce64a9a Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-125_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-150_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-150_contrast-white.png new file mode 100644 index 000000000000..475d3c25ae25 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-150_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-200_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-200_contrast-white.png new file mode 100644 index 000000000000..6a080facf779 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-200_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-400_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-400_contrast-white.png new file mode 100644 index 000000000000..768b28fb7658 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/StoreLogo.scale-400_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-100_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-100_contrast-white.png new file mode 100644 index 000000000000..1cf33f8cbbc0 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-100_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-125_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-125_contrast-white.png new file mode 100644 index 000000000000..3e0123187fb8 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-125_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-150_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-150_contrast-white.png new file mode 100644 index 000000000000..c9313f1d6310 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-150_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-200_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-200_contrast-white.png new file mode 100644 index 000000000000..30b1102c0f11 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-200_contrast-white.png differ diff --git a/src/Files.App/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-400_contrast-white.png b/src/Files.App/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-400_contrast-white.png new file mode 100644 index 000000000000..1de222267920 Binary files /dev/null and b/src/Files.App/Assets/AppTiles/Release/contrast-white/Wide310x150Logo.scale-400_contrast-white.png differ diff --git a/src/Files.App/Assets/Archives/ExtensionIcon.png b/src/Files.App/Assets/Archives/ExtensionIcon.png index 95378c03dd96..fb2890f5d787 100644 Binary files a/src/Files.App/Assets/Archives/ExtensionIcon.png and b/src/Files.App/Assets/Archives/ExtensionIcon.png differ diff --git a/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-16.png b/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-16.png index 194556915b00..11ba0e500e98 100644 Binary files a/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-16.png and b/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-16.png differ diff --git a/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-20.png b/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-20.png index 959fd975f4f7..ab6f766bf196 100644 Binary files a/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-20.png and b/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-20.png differ diff --git a/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-24.png b/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-24.png index bfcf027b5523..7d7fac8afcef 100644 Binary files a/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-24.png and b/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-24.png differ diff --git a/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-256.png b/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-256.png index fd1424856c7c..c217129045dc 100644 Binary files a/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-256.png and b/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-256.png differ diff --git a/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-32.png b/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-32.png index 9b83a918dde4..9bd4ab1354bd 100644 Binary files a/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-32.png and b/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-32.png differ diff --git a/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-40.png b/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-40.png index 4667bf05cdc7..8eeb58bd6a0f 100644 Binary files a/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-40.png and b/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-40.png differ diff --git a/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-48.png b/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-48.png index 95378c03dd96..fb2890f5d787 100644 Binary files a/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-48.png and b/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-48.png differ diff --git a/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-64.png b/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-64.png index 7e78016c462d..e8cd9d319197 100644 Binary files a/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-64.png and b/src/Files.App/Assets/Archives/ExtensionIcon.targetsize-64.png differ diff --git a/src/Files.App/Assets/FilesOpenDialog/Files.App.Launcher.exe b/src/Files.App/Assets/FilesOpenDialog/Files.App.Launcher.exe deleted file mode 100644 index c6da6679d18e..000000000000 Binary files a/src/Files.App/Assets/FilesOpenDialog/Files.App.Launcher.exe and /dev/null differ diff --git a/src/Files.App/Assets/FilesOpenDialog/Files.App.Launcher.exe.sha256 b/src/Files.App/Assets/FilesOpenDialog/Files.App.Launcher.exe.sha256 deleted file mode 100644 index 4abeab6731c4..000000000000 --- a/src/Files.App/Assets/FilesOpenDialog/Files.App.Launcher.exe.sha256 +++ /dev/null @@ -1 +0,0 @@ -9f09add6084f2b30451d91864a573cc4cd0bf728a1df9bfc87fd1b7027487c60 diff --git a/src/Files.App/Assets/FluentIcons/CloudDrive.png b/src/Files.App/Assets/FluentIcons/CloudDrive.png index c5980cd39a22..e88d8d069379 100644 Binary files a/src/Files.App/Assets/FluentIcons/CloudDrive.png and b/src/Files.App/Assets/FluentIcons/CloudDrive.png differ diff --git a/src/Files.App/Assets/FluentIcons/FileTags.png b/src/Files.App/Assets/FluentIcons/FileTags.png index f91982fa57f0..adb0fa0ce196 100644 Binary files a/src/Files.App/Assets/FluentIcons/FileTags.png and b/src/Files.App/Assets/FluentIcons/FileTags.png differ diff --git a/src/Files.App/Assets/FluentIcons/Home.png b/src/Files.App/Assets/FluentIcons/Home.png index b6121308740e..d7f1c25daf8a 100644 Binary files a/src/Files.App/Assets/FluentIcons/Home.png and b/src/Files.App/Assets/FluentIcons/Home.png differ diff --git a/src/Files.App/Assets/FluentIcons/Star.png b/src/Files.App/Assets/FluentIcons/Star.png index 595636435259..54949d7ca32d 100644 Binary files a/src/Files.App/Assets/FluentIcons/Star.png and b/src/Files.App/Assets/FluentIcons/Star.png differ diff --git a/src/Files.App/Assets/FolderIcon.png b/src/Files.App/Assets/FolderIcon.png index 3f76f460c329..4170caf5ea20 100644 Binary files a/src/Files.App/Assets/FolderIcon.png and b/src/Files.App/Assets/FolderIcon.png differ diff --git a/src/Files.App/Assets/Libraries/Microsoft.UI.winmd b/src/Files.App/Assets/Libraries/Microsoft.UI.winmd new file mode 100644 index 000000000000..ce9ff6630baa Binary files /dev/null and b/src/Files.App/Assets/Libraries/Microsoft.UI.winmd differ diff --git a/src/Files.App/Assets/Libraries/OwlCore.Storage.0.14.0.999.nupkg b/src/Files.App/Assets/Libraries/OwlCore.Storage.0.14.0.999.nupkg new file mode 100644 index 000000000000..1f6cd7a26ede Binary files /dev/null and b/src/Files.App/Assets/Libraries/OwlCore.Storage.0.14.0.999.nupkg differ diff --git a/src/Files.App/nupkgs/SevenZipSharp.1.0.2.nupkg b/src/Files.App/Assets/Libraries/SevenZipSharp.1.0.2.nupkg similarity index 100% rename from src/Files.App/nupkgs/SevenZipSharp.1.0.2.nupkg rename to src/Files.App/Assets/Libraries/SevenZipSharp.1.0.2.nupkg diff --git a/src/Files.App/Assets/Shelf/EmptyShelf_100_ThemeDark.svg b/src/Files.App/Assets/Shelf/EmptyShelf_100_ThemeDark.svg new file mode 100644 index 000000000000..2f7cfa0b9c5b --- /dev/null +++ b/src/Files.App/Assets/Shelf/EmptyShelf_100_ThemeDark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Files.App/Assets/Shelf/EmptyShelf_100_ThemeLight.svg b/src/Files.App/Assets/Shelf/EmptyShelf_100_ThemeLight.svg new file mode 100644 index 000000000000..d6376c979f62 --- /dev/null +++ b/src/Files.App/Assets/Shelf/EmptyShelf_100_ThemeLight.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Files.App/Assets/Shelf/EmptyShelf_200_ThemeDark.svg b/src/Files.App/Assets/Shelf/EmptyShelf_200_ThemeDark.svg new file mode 100644 index 000000000000..7e660a2a6b3d --- /dev/null +++ b/src/Files.App/Assets/Shelf/EmptyShelf_200_ThemeDark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Files.App/Assets/Shelf/EmptyShelf_200_ThemeLight.svg b/src/Files.App/Assets/Shelf/EmptyShelf_200_ThemeLight.svg new file mode 100644 index 000000000000..79b7fd258c17 --- /dev/null +++ b/src/Files.App/Assets/Shelf/EmptyShelf_200_ThemeLight.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Files.App/Assets/Shelf/EmptyShelf_48_ThemeDark.svg b/src/Files.App/Assets/Shelf/EmptyShelf_48_ThemeDark.svg new file mode 100644 index 000000000000..5c215a323d74 --- /dev/null +++ b/src/Files.App/Assets/Shelf/EmptyShelf_48_ThemeDark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Files.App/Assets/Shelf/EmptyShelf_48_ThemeLight.svg b/src/Files.App/Assets/Shelf/EmptyShelf_48_ThemeLight.svg new file mode 100644 index 000000000000..d1433f812d75 --- /dev/null +++ b/src/Files.App/Assets/Shelf/EmptyShelf_48_ThemeLight.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Files.App/Assets/Sidebar/EmptySidebar_100_ThemeDark.svg b/src/Files.App/Assets/Sidebar/EmptySidebar_100_ThemeDark.svg new file mode 100644 index 000000000000..df74f1c71dd0 --- /dev/null +++ b/src/Files.App/Assets/Sidebar/EmptySidebar_100_ThemeDark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Files.App/Assets/Sidebar/EmptySidebar_100_ThemeLight.svg b/src/Files.App/Assets/Sidebar/EmptySidebar_100_ThemeLight.svg new file mode 100644 index 000000000000..044f44721283 --- /dev/null +++ b/src/Files.App/Assets/Sidebar/EmptySidebar_100_ThemeLight.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Files.App/Assets/Sidebar/EmptySidebar_200_ThemeDark.svg b/src/Files.App/Assets/Sidebar/EmptySidebar_200_ThemeDark.svg new file mode 100644 index 000000000000..e1f199493836 --- /dev/null +++ b/src/Files.App/Assets/Sidebar/EmptySidebar_200_ThemeDark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Files.App/Assets/Sidebar/EmptySidebar_200_ThemeLight.svg b/src/Files.App/Assets/Sidebar/EmptySidebar_200_ThemeLight.svg new file mode 100644 index 000000000000..d26bca2b4a21 --- /dev/null +++ b/src/Files.App/Assets/Sidebar/EmptySidebar_200_ThemeLight.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Files.App/Assets/Sidebar/EmptySidebar_48_ThemeDark.svg b/src/Files.App/Assets/Sidebar/EmptySidebar_48_ThemeDark.svg new file mode 100644 index 000000000000..597b1f5484ee --- /dev/null +++ b/src/Files.App/Assets/Sidebar/EmptySidebar_48_ThemeDark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Files.App/Assets/Sidebar/EmptySidebar_48_ThemeLight.svg b/src/Files.App/Assets/Sidebar/EmptySidebar_48_ThemeLight.svg new file mode 100644 index 000000000000..afdff01524b7 --- /dev/null +++ b/src/Files.App/Assets/Sidebar/EmptySidebar_48_ThemeLight.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Files.App/Assets/WSL/alpinepng.png b/src/Files.App/Assets/WSL/alpinepng.png index dec8cac3c44c..ff3a43e30d9f 100644 Binary files a/src/Files.App/Assets/WSL/alpinepng.png and b/src/Files.App/Assets/WSL/alpinepng.png differ diff --git a/src/Files.App/Assets/WSL/debianpng.png b/src/Files.App/Assets/WSL/debianpng.png index a032f35c3fdf..b938887e0de0 100644 Binary files a/src/Files.App/Assets/WSL/debianpng.png and b/src/Files.App/Assets/WSL/debianpng.png differ diff --git a/src/Files.App/Assets/WSL/kalipng.png b/src/Files.App/Assets/WSL/kalipng.png index 805fd432e9d6..bda5634e9fd0 100644 Binary files a/src/Files.App/Assets/WSL/kalipng.png and b/src/Files.App/Assets/WSL/kalipng.png differ diff --git a/src/Files.App/Assets/WSL/opensusepng.png b/src/Files.App/Assets/WSL/opensusepng.png index 8fa49eb20529..86571789de06 100644 Binary files a/src/Files.App/Assets/WSL/opensusepng.png and b/src/Files.App/Assets/WSL/opensusepng.png differ diff --git a/src/Files.App/Assets/WSL/ubuntupng.png b/src/Files.App/Assets/WSL/ubuntupng.png index 8aae04bfb7ff..f2de753ec092 100644 Binary files a/src/Files.App/Assets/WSL/ubuntupng.png and b/src/Files.App/Assets/WSL/ubuntupng.png differ diff --git a/src/Files.App/Constants.cs b/src/Files.App/Constants.cs index 46e4325eeea5..578c8951644f 100644 --- a/src/Files.App/Constants.cs +++ b/src/Files.App/Constants.cs @@ -1,5 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Windows.ApplicationModel; namespace Files.App { @@ -54,6 +56,7 @@ public static class ImageRes public const int Libraries = 1023; public const int Folder = 3; public const int ShieldIcon = 78; + public const int SearchIcon = 177; } public static class Shell32 @@ -202,9 +205,10 @@ public static class ExternalUrl public const string DiscordUrl = @"https://discord.gg/files"; public const string FeatureRequestUrl = @"https://github.com/files-community/Files/issues/new?labels=feature+request&template=feature_request.yml"; public const string BugReportUrl = @"https://github.com/files-community/Files/issues/new?labels=bug&template=bug_report.yml"; - public const string PrivacyPolicyUrl = @"https://github.com/files-community/Files/blob/main/.github/PRIVACY.md"; - public const string SupportUsUrl = @"https://github.com/sponsors/yaira2"; + public const string PrivacyPolicyUrl = @"https://files.community/privacy"; + public const string SupportUsUrl = @"https://github.com/files-community/Files?sponsor"; public const string CrowdinUrl = @"https://crowdin.com/project/files-app"; + public static readonly string ReleaseNotesUrl = $"https://files.community/blog/posts/v{Package.Current.Id.Version.Major}-{Package.Current.Id.Version.Minor}-{Package.Current.Id.Version.Build}?minimal"; } public static class DocsPath @@ -217,6 +221,11 @@ public static class Actions public const int MaxSelectedItems = 5; } + public static class DragAndDrop + { + public const Int32 HoverToOpenTimespan = 1300; + } + public static class UserEnvironmentPaths { public static readonly string DesktopPath = Windows.Storage.UserDataPaths.GetDefault().Desktop; @@ -240,6 +249,14 @@ public static class UserEnvironmentPaths public static readonly string RecentItemsPath = Environment.GetFolderPath(Environment.SpecialFolder.Recent); + public static readonly string SystemDrivePath; + + static UserEnvironmentPaths() + { + var systemDrive = Environment.GetEnvironmentVariable("SystemDrive"); + SystemDrivePath = !string.IsNullOrEmpty(systemDrive) ? systemDrive : "C:"; + } + public static Dictionary ShellPlaces = new() { @@ -253,5 +270,25 @@ public static class UserEnvironmentPaths { NetworkFolderPath.ToUpperInvariant(), NetworkFolderPath }, }; } + + public static class Distributions + { + public static readonly string[] KnownAppNames = + { + "49306atecsolution.FilesUWP", // store stable + "FilesStable", // sideload stable + "FilesPreview", // sideload preview + "49306atecsolution.FilesPreview", // store preview + "FilesDev", // dev + }; + } + + public static class Startup + { + // These strings are intentionally hardcoded and cannot be moved to resource files. + // The Windows App Runtime (which powers the resource loading system) may itself be unavailable at this point + public const string MissingRuntimeMessage = "Files failed to start. A required Windows component could not be loaded. Try reinstalling Files from the Microsoft Store or from https://files.community/download"; + public const string MissingRuntimeTitle = "Files - Startup Error"; + } } } diff --git a/src/Files.App/Converters/ActionCategoryConverter.cs b/src/Files.App/Converters/ActionCategoryConverter.cs new file mode 100644 index 000000000000..fb9ac9be1b29 --- /dev/null +++ b/src/Files.App/Converters/ActionCategoryConverter.cs @@ -0,0 +1,34 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Converters +{ + public static class ActionCategoryConverter + { + public static string ToLocalizedCategoryPath(ActionCategory category) + => category switch + { + ActionCategory.Archive => Strings.Archive.GetLocalizedResource(), + ActionCategory.Display => Strings.Display.GetLocalizedResource(), + ActionCategory.Edit => Strings.Edit.GetLocalizedResource(), + ActionCategory.FileSystem => Strings.FileSystem.GetLocalizedResource(), + ActionCategory.Grouping => Strings.GroupBy.GetLocalizedResource(), + ActionCategory.Git => Strings.Git.GetLocalizedResource(), + ActionCategory.Image => Strings.PropertySectionImage.GetLocalizedResource(), + ActionCategory.Install => Strings.Install.GetLocalizedResource(), + ActionCategory.Layout => Strings.Layout.GetLocalizedResource(), + ActionCategory.Media => Strings.PropertySectionMedia.GetLocalizedResource(), + ActionCategory.Navigation => Strings.Navigation.GetLocalizedResource(), + ActionCategory.Create => Strings.Create.GetLocalizedResource(), + ActionCategory.Open => Strings.Open.GetLocalizedResource(), + ActionCategory.Run => Strings.Run.GetLocalizedResource(), + ActionCategory.Selection => Strings.Selection.GetLocalizedResource(), + ActionCategory.Show => Strings.Show.GetLocalizedResource(), + ActionCategory.Sorting => Strings.SortBy.GetLocalizedResource(), + ActionCategory.Start => Strings.Start.GetLocalizedResource(), + ActionCategory.Window => Strings.Window.GetLocalizedResource(), + ActionCategory.DualPane => Strings.DualPaneCategory.GetLocalizedResource(), + _ => Strings.General.GetLocalizedResource(), + }; + } +} \ No newline at end of file diff --git a/src/Files.App/Converters/BoolToSelectionModeConverter.cs b/src/Files.App/Converters/BoolToSelectionModeConverter.cs index 025849e25af9..1eaa8e2bacb8 100644 --- a/src/Files.App/Converters/BoolToSelectionModeConverter.cs +++ b/src/Files.App/Converters/BoolToSelectionModeConverter.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Data; namespace Files.App.Converters { - internal sealed class BoolToSelectionModeConverter : IValueConverter + internal sealed partial class BoolToSelectionModeConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { diff --git a/src/Files.App/Converters/ConflictResolveOptionToIndexConverter.cs b/src/Files.App/Converters/ConflictResolveOptionToIndexConverter.cs index 876d78ae6195..74f41dfa338d 100644 --- a/src/Files.App/Converters/ConflictResolveOptionToIndexConverter.cs +++ b/src/Files.App/Converters/ConflictResolveOptionToIndexConverter.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Data; namespace Files.App.Converters { - internal sealed class ConflictResolveOptionToIndexConverter : IValueConverter + internal sealed partial class ConflictResolveOptionToIndexConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { diff --git a/src/Files.App/Converters/Converters.cs b/src/Files.App/Converters/Converters.cs index 437e25519bd4..1f5cdb5620ec 100644 --- a/src/Files.App/Converters/Converters.cs +++ b/src/Files.App/Converters/Converters.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Data; @@ -119,7 +119,7 @@ internal abstract class ToObjectConverter : ValueConverter /// /// Converts a boolean to and from a visibility value. /// - internal sealed class InverseBooleanConverter : ValueConverter + internal sealed partial class InverseBooleanConverter : ValueConverter { /// /// Converts a source value to the target type. @@ -146,7 +146,7 @@ protected override bool ConvertBack(bool value, object? parameter, string? langu } } - internal sealed class NullToTrueConverter : ValueConverter + internal sealed partial class NullToTrueConverter : ValueConverter { /// /// Determines whether an inverse conversion should take place. @@ -179,7 +179,7 @@ protected override bool Convert(object? value, object? parameter, string? langua } } - internal sealed class StringNullOrWhiteSpaceToTrueConverter : ValueConverter + internal sealed partial class StringNullOrWhiteSpaceToTrueConverter : ValueConverter { /// /// Determines whether an inverse conversion should take place. @@ -212,7 +212,7 @@ protected override string ConvertBack(bool value, object? parameter, string? lan } } - internal sealed class NullToVisibilityCollapsedConverter : ValueConverter + internal sealed partial class NullToVisibilityCollapsedConverter : ValueConverter { /// /// Converts a source value to the target type. @@ -238,4 +238,31 @@ protected override Visibility Convert(object? value, object? parameter, string? return new NotSupportedException(); } } + + internal sealed partial class EmptyListToVisibilityConverter : ValueConverter + { + protected override Visibility Convert(object? value, object? parameter, string? language) + { + return (value is int count && count == 0) ? Visibility.Visible : Visibility.Collapsed; + } + + protected override object? ConvertBack(Visibility value, object? parameter, string? language) + { + return new NotSupportedException(); + } + } + + internal sealed partial class PopulatedListToVisibilityConverter : ValueConverter + { + protected override Visibility Convert(object? value, object? parameter, string? language) + { + return (value is int count && count > 0) ? Visibility.Visible : Visibility.Collapsed; + } + + protected override object? ConvertBack(Visibility value, object? parameter, string? language) + { + return new NotSupportedException(); + } + } } + diff --git a/src/Files.App/Converters/DateTimeOffsetToStringConverter.cs b/src/Files.App/Converters/DateTimeOffsetToStringConverter.cs index 11f95a75bd95..f919197467e0 100644 --- a/src/Files.App/Converters/DateTimeOffsetToStringConverter.cs +++ b/src/Files.App/Converters/DateTimeOffsetToStringConverter.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Data; namespace Files.App.Converters { - internal sealed class DateTimeOffsetToStringConverter : IValueConverter + internal sealed partial class DateTimeOffsetToStringConverter : IValueConverter { private static readonly IDateTimeFormatter formatter = Ioc.Default.GetService(); diff --git a/src/Files.App/Converters/DoubleArrayToStringConverter.cs b/src/Files.App/Converters/DoubleArrayToStringConverter.cs index b93100ce4e70..31953912be9d 100644 --- a/src/Files.App/Converters/DoubleArrayToStringConverter.cs +++ b/src/Files.App/Converters/DoubleArrayToStringConverter.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Data; using System.Text; namespace Files.App.Converters { - internal sealed class DoubleArrayToStringConverter : IValueConverter + internal sealed partial class DoubleArrayToStringConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { @@ -19,7 +19,7 @@ public object Convert(object value, Type targetType, object parameter, string la foreach (var i in array) { - str.Append(string.Format("{0}; ", i)); + str.Append($"{i}; "); } return str.ToString(); diff --git a/src/Files.App/Converters/DoubleToStringConverter.cs b/src/Files.App/Converters/DoubleToStringConverter.cs index 198a9cc8eb8c..da2646ede853 100644 --- a/src/Files.App/Converters/DoubleToStringConverter.cs +++ b/src/Files.App/Converters/DoubleToStringConverter.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Data; namespace Files.App.Converters { - internal sealed class DoubleToStringConverter : IValueConverter + internal sealed partial class DoubleToStringConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { diff --git a/src/Files.App/Converters/EnumToBoolConverter.cs b/src/Files.App/Converters/EnumToBoolConverter.cs index 5a640b5153bb..db3e73b2f91c 100644 --- a/src/Files.App/Converters/EnumToBoolConverter.cs +++ b/src/Files.App/Converters/EnumToBoolConverter.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Data; namespace Files.App.Converters { - internal sealed class EnumToBoolConverter : IValueConverter + internal sealed partial class EnumToBoolConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { diff --git a/src/Files.App/Converters/EnumToHumanizedConverter.cs b/src/Files.App/Converters/EnumToHumanizedConverter.cs index 0ab74343f023..c66038e8369b 100644 --- a/src/Files.App/Converters/EnumToHumanizedConverter.cs +++ b/src/Files.App/Converters/EnumToHumanizedConverter.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Data; namespace Files.App.Converters { - internal sealed class EnumToHumanizedConverter : IValueConverter + internal sealed partial class EnumToHumanizedConverter : IValueConverter { public string EnumTypeName { get; set; } = string.Empty; @@ -19,8 +19,8 @@ public object Convert(object value, Type targetType, object parameter, string la => LocalizedEnumDescriptionFactory.Get(Enum.Parse(stringValue)), "ListViewSizeKind" => LocalizedEnumDescriptionFactory.Get(Enum.Parse(stringValue)), - "TilesViewSizeKind" - => LocalizedEnumDescriptionFactory.Get(Enum.Parse(stringValue)), + "CardsViewSizeKind" + => LocalizedEnumDescriptionFactory.Get(Enum.Parse(stringValue)), "GridViewSizeKind" => LocalizedEnumDescriptionFactory.Get(Enum.Parse(stringValue)), "ColumnsViewSizeKind" diff --git a/src/Files.App/Converters/EnumToStringConverter.cs b/src/Files.App/Converters/EnumToStringConverter.cs index 89ad76920148..fe61c41ea5ee 100644 --- a/src/Files.App/Converters/EnumToStringConverter.cs +++ b/src/Files.App/Converters/EnumToStringConverter.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Data; namespace Files.App.Converters { - internal sealed class EnumToStringConverter : IValueConverter + internal sealed partial class EnumToStringConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { diff --git a/src/Files.App/Converters/GenericEnumConverter.cs b/src/Files.App/Converters/GenericEnumConverter.cs index 3347da45d61b..a704326bbe7a 100644 --- a/src/Files.App/Converters/GenericEnumConverter.cs +++ b/src/Files.App/Converters/GenericEnumConverter.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Data; namespace Files.App.Converters { - internal sealed class GenericEnumConverter : IValueConverter + internal sealed partial class GenericEnumConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { diff --git a/src/Files.App/Converters/ImageModelToImageConverter.cs b/src/Files.App/Converters/ImageModelToImageConverter.cs index 9d0ac2267ede..52763c680f2d 100644 --- a/src/Files.App/Converters/ImageModelToImageConverter.cs +++ b/src/Files.App/Converters/ImageModelToImageConverter.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Data; namespace Files.App.Converters { - internal sealed class ImageModelToImageConverter : IValueConverter + internal sealed partial class ImageModelToImageConverter : IValueConverter { public object? Convert(object value, Type targetType, object parameter, string language) { diff --git a/src/Files.App/Converters/MultiBooleanConverter.cs b/src/Files.App/Converters/MultiBooleanConverter.cs index ea02e07e517e..bbae5d7784f9 100644 --- a/src/Files.App/Converters/MultiBooleanConverter.cs +++ b/src/Files.App/Converters/MultiBooleanConverter.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; @@ -22,6 +22,9 @@ public static Boolean OrAndConvert(bool a, bool b, bool c) public static Visibility OrConvertToVisibility(bool a, bool b) => (a || b) ? Visibility.Visible : Visibility.Collapsed; + public static Visibility AndConvertToVisibility(bool a, bool b) + => (a && b) ? Visibility.Visible : Visibility.Collapsed; + public static Visibility NorConvertToVisibility(bool a, bool b) => !(a || b) ? Visibility.Visible : Visibility.Collapsed; diff --git a/src/Files.App/Converters/StatusCenterStateToBrushConverter.cs b/src/Files.App/Converters/StatusCenterStateToBrushConverter.cs index 92ee98e3a0d1..77398fc0f141 100644 --- a/src/Files.App/Converters/StatusCenterStateToBrushConverter.cs +++ b/src/Files.App/Converters/StatusCenterStateToBrushConverter.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Data; @@ -7,7 +7,7 @@ namespace Files.App.Converters { - public sealed class StatusCenterStateToBrushConverter : DependencyObject, IValueConverter + public sealed partial class StatusCenterStateToBrushConverter : DependencyObject, IValueConverter { public static readonly DependencyProperty InProgressBackgroundBrushProperty = DependencyProperty.Register(nameof(InProgressBackgroundBrush), typeof(SolidColorBrush), typeof(StatusCenterStateToBrushConverter), new PropertyMetadata(null)); @@ -84,7 +84,7 @@ public SolidColorBrush CanceledForegroundBrush public object? Convert(object value, Type targetType, object parameter, string language) { if (value is StatusCenterItemKind state) - { + { if (bool.TryParse(parameter?.ToString(), out var isBackground) && isBackground) { return state switch diff --git a/src/Files.App/Converters/StatusCenterStateToStateIconConverter.cs b/src/Files.App/Converters/StatusCenterStateToStateIconConverter.cs index 2cbcad428889..62a01038914a 100644 --- a/src/Files.App/Converters/StatusCenterStateToStateIconConverter.cs +++ b/src/Files.App/Converters/StatusCenterStateToStateIconConverter.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Data; @@ -9,7 +9,7 @@ namespace Files.App.Converters { - class StatusCenterStateToStateIconConverter : IValueConverter + partial class StatusCenterStateToStateIconConverter : IValueConverter { public object? Convert(object value, Type targetType, object parameter, string language) { @@ -17,14 +17,16 @@ class StatusCenterStateToStateIconConverter : IValueConverter { var pathMarkup = state switch { - StatusCenterItemIconKind.Copy => Application.Current.Resources["App.Theme.PathIcon.ActionCopy"] as string, - StatusCenterItemIconKind.Move => Application.Current.Resources["App.Theme.PathIcon.ActionMove"] as string, - StatusCenterItemIconKind.Delete => Application.Current.Resources["App.Theme.PathIcon.ActionDelete"] as string, - StatusCenterItemIconKind.Recycle => Application.Current.Resources["App.Theme.PathIcon.ActionDelete"] as string, - StatusCenterItemIconKind.Extract => Application.Current.Resources["App.Theme.PathIcon.ActionExtract"] as string, - StatusCenterItemIconKind.Compress => Application.Current.Resources["App.Theme.PathIcon.ActionExtract"] as string, + StatusCenterItemIconKind.Copy => Application.Current.Resources["App.Theme.PathIcon.ActionCopy"] as string, + StatusCenterItemIconKind.Move => Application.Current.Resources["App.Theme.PathIcon.ActionMove"] as string, + StatusCenterItemIconKind.Delete => Application.Current.Resources["App.Theme.PathIcon.ActionDelete"] as string, + StatusCenterItemIconKind.Recycle => Application.Current.Resources["App.Theme.PathIcon.ActionDelete"] as string, + StatusCenterItemIconKind.Extract => Application.Current.Resources["App.Theme.PathIcon.ActionExtract"] as string, + StatusCenterItemIconKind.Compress => Application.Current.Resources["App.Theme.PathIcon.ActionExtract"] as string, StatusCenterItemIconKind.Successful => Application.Current.Resources["App.Theme.PathIcon.ActionSuccess"] as string, - StatusCenterItemIconKind.Error => Application.Current.Resources["App.Theme.PathIcon.ActionInfo"] as string, + StatusCenterItemIconKind.Error => Application.Current.Resources["App.Theme.PathIcon.ActionInfo"] as string, + StatusCenterItemIconKind.GitClone => Application.Current.Resources["App.Theme.PathIcon.ActionGitClone"] as string, + StatusCenterItemIconKind.InstallFont => Application.Current.Resources["App.Theme.PathIcon.ActionInstallFont"] as string, _ => "" }; diff --git a/src/Files.App/Converters/StorageDeleteOptionToBooleanConverter.cs b/src/Files.App/Converters/StorageDeleteOptionToBooleanConverter.cs index aa303d9e4872..5fb6f1f429f8 100644 --- a/src/Files.App/Converters/StorageDeleteOptionToBooleanConverter.cs +++ b/src/Files.App/Converters/StorageDeleteOptionToBooleanConverter.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Data; using Windows.Storage; namespace Files.App.Converters { - internal sealed class StorageDeleteOptionToBooleanConverter : IValueConverter + internal sealed partial class StorageDeleteOptionToBooleanConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { diff --git a/src/Files.App/Converters/StringArrayToStringConverter.cs b/src/Files.App/Converters/StringArrayToStringConverter.cs index 8f192a4f7c71..85d25f6e96eb 100644 --- a/src/Files.App/Converters/StringArrayToStringConverter.cs +++ b/src/Files.App/Converters/StringArrayToStringConverter.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Data; using System.Text; namespace Files.App.Converters { - internal sealed class StringArrayToStringConverter : IValueConverter + internal sealed partial class StringArrayToStringConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { @@ -16,9 +16,9 @@ public object Convert(object value, Type targetType, object parameter, string la return string.Empty; var str = new StringBuilder(); - foreach (var i in array) + foreach (var s in array) { - str.Append(string.Format("{0}; ", i)); + str.Append($"{s}; "); } return str.ToString(); diff --git a/src/Files.App/Converters/StringToBrushConverter.cs b/src/Files.App/Converters/StringToBrushConverter.cs index 65213380477c..b7b647492631 100644 --- a/src/Files.App/Converters/StringToBrushConverter.cs +++ b/src/Files.App/Converters/StringToBrushConverter.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using CommunityToolkit.WinUI.Helpers; using Microsoft.UI; @@ -8,7 +8,7 @@ namespace Files.App.Converters { - internal sealed class StringToBrushConverter : IValueConverter + internal sealed partial class StringToBrushConverter : IValueConverter { public object? Convert(object value, Type targetType, object parameter, string language) { diff --git a/src/Files.App/Converters/UInt32ToStringConverter.cs b/src/Files.App/Converters/UInt32ToStringConverter.cs index 426b4ccd8f34..51d5e3f723b4 100644 --- a/src/Files.App/Converters/UInt32ToStringConverter.cs +++ b/src/Files.App/Converters/UInt32ToStringConverter.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Data; namespace Files.App.Converters { - internal sealed class UInt32ToStringConverter : IValueConverter + internal sealed partial class UInt32ToStringConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { diff --git a/src/Files.App/Converters/VisibilityInvertConverter.cs b/src/Files.App/Converters/VisibilityInvertConverter.cs index 24f8e4e070db..62bab14ea8cf 100644 --- a/src/Files.App/Converters/VisibilityInvertConverter.cs +++ b/src/Files.App/Converters/VisibilityInvertConverter.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Data; namespace Files.App.Converters { - internal sealed class VisibilityInvertConverter : IValueConverter + internal sealed partial class VisibilityInvertConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { diff --git a/src/Files.App/Data/AppModels/SingleFileDatabaseModel.cs b/src/Files.App/Data/AppModels/SingleFileDatabaseModel.cs index eff6c179e827..9b858e0dfcd1 100644 --- a/src/Files.App/Data/AppModels/SingleFileDatabaseModel.cs +++ b/src/Files.App/Data/AppModels/SingleFileDatabaseModel.cs @@ -1,14 +1,10 @@ -using Files.Core.Storage; -using Files.Core.Storage.Extensions; -using Files.Core.Storage.Storables; -using Files.Shared.Extensions; -using Files.Shared.Utils; +using Files.Shared.Utils; using System.IO; namespace Files.App.Data.AppModels { /// - public sealed class SingleFileDatabaseModel : BaseDatabaseModel + public sealed partial class SingleFileDatabaseModel : BaseDatabaseModel { private readonly string _fileName; private readonly IModifiableFolder _settingsFolder; diff --git a/src/Files.App/Data/Attributes/DependencyPropertyAttribute.cs b/src/Files.App/Data/Attributes/DependencyPropertyAttribute.cs deleted file mode 100644 index 22879ec09111..000000000000 --- a/src/Files.App/Data/Attributes/DependencyPropertyAttribute.cs +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Microsoft.UI.Xaml; - -namespace Files.App.Data.Attributes -{ - /// - /// Provides an attribute for generation of and its Property. - /// - /// - /// Following code will be generated: - /// - /// Property = - /// .Register( - /// (Field), - /// (), - /// (TClass), - /// (DefaultValue, OnPropertyChanged)); - ///
- ///
- /// Field - /// { - /// => ()GetValue(Property); - /// => SetValue(Property, ); - /// } - ///
- /// property type (nullable value type are not allowed) - ///
- [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] - public sealed class DependencyPropertyAttribute : Attribute where T : notnull - { - /// - /// The name of the property. - /// The name of the method, which called when property changed. - public DependencyPropertyAttribute(string name, string propertyChanged = "") - { - Name = name; - PropertyChanged = propertyChanged; - } - - /// - /// Gets the name of the property. - /// - public string Name { get; } - - /// - /// Gets the name of the method, which called when property changed. - /// - public string PropertyChanged { get; } - - /// - /// Gets or initializes a value whether property setter is private. - /// - /// - /// Default value is . - /// - public bool IsSetterPrivate { get; init; } - - /// - /// Gets or initializes a value whether property type is nullable (nullable value type are not allowed). - /// - /// - /// Default value is . - /// - public bool IsNullable { get; init; } - - /// - /// Gets or initializes a default value of property. - /// - /// default: - public string DefaultValue { get; init; } = "DependencyProperty.UnsetValue"; - } -} diff --git a/src/Files.App/Data/Behaviors/StickyHeaderBehavior.cs b/src/Files.App/Data/Behaviors/StickyHeaderBehavior.cs index 9f5e98fbc2de..d41f144e626b 100644 --- a/src/Files.App/Data/Behaviors/StickyHeaderBehavior.cs +++ b/src/Files.App/Data/Behaviors/StickyHeaderBehavior.cs @@ -1,16 +1,13 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using CommunityToolkit.WinUI.UI; -using CommunityToolkit.WinUI.UI.Animations.Expressions; -using CommunityToolkit.WinUI.UI.Behaviors; +using CommunityToolkit.WinUI; +using CommunityToolkit.WinUI.Animations.Expressions; +using CommunityToolkit.WinUI.Behaviors; using Microsoft.UI.Composition; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Hosting; -using Microsoft.UI.Xaml.Input; -using Microsoft.UI.Xaml.Media; -using Windows.Foundation; using Windows.Foundation.Metadata; namespace Files.App.Data.Behaviors diff --git a/src/Files.App/Data/Commands/ActionCommand.cs b/src/Files.App/Data/Commands/ActionCommand.cs index a2456a41ae0a..8ae55c1a25d6 100644 --- a/src/Files.App/Data/Commands/ActionCommand.cs +++ b/src/Files.App/Data/Commands/ActionCommand.cs @@ -1,7 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Sentry; using Files.App.Actions; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -10,7 +9,7 @@ namespace Files.App.Data.Commands { [DebuggerDisplay("Command {Code}")] - internal sealed class ActionCommand : ObservableObject, IRichCommand + internal sealed partial class ActionCommand : ObservableObject, IRichCommand { private IActionsSettingsService ActionsSettingsService { get; } = Ioc.Default.GetRequiredService(); @@ -25,6 +24,10 @@ internal sealed class ActionCommand : ObservableObject, IRichCommand public string Label => Action.Label; + /// + public string ExtendedLabel + => Action.ExtendedLabel; + /// public string LabelWithHotKey => HotKeyText is null ? Label : $"{Label} ({HotKeyText})"; @@ -48,7 +51,7 @@ public RichGlyph Glyph public FontIcon? FontIcon { get; } /// - public Style? OpacityStyle { get; } + public Style? ThemedIconStyle { get; } private bool isCustomHotKeys = false; /// @@ -70,6 +73,10 @@ public string? HotKeyText } } + /// + public string AccessKey + => Action.AccessKey; + private HotKeyCollection hotKeys; /// public HotKeyCollection HotKeys @@ -112,15 +119,19 @@ public bool IsExecutable public bool IsAccessibleGlobally => Action.IsAccessibleGlobally; - public ActionCommand(CommandManager manager, CommandCodes code, IAction action) + /// + public string AutomationId + => Action.AutomationId; + + public ActionCommand(CommandCodes code, IAction action) { Code = code; Action = action; Icon = action.Glyph.ToIcon(); FontIcon = action.Glyph.ToFontIcon(); - OpacityStyle = action.Glyph.ToOpacityStyle(); - hotKeys = CommandManager.GetDefaultKeyBindings(action); - DefaultHotKeys = CommandManager.GetDefaultKeyBindings(action); + ThemedIconStyle = action.Glyph.ToThemedIconStyle(); + hotKeys = GetDefaultKeyBindings(action); + DefaultHotKeys = GetDefaultKeyBindings(action); if (action is INotifyPropertyChanging notifyPropertyChanging) notifyPropertyChanging.PropertyChanging += Action_PropertyChanging; @@ -128,6 +139,11 @@ public ActionCommand(CommandManager manager, CommandCodes code, IAction action) notifyPropertyChanged.PropertyChanged += Action_PropertyChanged; } + private static HotKeyCollection GetDefaultKeyBindings(IAction action) + { + return new(action.HotKey, action.SecondHotKey, action.ThirdHotKey, action.MediaHotKey); + } + /// public bool CanExecute(object? parameter) { @@ -145,7 +161,8 @@ public Task ExecuteAsync(object? parameter = null) { if (IsExecutable) { - SentrySdk.Metrics.Increment("actions", tags: new Dictionary { { "command", Code.ToString() } }); + // Re-enable when Metris feature is available again + // SentrySdk.Metrics.Increment("actions", tags: new Dictionary { { "command", Code.ToString() } }); return Action.ExecuteAsync(parameter); } @@ -177,6 +194,13 @@ private void Action_PropertyChanging(object? sender, PropertyChangingEventArgs e OnPropertyChanging(nameof(Label)); OnPropertyChanging(nameof(LabelWithHotKey)); OnPropertyChanging(nameof(AutomationName)); + OnPropertyChanging(nameof(ExtendedLabel)); + break; + case nameof(IAction.ExtendedLabel): + OnPropertyChanging(nameof(ExtendedLabel)); + break; + case nameof(IAction.AccessKey): + OnPropertyChanging(nameof(AccessKey)); break; case nameof(IToggleAction.IsOn) when IsToggle: OnPropertyChanging(nameof(IsOn)); @@ -195,6 +219,13 @@ private void Action_PropertyChanged(object? sender, PropertyChangedEventArgs e) OnPropertyChanged(nameof(Label)); OnPropertyChanged(nameof(LabelWithHotKey)); OnPropertyChanged(nameof(AutomationName)); + OnPropertyChanged(nameof(ExtendedLabel)); + break; + case nameof(IAction.ExtendedLabel): + OnPropertyChanged(nameof(ExtendedLabel)); + break; + case nameof(IAction.AccessKey): + OnPropertyChanged(nameof(AccessKey)); break; case nameof(IToggleAction.IsOn) when IsToggle: OnPropertyChanged(nameof(IsOn)); diff --git a/src/Files.App/Data/Commands/HotKey/HotKey.cs b/src/Files.App/Data/Commands/HotKey/HotKey.cs index 2541b7ebe16c..f3ad7362fc60 100644 --- a/src/Files.App/Data/Commands/HotKey/HotKey.cs +++ b/src/Files.App/Data/Commands/HotKey/HotKey.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Collections.Frozen; -using System.Runtime.InteropServices; using System.Text; +using Windows.Win32; using Forms = System.Windows.Forms; namespace Files.App.Data.Commands @@ -278,6 +278,9 @@ public HotKey(Keys key, KeyModifiers modifier = KeyModifiers.None, bool isVisibl /// Humanized code with a format . public static HotKey Parse(string code, bool localized = true) { + if (string.IsNullOrEmpty(code)) + return None; + var key = Keys.None; var modifier = KeyModifiers.None; bool isVisible = true; @@ -305,7 +308,7 @@ public static HotKey Parse(string code, bool localized = true) { parts = [code]; } - else if (parts.Count > 0 && string.IsNullOrEmpty(parts.Last())) + else if (parts.Count > 1 && string.IsNullOrEmpty(parts.Last())) { // If the last part is empty, remove it and add a "+" to the last non-empty part parts.RemoveAt(parts.Count - 1); @@ -365,7 +368,7 @@ private static string GetLocalizedKey(string key) private static string GetLocalizedNumPadKey(string key) { - return "NumPadTypeName".GetLocalizedResource() + " " + key; + return Strings.NumPadTypeName.GetLocalizedResource() + " " + key; } private static string GetKeyCharacter(Forms.Keys key) @@ -374,17 +377,17 @@ private static string GetKeyCharacter(Forms.Keys key) var state = new byte[256]; // Get the current keyboard state - if (!Win32PInvoke.GetKeyboardState(state)) + if (!PInvoke.GetKeyboardState(state)) return buffer.ToString(); // Convert the key to its virtual key code var virtualKey = (uint)key; // Map the virtual key to a scan code - var scanCode = Win32PInvoke.MapVirtualKey(virtualKey, 0); + var scanCode = PInvoke.MapVirtualKey(virtualKey, 0); // Get the active keyboard layout - var keyboardLayout = Win32PInvoke.GetKeyboardLayout(0); + var keyboardLayout = PInvoke.GetKeyboardLayout(0); if (Win32PInvoke.ToUnicodeEx(virtualKey, scanCode, state, buffer, buffer.Capacity, 0, keyboardLayout) > 0) return buffer[^1].ToString(); diff --git a/src/Files.App/Data/Commands/HotKey/HotKeyCollection.cs b/src/Files.App/Data/Commands/HotKey/HotKeyCollection.cs index 02c1352dd686..92d929d46c70 100644 --- a/src/Files.App/Data/Commands/HotKey/HotKeyCollection.cs +++ b/src/Files.App/Data/Commands/HotKey/HotKeyCollection.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Collections.Immutable; @@ -123,8 +123,8 @@ public bool Equals(HotKeyCollection other) private static ImmutableArray Standardize(IEnumerable hotKeys) { return hotKeys - .Distinct() .Where(hotKey => !hotKey.IsNone) + .Distinct() .GroupBy(hotKey => hotKey with { IsVisible = true }) .Select(group => group.OrderBy(hotKey => hotKey.IsVisible).Last()) .ToImmutableArray(); diff --git a/src/Files.App/Data/Commands/HotKey/HotKeyHelpers.cs b/src/Files.App/Data/Commands/HotKey/HotKeyHelpers.cs index fcd9d74391cf..52df5bc6ed47 100644 --- a/src/Files.App/Data/Commands/HotKey/HotKeyHelpers.cs +++ b/src/Files.App/Data/Commands/HotKey/HotKeyHelpers.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Input; using Windows.System; diff --git a/src/Files.App/Data/Commands/HotKey/KeyModifiers.cs b/src/Files.App/Data/Commands/HotKey/KeyModifiers.cs index 0ec78e4841b7..4f936dbceb49 100644 --- a/src/Files.App/Data/Commands/HotKey/KeyModifiers.cs +++ b/src/Files.App/Data/Commands/HotKey/KeyModifiers.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Commands { diff --git a/src/Files.App/Data/Commands/HotKey/Keys.cs b/src/Files.App/Data/Commands/HotKey/Keys.cs index ee4bdc862660..24254efd1666 100644 --- a/src/Files.App/Data/Commands/HotKey/Keys.cs +++ b/src/Files.App/Data/Commands/HotKey/Keys.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.System; diff --git a/src/Files.App/Data/Commands/IRichCommand.cs b/src/Files.App/Data/Commands/IRichCommand.cs index 9a563fb35d75..77c5b601461c 100644 --- a/src/Files.App/Data/Commands/IRichCommand.cs +++ b/src/Files.App/Data/Commands/IRichCommand.cs @@ -1,6 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. +using Files.App.Controls; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; @@ -23,6 +24,11 @@ public interface IRichCommand : ICommand, INotifyPropertyChanging, INotifyProper ///
string Label { get; } + /// + /// Gets the extended label of this command, used to differentiate similar worded labels. Defaults to . + /// + string ExtendedLabel { get; } + /// /// Gets the combined string of the command label and humanized hotkey string. /// @@ -39,7 +45,7 @@ public interface IRichCommand : ICommand, INotifyPropertyChanging, INotifyProper string Description { get; } /// - /// Gets string glyph or opacity icon. + /// Gets string glyph or style. /// RichGlyph Glyph { get; } @@ -54,9 +60,9 @@ public interface IRichCommand : ICommand, INotifyPropertyChanging, INotifyProper FontIcon? FontIcon { get; } /// - /// Gets the style of the opacity icon of this command. + /// Gets the commands style. /// - Style? OpacityStyle { get; } + Style? ThemedIconStyle { get; } /// /// Gets the value that indicates whether the hotkey is customized by user setting. @@ -68,6 +74,11 @@ public interface IRichCommand : ICommand, INotifyPropertyChanging, INotifyProper /// string? HotKeyText { get; } + /// + /// Gets the access key hint for menus. + /// + string AccessKey { get; } + /// /// Gets the hotkey that is assigned to this command. /// @@ -98,6 +109,11 @@ public interface IRichCommand : ICommand, INotifyPropertyChanging, INotifyProper ///
bool IsAccessibleGlobally { get; } + /// + /// Gets the automation ID for UI testing. + /// + string AutomationId { get; } + /// /// Executes the command. /// diff --git a/src/Files.App/Data/Commands/Manager/CommandCodes.cs b/src/Files.App/Data/Commands/Manager/CommandCodes.cs deleted file mode 100644 index 88922ed85360..000000000000 --- a/src/Files.App/Data/Commands/Manager/CommandCodes.cs +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -namespace Files.App.Data.Commands -{ - public enum CommandCodes - { - None, - - // Global - OpenHelp, - ToggleFullScreen, - EnterCompactOverlay, - ExitCompactOverlay, - ToggleCompactOverlay, - Search, - EditPath, - Redo, - Undo, - - // Show - ToggleShowHiddenItems, - ToggleShowFileExtensions, - TogglePreviewPane, - ToggleDetailsPane, - ToggleInfoPane, - ToggleToolbar, - - // File System - CopyItem, - CopyPath, - CopyPathWithQuotes, - CutItem, - PasteItem, - PasteItemToSelection, - DeleteItem, - DeleteItemPermanently, - CreateFolder, - CreateFolderWithSelection, - AddItem, - CreateShortcut, - CreateShortcutFromDialog, - EmptyRecycleBin, - FormatDrive, - RestoreRecycleBin, - RestoreAllRecycleBin, - OpenItem, - OpenItemWithApplicationPicker, - OpenParentFolder, - OpenFileLocation, - RefreshItems, - Rename, - - // Selection - SelectAll, - InvertSelection, - ClearSelection, - ToggleSelect, - - // Share - ShareItem, - - // Start - PinToStart, - UnpinFromStart, - - // Sidebar - PinFolderToSidebar, - UnpinFolderFromSidebar, - - // Backgrounds - SetAsWallpaperBackground, - SetAsSlideshowBackground, - SetAsLockscreenBackground, - SetAsAppBackground, - - // Install - InstallFont, - InstallInfDriver, - InstallCertificate, - - // Run - RunAsAdmin, - RunAsAnotherUser, - RunWithPowershell, - - // Preview Popup - LaunchPreviewPopup, - - // Archives - CompressIntoArchive, - CompressIntoSevenZip, - CompressIntoZip, - DecompressArchive, - DecompressArchiveHere, - DecompressArchiveHereSmart, - DecompressArchiveToChildFolder, - - // Image Manipulation - RotateLeft, - RotateRight, - - // Open - OpenInVSCode, - OpenRepoInVSCode, - OpenProperties, - OpenSettings, - OpenTerminal, - OpenTerminalAsAdmin, - OpenCommandPalette, - EditInNotepad, - - // Layout - LayoutDecreaseSize, - LayoutIncreaseSize, - LayoutDetails, - LayoutList, - LayoutTiles, - LayoutGrid, - LayoutColumns, - LayoutAdaptive, - - // Sort by - SortByName, - SortByDateModified, - SortByDateCreated, - SortBySize, - SortByType, - SortBySyncStatus, - SortByTag, - SortByPath, - SortByOriginalFolder, - SortByDateDeleted, - SortAscending, - SortDescending, - ToggleSortDirection, - SortFoldersFirst, - SortFilesFirst, - SortFilesAndFoldersTogether, - - // Group by - GroupByNone, - GroupByName, - GroupByDateModified, - GroupByDateCreated, - GroupBySize, - GroupByType, - GroupBySyncStatus, - GroupByTag, - GroupByOriginalFolder, - GroupByDateDeleted, - GroupByFolderPath, - GroupByDateModifiedYear, - GroupByDateModifiedMonth, - GroupByDateModifiedDay, - GroupByDateCreatedYear, - GroupByDateCreatedMonth, - GroupByDateCreatedDay, - GroupByDateDeletedYear, - GroupByDateDeletedMonth, - GroupByDateDeletedDay, - GroupAscending, - GroupDescending, - ToggleGroupDirection, - GroupByYear, - GroupByMonth, - ToggleGroupByDateUnit, - - // Navigation - NewWindow, - NewTab, - NavigateBack, - NavigateForward, - NavigateUp, - - // Other - DuplicateCurrentTab, - DuplicateSelectedTab, - CloseTabsToTheLeftCurrent, - CloseTabsToTheLeftSelected, - CloseTabsToTheRightCurrent, - CloseTabsToTheRightSelected, - CloseOtherTabsCurrent, - CloseOtherTabsSelected, - ReopenClosedTab, - PreviousTab, - NextTab, - CloseSelectedTab, - - // Shell Panes - CloseActivePane, - FocusOtherPane, - AddVerticalPane, - AddHorizontalPane, - ArrangePanesVertically, - ArrangePanesHorizontally, - - // OpenInNew - OpenInNewPane, - OpenInNewPaneFromHome, - OpenInNewPaneFromSidebar, - OpenInNewTab, - OpenInNewTabFromHome, - OpenInNewTabFromSidebar, - OpenInNewWindow, - OpenInNewWindowFromHome, - OpenInNewWindowFromSidebar, - - // Play - PlayAll, - - // Git - GitFetch, - GitInit, - GitPull, - GitPush, - GitSync, - - // Tags - OpenAllTaggedItems, - } -} diff --git a/src/Files.App/Data/Commands/Manager/CommandGroup.cs b/src/Files.App/Data/Commands/Manager/CommandGroup.cs new file mode 100644 index 000000000000..7ebb4630903f --- /dev/null +++ b/src/Files.App/Data/Commands/Manager/CommandGroup.cs @@ -0,0 +1,24 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace Files.App.Data.Commands; + +public abstract class CommandGroup +{ + public abstract string DisplayName { get; } + public abstract RichGlyph Glyph { get; } + public abstract string AccessKey { get; } + public abstract IReadOnlyList Commands { get; } + + public object? Icon + => Glyph.ToIcon(); + + public FontIcon? FontIcon + => Glyph.ToFontIcon(); + + public Style? ThemedIconStyle + => Glyph.ToThemedIconStyle(); +} \ No newline at end of file diff --git a/src/Files.App/Data/Commands/Manager/CommandGroupManager.cs b/src/Files.App/Data/Commands/Manager/CommandGroupManager.cs new file mode 100644 index 000000000000..f4878946da79 --- /dev/null +++ b/src/Files.App/Data/Commands/Manager/CommandGroupManager.cs @@ -0,0 +1,62 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Data.Commands; + +internal sealed class ExtractCommandGroup : CommandGroup +{ + public override string DisplayName + => Strings.Extract.GetLocalizedResource(); + + public override RichGlyph Glyph + => new(themedIconStyle: "App.ThemedIcons.Zip"); + + public override string AccessKey + => "Z"; + + public override IReadOnlyList Commands => + [ + CommandCodes.DecompressArchive, + CommandCodes.DecompressArchiveHereSmart, + CommandCodes.DecompressArchiveHere, + CommandCodes.DecompressArchiveToChildFolder, + ]; +} + +internal sealed class SetAsCommandGroup : CommandGroup +{ + public override string DisplayName + => Strings.SetAsBackgroundFlyout.GetLocalizedResource(); + + public override RichGlyph Glyph + => new(themedIconStyle: "App.ThemedIcons.SetWallpaper.16"); + + public override string AccessKey + => "B"; + + public override IReadOnlyList Commands => + [ + CommandCodes.SetAsWallpaperBackground, + CommandCodes.SetAsLockscreenBackground, + CommandCodes.SetAsAppBackground, + ]; +} + +internal sealed class NewItemCommandGroup : CommandGroup +{ + public override string DisplayName + => Strings.BaseLayoutContextFlyoutNew_Label.GetLocalizedResource(); + + public override RichGlyph Glyph + => new(themedIconStyle: "App.ThemedIcons.New.Item"); + + public override string AccessKey + => "W"; + + public override IReadOnlyList Commands => + [ + CommandCodes.CreateFolder, + CommandCodes.CreateFile, + CommandCodes.CreateShortcutFromDialog, + ]; +} \ No newline at end of file diff --git a/src/Files.App/Data/Commands/Manager/CommandManager.cs b/src/Files.App/Data/Commands/Manager/CommandManager.cs index 074ae1619802..0715524809b1 100644 --- a/src/Files.App/Data/Commands/Manager/CommandManager.cs +++ b/src/Files.App/Data/Commands/Manager/CommandManager.cs @@ -1,14 +1,12 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using System.Collections.Frozen; -using System.Collections.Immutable; -using Files.App.Actions; using Microsoft.Extensions.Logging; +using System.Collections.Immutable; namespace Files.App.Data.Commands { - internal sealed class CommandManager : ICommandManager + internal sealed partial class CommandManager : ICommandManager { // Dependency injections @@ -16,204 +14,22 @@ internal sealed class CommandManager : ICommandManager // Fields - private readonly FrozenDictionary commands; + private readonly ImmutableArray _commands; + private ImmutableDictionary _allKeyBindings = new Dictionary().ToImmutableDictionary(); - public IRichCommand this[CommandCodes code] => commands.TryGetValue(code, out var command) ? command : None; - public IRichCommand this[string code] - { - get - { - try - { - return commands[Enum.Parse(code, true)]; - } - catch - { - return None; - } - } - } + public IRichCommand this[CommandCodes code] => _commands[(int)code]; + public IRichCommand this[string code] => Enum.TryParse(code, true, out var codeValue) ? this[codeValue] : None; public IRichCommand this[HotKey hotKey] => _allKeyBindings.TryGetValue(hotKey with { IsVisible = true }, out var command) ? command : _allKeyBindings.TryGetValue(hotKey with { IsVisible = false }, out command) ? command : None; - #region Commands - public IRichCommand None => commands[CommandCodes.None]; - public IRichCommand OpenHelp => commands[CommandCodes.OpenHelp]; - public IRichCommand ToggleFullScreen => commands[CommandCodes.ToggleFullScreen]; - public IRichCommand EnterCompactOverlay => commands[CommandCodes.EnterCompactOverlay]; - public IRichCommand ExitCompactOverlay => commands[CommandCodes.ExitCompactOverlay]; - public IRichCommand ToggleCompactOverlay => commands[CommandCodes.ToggleCompactOverlay]; - public IRichCommand Search => commands[CommandCodes.Search]; - public IRichCommand EditPath => commands[CommandCodes.EditPath]; - public IRichCommand Redo => commands[CommandCodes.Redo]; - public IRichCommand Undo => commands[CommandCodes.Undo]; - public IRichCommand ToggleShowHiddenItems => commands[CommandCodes.ToggleShowHiddenItems]; - public IRichCommand ToggleShowFileExtensions => commands[CommandCodes.ToggleShowFileExtensions]; - public IRichCommand TogglePreviewPane => commands[CommandCodes.TogglePreviewPane]; - public IRichCommand ToggleDetailsPane => commands[CommandCodes.ToggleDetailsPane]; - public IRichCommand ToggleInfoPane => commands[CommandCodes.ToggleInfoPane]; - public IRichCommand ToggleToolbar => commands[CommandCodes.ToggleToolbar]; - public IRichCommand SelectAll => commands[CommandCodes.SelectAll]; - public IRichCommand InvertSelection => commands[CommandCodes.InvertSelection]; - public IRichCommand ClearSelection => commands[CommandCodes.ClearSelection]; - public IRichCommand ToggleSelect => commands[CommandCodes.ToggleSelect]; - public IRichCommand ShareItem => commands[CommandCodes.ShareItem]; - public IRichCommand EmptyRecycleBin => commands[CommandCodes.EmptyRecycleBin]; - public IRichCommand RestoreRecycleBin => commands[CommandCodes.RestoreRecycleBin]; - public IRichCommand RestoreAllRecycleBin => commands[CommandCodes.RestoreAllRecycleBin]; - public IRichCommand RefreshItems => commands[CommandCodes.RefreshItems]; - public IRichCommand Rename => commands[CommandCodes.Rename]; - public IRichCommand CreateShortcut => commands[CommandCodes.CreateShortcut]; - public IRichCommand CreateShortcutFromDialog => commands[CommandCodes.CreateShortcutFromDialog]; - public IRichCommand CreateFolder => commands[CommandCodes.CreateFolder]; - public IRichCommand CreateFolderWithSelection => commands[CommandCodes.CreateFolderWithSelection]; - public IRichCommand AddItem => commands[CommandCodes.AddItem]; - public IRichCommand PinToStart => commands[CommandCodes.PinToStart]; - public IRichCommand UnpinFromStart => commands[CommandCodes.UnpinFromStart]; - public IRichCommand PinFolderToSidebar => commands[CommandCodes.PinFolderToSidebar]; - public IRichCommand UnpinFolderFromSidebar => commands[CommandCodes.UnpinFolderFromSidebar]; - public IRichCommand SetAsWallpaperBackground => commands[CommandCodes.SetAsWallpaperBackground]; - public IRichCommand SetAsSlideshowBackground => commands[CommandCodes.SetAsSlideshowBackground]; - public IRichCommand SetAsLockscreenBackground => commands[CommandCodes.SetAsLockscreenBackground]; - public IRichCommand SetAsAppBackground => commands[CommandCodes.SetAsAppBackground]; - public IRichCommand CopyItem => commands[CommandCodes.CopyItem]; - public IRichCommand CopyPath => commands[CommandCodes.CopyPath]; - public IRichCommand CopyPathWithQuotes => commands[CommandCodes.CopyPathWithQuotes]; - public IRichCommand CutItem => commands[CommandCodes.CutItem]; - public IRichCommand PasteItem => commands[CommandCodes.PasteItem]; - public IRichCommand PasteItemToSelection => commands[CommandCodes.PasteItemToSelection]; - public IRichCommand DeleteItem => commands[CommandCodes.DeleteItem]; - public IRichCommand DeleteItemPermanently => commands[CommandCodes.DeleteItemPermanently]; - public IRichCommand InstallFont => commands[CommandCodes.InstallFont]; - public IRichCommand InstallInfDriver => commands[CommandCodes.InstallInfDriver]; - public IRichCommand InstallCertificate => commands[CommandCodes.InstallCertificate]; - public IRichCommand RunAsAdmin => commands[CommandCodes.RunAsAdmin]; - public IRichCommand RunAsAnotherUser => commands[CommandCodes.RunAsAnotherUser]; - public IRichCommand RunWithPowershell => commands[CommandCodes.RunWithPowershell]; - public IRichCommand LaunchPreviewPopup => commands[CommandCodes.LaunchPreviewPopup]; - public IRichCommand CompressIntoArchive => commands[CommandCodes.CompressIntoArchive]; - public IRichCommand CompressIntoSevenZip => commands[CommandCodes.CompressIntoSevenZip]; - public IRichCommand CompressIntoZip => commands[CommandCodes.CompressIntoZip]; - public IRichCommand DecompressArchive => commands[CommandCodes.DecompressArchive]; - public IRichCommand DecompressArchiveHere => commands[CommandCodes.DecompressArchiveHere]; - public IRichCommand DecompressArchiveHereSmart => commands[CommandCodes.DecompressArchiveHereSmart]; - public IRichCommand DecompressArchiveToChildFolder => commands[CommandCodes.DecompressArchiveToChildFolder]; - public IRichCommand RotateLeft => commands[CommandCodes.RotateLeft]; - public IRichCommand RotateRight => commands[CommandCodes.RotateRight]; - public IRichCommand OpenItem => commands[CommandCodes.OpenItem]; - public IRichCommand OpenItemWithApplicationPicker => commands[CommandCodes.OpenItemWithApplicationPicker]; - public IRichCommand OpenParentFolder => commands[CommandCodes.OpenParentFolder]; - public IRichCommand OpenInVSCode => commands[CommandCodes.OpenInVSCode]; - public IRichCommand OpenRepoInVSCode => commands[CommandCodes.OpenRepoInVSCode]; - public IRichCommand OpenProperties => commands[CommandCodes.OpenProperties]; - public IRichCommand OpenSettings => commands[CommandCodes.OpenSettings]; - public IRichCommand OpenTerminal => commands[CommandCodes.OpenTerminal]; - public IRichCommand OpenTerminalAsAdmin => commands[CommandCodes.OpenTerminalAsAdmin]; - public IRichCommand OpenCommandPalette => commands[CommandCodes.OpenCommandPalette]; - public IRichCommand EditInNotepad => commands[CommandCodes.EditInNotepad]; - public IRichCommand LayoutDecreaseSize => commands[CommandCodes.LayoutDecreaseSize]; - public IRichCommand LayoutIncreaseSize => commands[CommandCodes.LayoutIncreaseSize]; - public IRichCommand LayoutDetails => commands[CommandCodes.LayoutDetails]; - public IRichCommand LayoutList => commands[CommandCodes.LayoutList]; - public IRichCommand LayoutTiles => commands[CommandCodes.LayoutTiles]; - public IRichCommand LayoutGrid => commands[CommandCodes.LayoutGrid]; - public IRichCommand LayoutColumns => commands[CommandCodes.LayoutColumns]; - public IRichCommand LayoutAdaptive => commands[CommandCodes.LayoutAdaptive]; - public IRichCommand SortByName => commands[CommandCodes.SortByName]; - public IRichCommand SortByDateModified => commands[CommandCodes.SortByDateModified]; - public IRichCommand SortByDateCreated => commands[CommandCodes.SortByDateCreated]; - public IRichCommand SortBySize => commands[CommandCodes.SortBySize]; - public IRichCommand SortByType => commands[CommandCodes.SortByType]; - public IRichCommand SortBySyncStatus => commands[CommandCodes.SortBySyncStatus]; - public IRichCommand SortByTag => commands[CommandCodes.SortByTag]; - public IRichCommand SortByPath => commands[CommandCodes.SortByPath]; - public IRichCommand SortByOriginalFolder => commands[CommandCodes.SortByOriginalFolder]; - public IRichCommand SortByDateDeleted => commands[CommandCodes.SortByDateDeleted]; - public IRichCommand SortAscending => commands[CommandCodes.SortAscending]; - public IRichCommand SortDescending => commands[CommandCodes.SortDescending]; - public IRichCommand ToggleSortDirection => commands[CommandCodes.ToggleSortDirection]; - public IRichCommand SortFoldersFirst => commands[CommandCodes.SortFoldersFirst]; - public IRichCommand SortFilesFirst => commands[CommandCodes.SortFilesFirst]; - public IRichCommand SortFilesAndFoldersTogether => commands[CommandCodes.SortFilesAndFoldersTogether]; - public IRichCommand GroupByNone => commands[CommandCodes.GroupByNone]; - public IRichCommand GroupByName => commands[CommandCodes.GroupByName]; - public IRichCommand GroupByDateModified => commands[CommandCodes.GroupByDateModified]; - public IRichCommand GroupByDateCreated => commands[CommandCodes.GroupByDateCreated]; - public IRichCommand GroupBySize => commands[CommandCodes.GroupBySize]; - public IRichCommand GroupByType => commands[CommandCodes.GroupByType]; - public IRichCommand GroupBySyncStatus => commands[CommandCodes.GroupBySyncStatus]; - public IRichCommand GroupByTag => commands[CommandCodes.GroupByTag]; - public IRichCommand GroupByOriginalFolder => commands[CommandCodes.GroupByOriginalFolder]; - public IRichCommand GroupByDateDeleted => commands[CommandCodes.GroupByDateDeleted]; - public IRichCommand GroupByFolderPath => commands[CommandCodes.GroupByFolderPath]; - public IRichCommand GroupByDateModifiedYear => commands[CommandCodes.GroupByDateModifiedYear]; - public IRichCommand GroupByDateModifiedMonth => commands[CommandCodes.GroupByDateModifiedMonth]; - public IRichCommand GroupByDateModifiedDay => commands[CommandCodes.GroupByDateModifiedDay]; - public IRichCommand GroupByDateCreatedYear => commands[CommandCodes.GroupByDateCreatedYear]; - public IRichCommand GroupByDateCreatedMonth => commands[CommandCodes.GroupByDateCreatedMonth]; - public IRichCommand GroupByDateCreatedDay => commands[CommandCodes.GroupByDateCreatedDay]; - public IRichCommand GroupByDateDeletedYear => commands[CommandCodes.GroupByDateDeletedYear]; - public IRichCommand GroupByDateDeletedMonth => commands[CommandCodes.GroupByDateDeletedMonth]; - public IRichCommand GroupByDateDeletedDay => commands[CommandCodes.GroupByDateDeletedDay]; - public IRichCommand GroupAscending => commands[CommandCodes.GroupAscending]; - public IRichCommand GroupDescending => commands[CommandCodes.GroupDescending]; - public IRichCommand ToggleGroupDirection => commands[CommandCodes.ToggleGroupDirection]; - public IRichCommand GroupByYear => commands[CommandCodes.GroupByYear]; - public IRichCommand GroupByMonth => commands[CommandCodes.GroupByMonth]; - public IRichCommand ToggleGroupByDateUnit => commands[CommandCodes.ToggleGroupByDateUnit]; - public IRichCommand NewWindow => commands[CommandCodes.NewWindow]; - public IRichCommand NewTab => commands[CommandCodes.NewTab]; - public IRichCommand FormatDrive => commands[CommandCodes.FormatDrive]; - public IRichCommand NavigateBack => commands[CommandCodes.NavigateBack]; - public IRichCommand NavigateForward => commands[CommandCodes.NavigateForward]; - public IRichCommand NavigateUp => commands[CommandCodes.NavigateUp]; - public IRichCommand DuplicateCurrentTab => commands[CommandCodes.DuplicateCurrentTab]; - public IRichCommand DuplicateSelectedTab => commands[CommandCodes.DuplicateSelectedTab]; - public IRichCommand CloseTabsToTheLeftCurrent => commands[CommandCodes.CloseTabsToTheLeftCurrent]; - public IRichCommand CloseTabsToTheLeftSelected => commands[CommandCodes.CloseTabsToTheLeftSelected]; - public IRichCommand CloseTabsToTheRightCurrent => commands[CommandCodes.CloseTabsToTheRightCurrent]; - public IRichCommand CloseTabsToTheRightSelected => commands[CommandCodes.CloseTabsToTheRightSelected]; - public IRichCommand CloseOtherTabsCurrent => commands[CommandCodes.CloseOtherTabsCurrent]; - public IRichCommand CloseOtherTabsSelected => commands[CommandCodes.CloseOtherTabsSelected]; - public IRichCommand OpenInNewPaneAction => commands[CommandCodes.OpenInNewPane]; - public IRichCommand OpenInNewPaneFromHomeAction => commands[CommandCodes.OpenInNewPaneFromHome]; - public IRichCommand OpenInNewPaneFromSidebarAction => commands[CommandCodes.OpenInNewPaneFromSidebar]; - public IRichCommand OpenInNewTabAction => commands[CommandCodes.OpenInNewTab]; - public IRichCommand OpenInNewTabFromHomeAction => commands[CommandCodes.OpenInNewTabFromHome]; - public IRichCommand OpenInNewTabFromSidebarAction => commands[CommandCodes.OpenInNewTabFromSidebar]; - public IRichCommand OpenInNewWindowAction => commands[CommandCodes.OpenInNewWindow]; - public IRichCommand OpenInNewWindowFromHomeAction => commands[CommandCodes.OpenInNewWindowFromHome]; - public IRichCommand OpenInNewWindowFromSidebarAction => commands[CommandCodes.OpenInNewWindowFromSidebar]; - public IRichCommand ReopenClosedTab => commands[CommandCodes.ReopenClosedTab]; - public IRichCommand PreviousTab => commands[CommandCodes.PreviousTab]; - public IRichCommand NextTab => commands[CommandCodes.NextTab]; - public IRichCommand CloseSelectedTab => commands[CommandCodes.CloseSelectedTab]; - public IRichCommand CloseActivePane => commands[CommandCodes.CloseActivePane]; - public IRichCommand FocusOtherPane => commands[CommandCodes.FocusOtherPane]; - public IRichCommand AddVerticalPane => commands[CommandCodes.AddVerticalPane]; - public IRichCommand AddHorizontalPane => commands[CommandCodes.AddHorizontalPane]; - public IRichCommand ArrangePanesVertically => commands[CommandCodes.ArrangePanesVertically]; - public IRichCommand ArrangePanesHorizontally => commands[CommandCodes.ArrangePanesHorizontally]; - public IRichCommand OpenFileLocation => commands[CommandCodes.OpenFileLocation]; - public IRichCommand PlayAll => commands[CommandCodes.PlayAll]; - public IRichCommand GitFetch => commands[CommandCodes.GitFetch]; - public IRichCommand GitInit => commands[CommandCodes.GitInit]; - public IRichCommand GitPull => commands[CommandCodes.GitPull]; - public IRichCommand GitPush => commands[CommandCodes.GitPush]; - public IRichCommand GitSync => commands[CommandCodes.GitSync]; - public IRichCommand OpenAllTaggedItems => commands[CommandCodes.OpenAllTaggedItems]; - #endregion + public CommandGroups Groups { get; } = new(); public CommandManager() { - commands = CreateActions() - .Select(action => new ActionCommand(this, action.Key, action.Value)) - .Cast() - .Append(new NoneCommand()) - .ToFrozenDictionary(command => command.Code); + _commands = CreateCommands(); ActionsSettingsService.PropertyChanged += (s, e) => { OverwriteKeyBindings(); }; @@ -223,192 +39,25 @@ public CommandManager() IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public IEnumerator GetEnumerator() => - (commands.Values as IEnumerable).GetEnumerator(); - - private static Dictionary CreateActions() => new() - { - [CommandCodes.OpenHelp] = new OpenHelpAction(), - [CommandCodes.ToggleFullScreen] = new ToggleFullScreenAction(), - [CommandCodes.EnterCompactOverlay] = new EnterCompactOverlayAction(), - [CommandCodes.ExitCompactOverlay] = new ExitCompactOverlayAction(), - [CommandCodes.ToggleCompactOverlay] = new ToggleCompactOverlayAction(), - [CommandCodes.Search] = new SearchAction(), - [CommandCodes.EditPath] = new EditPathAction(), - [CommandCodes.Redo] = new RedoAction(), - [CommandCodes.Undo] = new UndoAction(), - [CommandCodes.ToggleShowHiddenItems] = new ToggleShowHiddenItemsAction(), - [CommandCodes.ToggleShowFileExtensions] = new ToggleShowFileExtensionsAction(), - [CommandCodes.TogglePreviewPane] = new TogglePreviewPaneAction(), - [CommandCodes.ToggleDetailsPane] = new ToggleDetailsPaneAction(), - [CommandCodes.ToggleInfoPane] = new ToggleInfoPaneAction(), - [CommandCodes.ToggleToolbar] = new ToggleToolbarAction(), - [CommandCodes.SelectAll] = new SelectAllAction(), - [CommandCodes.InvertSelection] = new InvertSelectionAction(), - [CommandCodes.ClearSelection] = new ClearSelectionAction(), - [CommandCodes.ToggleSelect] = new ToggleSelectAction(), - [CommandCodes.ShareItem] = new ShareItemAction(), - [CommandCodes.EmptyRecycleBin] = new EmptyRecycleBinAction(), - [CommandCodes.RestoreRecycleBin] = new RestoreRecycleBinAction(), - [CommandCodes.RestoreAllRecycleBin] = new RestoreAllRecycleBinAction(), - [CommandCodes.RefreshItems] = new RefreshItemsAction(), - [CommandCodes.Rename] = new RenameAction(), - [CommandCodes.CreateShortcut] = new CreateShortcutAction(), - [CommandCodes.CreateShortcutFromDialog] = new CreateShortcutFromDialogAction(), - [CommandCodes.CreateFolder] = new CreateFolderAction(), - [CommandCodes.CreateFolderWithSelection] = new CreateFolderWithSelectionAction(), - [CommandCodes.AddItem] = new AddItemAction(), - [CommandCodes.PinToStart] = new PinToStartAction(), - [CommandCodes.UnpinFromStart] = new UnpinFromStartAction(), - [CommandCodes.PinFolderToSidebar] = new PinFolderToSidebarAction(), - [CommandCodes.UnpinFolderFromSidebar] = new UnpinFolderFromSidebarAction(), - [CommandCodes.SetAsWallpaperBackground] = new SetAsWallpaperBackgroundAction(), - [CommandCodes.SetAsSlideshowBackground] = new SetAsSlideshowBackgroundAction(), - [CommandCodes.SetAsLockscreenBackground] = new SetAsLockscreenBackgroundAction(), - [CommandCodes.SetAsAppBackground] = new SetAsAppBackgroundAction(), - [CommandCodes.CopyItem] = new CopyItemAction(), - [CommandCodes.CopyPath] = new CopyPathAction(), - [CommandCodes.CopyPathWithQuotes] = new CopyPathWithQuotesAction(), - [CommandCodes.CutItem] = new CutItemAction(), - [CommandCodes.PasteItem] = new PasteItemAction(), - [CommandCodes.PasteItemToSelection] = new PasteItemToSelectionAction(), - [CommandCodes.DeleteItem] = new DeleteItemAction(), - [CommandCodes.DeleteItemPermanently] = new DeleteItemPermanentlyAction(), - [CommandCodes.InstallFont] = new InstallFontAction(), - [CommandCodes.InstallInfDriver] = new InstallInfDriverAction(), - [CommandCodes.InstallCertificate] = new InstallCertificateAction(), - [CommandCodes.RunAsAdmin] = new RunAsAdminAction(), - [CommandCodes.RunAsAnotherUser] = new RunAsAnotherUserAction(), - [CommandCodes.RunWithPowershell] = new RunWithPowershellAction(), - [CommandCodes.LaunchPreviewPopup] = new LaunchPreviewPopupAction(), - [CommandCodes.CompressIntoArchive] = new CompressIntoArchiveAction(), - [CommandCodes.CompressIntoSevenZip] = new CompressIntoSevenZipAction(), - [CommandCodes.CompressIntoZip] = new CompressIntoZipAction(), - [CommandCodes.DecompressArchive] = new DecompressArchive(), - [CommandCodes.DecompressArchiveHere] = new DecompressArchiveHere(), - [CommandCodes.DecompressArchiveHereSmart] = new DecompressArchiveHereSmart(), - [CommandCodes.DecompressArchiveToChildFolder] = new DecompressArchiveToChildFolderAction(), - [CommandCodes.RotateLeft] = new RotateLeftAction(), - [CommandCodes.RotateRight] = new RotateRightAction(), - [CommandCodes.OpenItem] = new OpenItemAction(), - [CommandCodes.OpenItemWithApplicationPicker] = new OpenItemWithApplicationPickerAction(), - [CommandCodes.OpenParentFolder] = new OpenParentFolderAction(), - [CommandCodes.OpenInVSCode] = new OpenInVSCodeAction(), - [CommandCodes.OpenRepoInVSCode] = new OpenRepoInVSCodeAction(), - [CommandCodes.OpenProperties] = new OpenPropertiesAction(), - [CommandCodes.OpenSettings] = new OpenSettingsAction(), - [CommandCodes.OpenTerminal] = new OpenTerminalAction(), - [CommandCodes.OpenTerminalAsAdmin] = new OpenTerminalAsAdminAction(), - [CommandCodes.OpenCommandPalette] = new OpenCommandPaletteAction(), - [CommandCodes.EditInNotepad] = new EditInNotepadAction(), - [CommandCodes.LayoutDecreaseSize] = new LayoutDecreaseSizeAction(), - [CommandCodes.LayoutIncreaseSize] = new LayoutIncreaseSizeAction(), - [CommandCodes.LayoutDetails] = new LayoutDetailsAction(), - [CommandCodes.LayoutList] = new LayoutListAction(), - [CommandCodes.LayoutTiles] = new LayoutTilesAction(), - [CommandCodes.LayoutGrid] = new LayoutGridAction(), - [CommandCodes.LayoutColumns] = new LayoutColumnsAction(), - [CommandCodes.LayoutAdaptive] = new LayoutAdaptiveAction(), - [CommandCodes.SortByName] = new SortByNameAction(), - [CommandCodes.SortByDateModified] = new SortByDateModifiedAction(), - [CommandCodes.SortByDateCreated] = new SortByDateCreatedAction(), - [CommandCodes.SortBySize] = new SortBySizeAction(), - [CommandCodes.SortByType] = new SortByTypeAction(), - [CommandCodes.SortBySyncStatus] = new SortBySyncStatusAction(), - [CommandCodes.SortByTag] = new SortByTagAction(), - [CommandCodes.SortByPath] = new SortByPathAction(), - [CommandCodes.SortByOriginalFolder] = new SortByOriginalFolderAction(), - [CommandCodes.SortByDateDeleted] = new SortByDateDeletedAction(), - [CommandCodes.SortAscending] = new SortAscendingAction(), - [CommandCodes.SortDescending] = new SortDescendingAction(), - [CommandCodes.ToggleSortDirection] = new ToggleSortDirectionAction(), - [CommandCodes.SortFoldersFirst] = new SortFoldersFirstAction(), - [CommandCodes.SortFilesFirst] = new SortFilesFirstAction(), - [CommandCodes.SortFilesAndFoldersTogether] = new SortFilesAndFoldersTogetherAction(), - [CommandCodes.GroupByNone] = new GroupByNoneAction(), - [CommandCodes.GroupByName] = new GroupByNameAction(), - [CommandCodes.GroupByDateModified] = new GroupByDateModifiedAction(), - [CommandCodes.GroupByDateCreated] = new GroupByDateCreatedAction(), - [CommandCodes.GroupBySize] = new GroupBySizeAction(), - [CommandCodes.GroupByType] = new GroupByTypeAction(), - [CommandCodes.GroupBySyncStatus] = new GroupBySyncStatusAction(), - [CommandCodes.GroupByTag] = new GroupByTagAction(), - [CommandCodes.GroupByOriginalFolder] = new GroupByOriginalFolderAction(), - [CommandCodes.GroupByDateDeleted] = new GroupByDateDeletedAction(), - [CommandCodes.GroupByFolderPath] = new GroupByFolderPathAction(), - [CommandCodes.GroupByDateModifiedYear] = new GroupByDateModifiedYearAction(), - [CommandCodes.GroupByDateModifiedMonth] = new GroupByDateModifiedMonthAction(), - [CommandCodes.GroupByDateModifiedDay] = new GroupByDateModifiedDayAction(), - [CommandCodes.GroupByDateCreatedYear] = new GroupByDateCreatedYearAction(), - [CommandCodes.GroupByDateCreatedMonth] = new GroupByDateCreatedMonthAction(), - [CommandCodes.GroupByDateCreatedDay] = new GroupByDateCreatedDayAction(), - [CommandCodes.GroupByDateDeletedYear] = new GroupByDateDeletedYearAction(), - [CommandCodes.GroupByDateDeletedMonth] = new GroupByDateDeletedMonthAction(), - [CommandCodes.GroupByDateDeletedDay] = new GroupByDateDeletedDayAction(), - [CommandCodes.GroupAscending] = new GroupAscendingAction(), - [CommandCodes.GroupDescending] = new GroupDescendingAction(), - [CommandCodes.ToggleGroupDirection] = new ToggleGroupDirectionAction(), - [CommandCodes.GroupByYear] = new GroupByYearAction(), - [CommandCodes.GroupByMonth] = new GroupByMonthAction(), - [CommandCodes.ToggleGroupByDateUnit] = new ToggleGroupByDateUnitAction(), - [CommandCodes.NewWindow] = new NewWindowAction(), - [CommandCodes.NewTab] = new NewTabAction(), - [CommandCodes.FormatDrive] = new FormatDriveAction(), - [CommandCodes.NavigateBack] = new NavigateBackAction(), - [CommandCodes.NavigateForward] = new NavigateForwardAction(), - [CommandCodes.NavigateUp] = new NavigateUpAction(), - [CommandCodes.DuplicateCurrentTab] = new DuplicateCurrentTabAction(), - [CommandCodes.DuplicateSelectedTab] = new DuplicateSelectedTabAction(), - [CommandCodes.CloseTabsToTheLeftCurrent] = new CloseTabsToTheLeftCurrentAction(), - [CommandCodes.CloseTabsToTheLeftSelected] = new CloseTabsToTheLeftSelectedAction(), - [CommandCodes.CloseTabsToTheRightCurrent] = new CloseTabsToTheRightCurrentAction(), - [CommandCodes.CloseTabsToTheRightSelected] = new CloseTabsToTheRightSelectedAction(), - [CommandCodes.CloseOtherTabsCurrent] = new CloseOtherTabsCurrentAction(), - [CommandCodes.CloseOtherTabsSelected] = new CloseOtherTabsSelectedAction(), - [CommandCodes.OpenInNewPane] = new OpenInNewPaneAction(), - [CommandCodes.OpenInNewPaneFromHome] = new OpenInNewPaneFromHomeAction(), - [CommandCodes.OpenInNewPaneFromSidebar] = new OpenInNewPaneFromSidebarAction(), - [CommandCodes.OpenInNewTab] = new OpenInNewTabAction(), - [CommandCodes.OpenInNewTabFromHome] = new OpenInNewTabFromHomeAction(), - [CommandCodes.OpenInNewTabFromSidebar] = new OpenInNewTabFromSidebarAction(), - [CommandCodes.OpenInNewWindow] = new OpenInNewWindowAction(), - [CommandCodes.OpenInNewWindowFromHome] = new OpenInNewWindowFromHomeAction(), - [CommandCodes.OpenInNewWindowFromSidebar] = new OpenInNewWindowFromSidebarAction(), - [CommandCodes.ReopenClosedTab] = new ReopenClosedTabAction(), - [CommandCodes.PreviousTab] = new PreviousTabAction(), - [CommandCodes.NextTab] = new NextTabAction(), - [CommandCodes.CloseSelectedTab] = new CloseSelectedTabAction(), - [CommandCodes.CloseActivePane] = new CloseActivePaneAction(), - [CommandCodes.FocusOtherPane] = new FocusOtherPaneAction(), - [CommandCodes.AddVerticalPane] = new AddVerticalPaneAction(), - [CommandCodes.AddHorizontalPane] = new AddHorizontalPaneAction(), - [CommandCodes.ArrangePanesVertically] = new ArrangePanesVerticallyAction(), - [CommandCodes.ArrangePanesHorizontally] = new ArrangePanesHorizontallyAction(), - [CommandCodes.OpenFileLocation] = new OpenFileLocationAction(), - [CommandCodes.PlayAll] = new PlayAllAction(), - [CommandCodes.GitFetch] = new GitFetchAction(), - [CommandCodes.GitInit] = new GitInitAction(), - [CommandCodes.GitPull] = new GitPullAction(), - [CommandCodes.GitPush] = new GitPushAction(), - [CommandCodes.GitSync] = new GitSyncAction(), - [CommandCodes.OpenAllTaggedItems] = new OpenAllTaggedActions(), - }; + (_commands as IEnumerable).GetEnumerator(); /// /// Replaces default key binding collection with customized one(s) if exists. /// private void OverwriteKeyBindings() { - var allCommands = commands.Values.OfType(); + var allCommands = _commands.OfType(); if (ActionsSettingsService.ActionsV2 is null) { - allCommands.ForEach(x => x.RestoreKeyBindings()); + RestoreKeyBindings(allCommands); } else { foreach (var command in allCommands) { - var customizedKeyBindings = ActionsSettingsService.ActionsV2.FindAll(x => x.CommandCode == command.Code.ToString()); + string code = command.Code.ToString(); + var customizedKeyBindings = ActionsSettingsService.ActionsV2.FindAll(x => x.CommandCode == code); if (customizedKeyBindings.IsEmpty()) { @@ -431,7 +80,7 @@ private void OverwriteKeyBindings() try { // Set collection of a set of command code and key bindings to dictionary - _allKeyBindings = commands.Values + _allKeyBindings = _commands .SelectMany(command => command.HotKeys, (command, hotKey) => (Command: command, HotKey: hotKey)) .ToImmutableDictionary(item => item.HotKey, item => item.Command); } @@ -440,7 +89,7 @@ private void OverwriteKeyBindings() // The keys are not necessarily all different because they can be set manually in text editor // ISSUE: https://github.com/files-community/Files/issues/15331 - var flat = commands.Values.SelectMany(x => x.HotKeys).Select(x => x.LocalizedLabel); + var flat = _commands.SelectMany(x => x.HotKeys).Select(x => x.LocalizedLabel); var duplicates = flat.GroupBy(x => x).Where(x => x.Count() > 1).Select(group => group.Key); foreach (var item in duplicates) @@ -450,7 +99,7 @@ private void OverwriteKeyBindings() var occurrences = allCommands.Where(x => x.HotKeys.Select(x => x.LocalizedLabel).Contains(item)); // Restore the defaults for all occurrences in our cache - occurrences.ForEach(x => x.RestoreKeyBindings()); + RestoreKeyBindings(occurrences); // Get all customized key bindings from user settings json var actions = @@ -467,18 +116,18 @@ ActionsSettingsService.ActionsV2 is not null } // Set collection of a set of command code and key bindings to dictionary - _allKeyBindings = commands.Values + _allKeyBindings = _commands .SelectMany(command => command.HotKeys, (command, hotKey) => (Command: command, HotKey: hotKey)) .ToImmutableDictionary(item => item.HotKey, item => item.Command); - App.Logger.LogWarning(ex, "The app found some keys in different commands are duplicated and are using default key bindings for those commands."); + App.Logger.LogInformation(ex, "The app found some keys in different commands are duplicated and are using default key bindings for those commands."); } catch (Exception ex) { - allCommands.ForEach(x => x.RestoreKeyBindings()); + RestoreKeyBindings(allCommands); // Set collection of a set of command code and key bindings to dictionary - _allKeyBindings = commands.Values + _allKeyBindings = _commands .SelectMany(command => command.HotKeys, (command, hotKey) => (Command: command, HotKey: hotKey)) .ToImmutableDictionary(item => item.HotKey, item => item.Command); @@ -486,9 +135,10 @@ ActionsSettingsService.ActionsV2 is not null } } - public static HotKeyCollection GetDefaultKeyBindings(IAction action) + private static void RestoreKeyBindings(IEnumerable allCommands) { - return new(action.HotKey, action.SecondHotKey, action.ThirdHotKey, action.MediaHotKey); + foreach (var command in allCommands) + command.RestoreKeyBindings(); } } } diff --git a/src/Files.App/Data/Commands/Manager/ICommandManager.cs b/src/Files.App/Data/Commands/Manager/ICommandManager.cs deleted file mode 100644 index dcbe0bc8c010..000000000000 --- a/src/Files.App/Data/Commands/Manager/ICommandManager.cs +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -namespace Files.App.Data.Commands -{ - /// - /// Represents a collection of and provides manager classes. - /// - public interface ICommandManager : IEnumerable - { - IRichCommand this[CommandCodes code] { get; } - IRichCommand this[string code] { get; } - IRichCommand this[HotKey customHotKey] { get; } - - IRichCommand None { get; } - - IRichCommand OpenHelp { get; } - IRichCommand ToggleFullScreen { get; } - IRichCommand EnterCompactOverlay { get; } - IRichCommand ExitCompactOverlay { get; } - IRichCommand ToggleCompactOverlay { get; } - IRichCommand Search { get; } - IRichCommand EditPath { get; } - IRichCommand Redo { get; } - IRichCommand Undo { get; } - - IRichCommand ToggleShowHiddenItems { get; } - IRichCommand ToggleShowFileExtensions { get; } - IRichCommand TogglePreviewPane { get; } - IRichCommand ToggleDetailsPane { get; } - IRichCommand ToggleInfoPane { get; } - IRichCommand ToggleToolbar { get; } - - IRichCommand CopyItem { get; } - IRichCommand CopyPath { get; } - IRichCommand CopyPathWithQuotes { get; } - IRichCommand CutItem { get; } - IRichCommand PasteItem { get; } - IRichCommand PasteItemToSelection { get; } - IRichCommand DeleteItem { get; } - IRichCommand DeleteItemPermanently { get; } - IRichCommand SelectAll { get; } - IRichCommand InvertSelection { get; } - IRichCommand ClearSelection { get; } - IRichCommand ToggleSelect { get; } - IRichCommand ShareItem { get; } - IRichCommand CreateFolder { get; } - IRichCommand CreateFolderWithSelection { get; } - IRichCommand AddItem { get; } - IRichCommand CreateShortcut { get; } - IRichCommand CreateShortcutFromDialog { get; } - IRichCommand EmptyRecycleBin { get; } - IRichCommand RestoreRecycleBin { get; } - IRichCommand RestoreAllRecycleBin { get; } - IRichCommand FormatDrive { get; } - IRichCommand OpenItem { get; } - IRichCommand OpenItemWithApplicationPicker { get; } - IRichCommand OpenParentFolder { get; } - IRichCommand OpenFileLocation { get; } - IRichCommand RefreshItems { get; } - IRichCommand Rename { get; } - - IRichCommand PinToStart { get; } - IRichCommand UnpinFromStart { get; } - IRichCommand PinFolderToSidebar { get; } - IRichCommand UnpinFolderFromSidebar { get; } - - IRichCommand SetAsWallpaperBackground { get; } - IRichCommand SetAsSlideshowBackground { get; } - IRichCommand SetAsLockscreenBackground { get; } - IRichCommand SetAsAppBackground { get; } - - IRichCommand InstallFont { get; } - IRichCommand InstallInfDriver { get; } - IRichCommand InstallCertificate { get; } - - IRichCommand RunAsAdmin { get; } - IRichCommand RunAsAnotherUser { get; } - IRichCommand RunWithPowershell { get; } - - IRichCommand LaunchPreviewPopup { get; } - - IRichCommand CompressIntoArchive { get; } - IRichCommand CompressIntoSevenZip { get; } - IRichCommand CompressIntoZip { get; } - IRichCommand DecompressArchive { get; } - IRichCommand DecompressArchiveHere { get; } - IRichCommand DecompressArchiveHereSmart { get; } - IRichCommand DecompressArchiveToChildFolder { get; } - - IRichCommand RotateLeft { get; } - IRichCommand RotateRight { get; } - - IRichCommand OpenInVSCode { get; } - IRichCommand OpenRepoInVSCode { get; } - IRichCommand OpenProperties { get; } - IRichCommand OpenSettings { get; } - IRichCommand OpenTerminal { get; } - IRichCommand OpenTerminalAsAdmin { get; } - IRichCommand OpenCommandPalette { get; } - IRichCommand EditInNotepad { get; } - - IRichCommand LayoutDecreaseSize { get; } - IRichCommand LayoutIncreaseSize { get; } - IRichCommand LayoutDetails { get; } - IRichCommand LayoutList { get; } - IRichCommand LayoutTiles { get; } - IRichCommand LayoutGrid { get; } - IRichCommand LayoutColumns { get; } - IRichCommand LayoutAdaptive { get; } - - IRichCommand SortByName { get; } - IRichCommand SortByDateModified { get; } - IRichCommand SortByDateCreated { get; } - IRichCommand SortBySize { get; } - IRichCommand SortByType { get; } - IRichCommand SortBySyncStatus { get; } - IRichCommand SortByTag { get; } - IRichCommand SortByPath { get; } - IRichCommand SortByOriginalFolder { get; } - IRichCommand SortByDateDeleted { get; } - IRichCommand SortAscending { get; } - IRichCommand SortDescending { get; } - IRichCommand ToggleSortDirection { get; } - IRichCommand SortFoldersFirst { get; } - IRichCommand SortFilesFirst { get; } - IRichCommand SortFilesAndFoldersTogether { get; } - - IRichCommand GroupByNone { get; } - IRichCommand GroupByName { get; } - IRichCommand GroupByDateModified { get; } - IRichCommand GroupByDateCreated { get; } - IRichCommand GroupBySize { get; } - IRichCommand GroupByType { get; } - IRichCommand GroupBySyncStatus { get; } - IRichCommand GroupByTag { get; } - IRichCommand GroupByOriginalFolder { get; } - IRichCommand GroupByDateDeleted { get; } - IRichCommand GroupByFolderPath { get; } - IRichCommand GroupByDateModifiedYear { get; } - IRichCommand GroupByDateModifiedMonth { get; } - IRichCommand GroupByDateModifiedDay { get; } - IRichCommand GroupByDateCreatedYear { get; } - IRichCommand GroupByDateCreatedMonth { get; } - IRichCommand GroupByDateCreatedDay { get; } - IRichCommand GroupByDateDeletedYear { get; } - IRichCommand GroupByDateDeletedMonth { get; } - IRichCommand GroupByDateDeletedDay { get; } - IRichCommand GroupAscending { get; } - IRichCommand GroupDescending { get; } - IRichCommand ToggleGroupDirection { get; } - IRichCommand GroupByYear { get; } - IRichCommand GroupByMonth { get; } - IRichCommand ToggleGroupByDateUnit { get; } - - IRichCommand NewWindow { get; } - IRichCommand NewTab { get; } - IRichCommand NavigateBack { get; } - IRichCommand NavigateForward { get; } - IRichCommand NavigateUp { get; } - - IRichCommand DuplicateCurrentTab { get; } - IRichCommand DuplicateSelectedTab { get; } - IRichCommand CloseTabsToTheLeftCurrent { get; } - IRichCommand CloseTabsToTheLeftSelected { get; } - IRichCommand CloseTabsToTheRightCurrent { get; } - IRichCommand CloseTabsToTheRightSelected { get; } - IRichCommand CloseOtherTabsCurrent { get; } - IRichCommand CloseOtherTabsSelected { get; } - IRichCommand ReopenClosedTab { get; } - IRichCommand PreviousTab { get; } - IRichCommand NextTab { get; } - IRichCommand CloseSelectedTab { get; } - - IRichCommand CloseActivePane { get; } - IRichCommand FocusOtherPane { get; } - IRichCommand AddVerticalPane { get; } - IRichCommand AddHorizontalPane { get; } - IRichCommand ArrangePanesVertically { get; } - IRichCommand ArrangePanesHorizontally { get; } - - IRichCommand OpenInNewPaneAction { get; } - IRichCommand OpenInNewPaneFromHomeAction { get; } - IRichCommand OpenInNewPaneFromSidebarAction { get; } - IRichCommand OpenInNewTabAction { get; } - IRichCommand OpenInNewTabFromHomeAction { get; } - IRichCommand OpenInNewTabFromSidebarAction { get; } - IRichCommand OpenInNewWindowAction { get; } - IRichCommand OpenInNewWindowFromHomeAction { get; } - IRichCommand OpenInNewWindowFromSidebarAction { get; } - - IRichCommand PlayAll { get; } - - IRichCommand GitFetch { get; } - IRichCommand GitInit { get; } - IRichCommand GitPull { get; } - IRichCommand GitPush { get; } - IRichCommand GitSync { get; } - - IRichCommand OpenAllTaggedItems { get; } - } -} diff --git a/src/Files.App/Data/Commands/Manager/IModifiableCommandManager.cs b/src/Files.App/Data/Commands/Manager/IModifiableCommandManager.cs index 47dc04ae72c6..8dc9e6c74efb 100644 --- a/src/Files.App/Data/Commands/Manager/IModifiableCommandManager.cs +++ b/src/Files.App/Data/Commands/Manager/IModifiableCommandManager.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Commands { @@ -11,5 +11,6 @@ public interface IModifiableCommandManager : IEnumerable IRichCommand PasteItem { get; } IRichCommand DeleteItem { get; } + IRichCommand OpenProperties { get; } } } diff --git a/src/Files.App/Data/Commands/Manager/ModifiableCommandManager.cs b/src/Files.App/Data/Commands/Manager/ModifiableCommandManager.cs index 2ef98be35bb2..404ce87a9a5c 100644 --- a/src/Files.App/Data/Commands/Manager/ModifiableCommandManager.cs +++ b/src/Files.App/Data/Commands/Manager/ModifiableCommandManager.cs @@ -1,12 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Collections.Frozen; -using System.Collections.Immutable; namespace Files.App.Data.Commands { - internal sealed class ModifiableCommandManager : IModifiableCommandManager + internal sealed partial class ModifiableCommandManager : IModifiableCommandManager { private static readonly ICommandManager Commands = Ioc.Default.GetRequiredService(); @@ -17,6 +16,7 @@ internal sealed class ModifiableCommandManager : IModifiableCommandManager public IRichCommand None => ModifiableCommands[CommandCodes.None]; public IRichCommand PasteItem => ModifiableCommands[CommandCodes.PasteItem]; public IRichCommand DeleteItem => ModifiableCommands[CommandCodes.DeleteItem]; + public IRichCommand OpenProperties => ModifiableCommands[CommandCodes.OpenProperties]; public ModifiableCommandManager() { @@ -35,6 +35,9 @@ public ModifiableCommandManager() [CommandCodes.DeleteItem] = new ModifiableCommand(Commands.DeleteItem, new() { { KeyModifiers.Shift, Commands.DeleteItemPermanently } }), + [CommandCodes.OpenProperties] = new ModifiableCommand(Commands.OpenProperties, new() { + { KeyModifiers.Shift, Commands.OpenClassicProperties } + }), }.ToFrozenDictionary(); } } diff --git a/src/Files.App/Data/Commands/ModifiableCommand.cs b/src/Files.App/Data/Commands/ModifiableCommand.cs index 5564d6d9a8eb..4819190aec3b 100644 --- a/src/Files.App/Data/Commands/ModifiableCommand.cs +++ b/src/Files.App/Data/Commands/ModifiableCommand.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.Actions; using Microsoft.UI.Xaml; @@ -10,7 +10,7 @@ namespace Files.App.Data.Commands { [DebuggerDisplay("Command {Code} (Modifiable)")] - internal sealed class ModifiableCommand : ObservableObject, IRichCommand + internal sealed partial class ModifiableCommand : ObservableObject, IRichCommand { public event EventHandler? CanExecuteChanged; @@ -23,6 +23,9 @@ internal sealed class ModifiableCommand : ObservableObject, IRichCommand /// public string Label => BaseCommand.Label; + /// + public string ExtendedLabel => BaseCommand.ExtendedLabel; + /// public string LabelWithHotKey => BaseCommand.LabelWithHotKey; @@ -42,7 +45,7 @@ internal sealed class ModifiableCommand : ObservableObject, IRichCommand public FontIcon? FontIcon => BaseCommand.FontIcon; /// - public Style? OpacityStyle => BaseCommand.OpacityStyle; + public Style? ThemedIconStyle => BaseCommand.ThemedIconStyle; /// public bool IsCustomHotKeys => BaseCommand.IsCustomHotKeys; @@ -50,6 +53,9 @@ internal sealed class ModifiableCommand : ObservableObject, IRichCommand /// public string? HotKeyText => BaseCommand.HotKeyText; + /// + public string AccessKey => BaseCommand.AccessKey; + /// public HotKeyCollection HotKeys { @@ -79,6 +85,10 @@ public bool IsExecutable public bool IsAccessibleGlobally => BaseCommand.IsAccessibleGlobally; + /// + public string AutomationId + => BaseCommand.AutomationId; + public ModifiableCommand(IRichCommand baseCommand, Dictionary modifiedCommands) { BaseCommand = baseCommand; @@ -130,6 +140,13 @@ private void Action_PropertyChanging(object? sender, PropertyChangingEventArgs e OnPropertyChanging(nameof(Label)); OnPropertyChanging(nameof(LabelWithHotKey)); OnPropertyChanging(nameof(AutomationName)); + OnPropertyChanging(nameof(ExtendedLabel)); + break; + case nameof(IAction.ExtendedLabel): + OnPropertyChanging(nameof(ExtendedLabel)); + break; + case nameof(IAction.AccessKey): + OnPropertyChanging(nameof(AccessKey)); break; case nameof(IToggleAction.IsOn) when IsToggle: OnPropertyChanging(nameof(IsOn)); @@ -148,6 +165,13 @@ private void Action_PropertyChanged(object? sender, PropertyChangedEventArgs e) OnPropertyChanged(nameof(Label)); OnPropertyChanged(nameof(LabelWithHotKey)); OnPropertyChanged(nameof(AutomationName)); + OnPropertyChanged(nameof(ExtendedLabel)); + break; + case nameof(IAction.ExtendedLabel): + OnPropertyChanged(nameof(ExtendedLabel)); + break; + case nameof(IAction.AccessKey): + OnPropertyChanged(nameof(AccessKey)); break; case nameof(IToggleAction.IsOn) when IsToggle: OnPropertyChanged(nameof(IsOn)); diff --git a/src/Files.App/Data/Commands/NoneCommand.cs b/src/Files.App/Data/Commands/NoneCommand.cs index 18aba72f8dea..4b70ebefc73e 100644 --- a/src/Files.App/Data/Commands/NoneCommand.cs +++ b/src/Files.App/Data/Commands/NoneCommand.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -8,7 +8,7 @@ namespace Files.App.Data.Commands { [DebuggerDisplay("Command None")] - internal sealed class NoneCommand : IRichCommand + internal sealed partial class NoneCommand : IRichCommand { public event EventHandler? CanExecuteChanged { add { } remove { } } public event PropertyChangingEventHandler? PropertyChanging { add { } remove { } } @@ -22,6 +22,10 @@ public CommandCodes Code public string Label => string.Empty; + /// + public string ExtendedLabel + => string.Empty; + /// public string LabelWithHotKey => string.Empty; @@ -47,7 +51,7 @@ public FontIcon? FontIcon => null; /// - public Style? OpacityStyle + public Style? ThemedIconStyle => null; /// @@ -58,6 +62,10 @@ public bool IsCustomHotKeys public string? HotKeyText => null; + /// + public string AccessKey + => string.Empty; + /// public HotKeyCollection HotKeys { @@ -91,6 +99,10 @@ public bool IsExecutable public bool IsAccessibleGlobally => false; + /// + public string AutomationId + => string.Empty; + public bool CanExecute(object? parameter) => false; diff --git a/src/Files.App/Data/Commands/RichGlyph.cs b/src/Files.App/Data/Commands/RichGlyph.cs index 3de68ada072b..be6632335ada 100644 --- a/src/Files.App/Data/Commands/RichGlyph.cs +++ b/src/Files.App/Data/Commands/RichGlyph.cs @@ -1,6 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. +using Files.App.Controls; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media; @@ -15,27 +16,27 @@ public readonly struct RichGlyph public string BaseGlyph { get; } public string FontFamily { get; } - public string OpacityStyle { get; } + public string ThemedIconStyle { get; } - public RichGlyph(string baseGlyph = "", string fontFamily = "", string opacityStyle = "") + public RichGlyph(string baseGlyph = "", string fontFamily = "", string themedIconStyle = "") { BaseGlyph = baseGlyph; FontFamily = fontFamily; - OpacityStyle = opacityStyle; + ThemedIconStyle = themedIconStyle; - IsNone = string.IsNullOrEmpty(baseGlyph) && string.IsNullOrEmpty(fontFamily) && string.IsNullOrEmpty(opacityStyle); + IsNone = string.IsNullOrEmpty(baseGlyph) && string.IsNullOrEmpty(fontFamily) && string.IsNullOrEmpty(themedIconStyle); } - public void Deconstruct(out string baseGlyph, out string fontFamily, out string opacityStyle) + public void Deconstruct(out string baseGlyph, out string fontFamily, out string themedIconStyle) { baseGlyph = BaseGlyph; fontFamily = FontFamily; - opacityStyle = OpacityStyle; + themedIconStyle = ThemedIconStyle; } public object? ToIcon() { - return (object?)ToOpacityIcon() ?? ToFontIcon(); + return (object?)ToThemedIcon() ?? ToFontIcon(); } public FontIcon? ToFontIcon() @@ -54,22 +55,22 @@ public void Deconstruct(out string baseGlyph, out string fontFamily, out string return fontIcon; } - public OpacityIcon? ToOpacityIcon() + public ThemedIcon? ToThemedIcon() { - if (string.IsNullOrEmpty(OpacityStyle)) + if (string.IsNullOrEmpty(ThemedIconStyle)) return null; return new() { - Style = (Style)Application.Current.Resources[OpacityStyle] + Style = (Style)Application.Current.Resources[ThemedIconStyle] }; } - public Style? ToOpacityStyle() + public Style? ToThemedIconStyle() { - if (string.IsNullOrEmpty(OpacityStyle)) + if (string.IsNullOrEmpty(ThemedIconStyle)) return null; - return (Style)Application.Current.Resources[OpacityStyle]; + return (Style)Application.Current.Resources[ThemedIconStyle]; } } } diff --git a/src/Files.App/Data/Contexts/ContentPage/ContentPageContext.cs b/src/Files.App/Data/Contexts/ContentPage/ContentPageContext.cs index 0ab9a294b0ce..5be1861d13c3 100644 --- a/src/Files.App/Data/Contexts/ContentPage/ContentPageContext.cs +++ b/src/Files.App/Data/Contexts/ContentPage/ContentPageContext.cs @@ -1,12 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.App.UserControls.TabBar; using System.Collections.Immutable; namespace Files.App.Data.Contexts { - internal sealed class ContentPageContext : ObservableObject, IContentPageContext + internal sealed partial class ContentPageContext : ObservableObject, IContentPageContext { private static readonly IReadOnlyList emptyItems = Enumerable.Empty().ToImmutableList(); @@ -39,8 +38,6 @@ internal sealed class ContentPageContext : ObservableObject, IContentPageContext public bool CanNavigateToParent => ShellPage is not null && ShellPage.ToolbarViewModel.CanNavigateToParent; - public bool IsSearchBoxVisible => ShellPage is not null && ShellPage.ToolbarViewModel.IsSearchBoxVisible; - public bool CanCreateItem => GetCanCreateItem(); public bool IsMultiPaneAvailable => ShellPage is not null && ShellPage.PaneHolder is not null && ShellPage.PaneHolder.IsMultiPaneAvailable; @@ -94,7 +91,7 @@ private void Context_Changed(object? sender, EventArgs e) page.ContentChanged += Page_ContentChanged; page.InstanceViewModel.PropertyChanged += InstanceViewModel_PropertyChanged; page.ToolbarViewModel.PropertyChanged += ToolbarViewModel_PropertyChanged; - + if (page.PaneHolder is not null) page.PaneHolder.PropertyChanged += PaneHolder_PropertyChanged; } @@ -147,6 +144,8 @@ private void InstanceViewModel_PropertyChanged(object? sender, PropertyChangedEv case nameof(CurrentInstanceViewModel.IsPageTypeCloudDrive): case nameof(CurrentInstanceViewModel.IsPageTypeMtpDevice): case nameof(CurrentInstanceViewModel.IsPageTypeSearchResults): + case nameof(CurrentInstanceViewModel.IsPageTypeReleaseNotes): + case nameof(CurrentInstanceViewModel.IsPageTypeSettings): UpdatePageType(); break; case nameof(CurrentInstanceViewModel.IsGitRepository): @@ -160,15 +159,14 @@ private void ToolbarViewModel_PropertyChanged(object? sender, PropertyChangedEve { switch (e.PropertyName) { - case nameof(AddressToolbarViewModel.CanGoBack): - case nameof(AddressToolbarViewModel.CanGoForward): - case nameof(AddressToolbarViewModel.CanNavigateToParent): - case nameof(AddressToolbarViewModel.HasItem): - case nameof(AddressToolbarViewModel.CanRefresh): - case nameof(AddressToolbarViewModel.IsSearchBoxVisible): + case nameof(NavigationToolbarViewModel.CanGoBack): + case nameof(NavigationToolbarViewModel.CanGoForward): + case nameof(NavigationToolbarViewModel.CanNavigateToParent): + case nameof(NavigationToolbarViewModel.HasItem): + case nameof(NavigationToolbarViewModel.CanRefresh): OnPropertyChanged(e.PropertyName); break; - case nameof(AddressToolbarViewModel.SelectedItems): + case nameof(NavigationToolbarViewModel.SelectedItems): UpdateSelectedItems(); break; } @@ -210,6 +208,7 @@ private void UpdatePageType() { null => ContentPageTypes.None, { IsPageTypeNotHome: false } => ContentPageTypes.Home, + { IsPageTypeReleaseNotes: true } => ContentPageTypes.ReleaseNotes, { IsPageTypeRecycleBin: true } => ContentPageTypes.RecycleBin, { IsPageTypeZipFolder: true } => ContentPageTypes.ZipFolder, { IsPageTypeFtp: true } => ContentPageTypes.Ftp, @@ -217,6 +216,7 @@ private void UpdatePageType() { IsPageTypeCloudDrive: true } => ContentPageTypes.CloudDrive, { IsPageTypeMtpDevice: true } => ContentPageTypes.MtpDevice, { IsPageTypeSearchResults: true } => ContentPageTypes.SearchResults, + { IsPageTypeSettings: true } => ContentPageTypes.Settings, _ => ContentPageTypes.Folder, }; SetProperty(ref pageType, type, nameof(PageType)); @@ -246,7 +246,9 @@ and not ContentPageTypes.Home and not ContentPageTypes.RecycleBin and not ContentPageTypes.ZipFolder and not ContentPageTypes.SearchResults - and not ContentPageTypes.MtpDevice; + and not ContentPageTypes.MtpDevice + and not ContentPageTypes.ReleaseNotes + and not ContentPageTypes.Settings; } } } diff --git a/src/Files.App/Data/Contexts/ContentPage/ContentPageTypes.cs b/src/Files.App/Data/Contexts/ContentPage/ContentPageTypes.cs index 2a0cee1f7588..c89090e47f80 100644 --- a/src/Files.App/Data/Contexts/ContentPage/ContentPageTypes.cs +++ b/src/Files.App/Data/Contexts/ContentPage/ContentPageTypes.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contexts { @@ -15,5 +15,7 @@ public enum ContentPageTypes : ushort CloudDrive, MtpDevice, SearchResults, + ReleaseNotes, + Settings, } } diff --git a/src/Files.App/Data/Contexts/ContentPage/IContentPageContext.cs b/src/Files.App/Data/Contexts/ContentPage/IContentPageContext.cs index 24bc26aa3a08..7a278b3a86b9 100644 --- a/src/Files.App/Data/Contexts/ContentPage/IContentPageContext.cs +++ b/src/Files.App/Data/Contexts/ContentPage/IContentPageContext.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contexts { @@ -23,8 +23,6 @@ public interface IContentPageContext : INotifyPropertyChanged bool CanGoForward { get; } bool CanNavigateToParent { get; } - bool IsSearchBoxVisible { get; } - bool CanCreateItem { get; } bool IsMultiPaneAvailable { get; } diff --git a/src/Files.App/Data/Contexts/DisplayPage/DisplayPageContext.cs b/src/Files.App/Data/Contexts/DisplayPage/DisplayPageContext.cs index 7e6b10d8db3a..36810ae274fd 100644 --- a/src/Files.App/Data/Contexts/DisplayPage/DisplayPageContext.cs +++ b/src/Files.App/Data/Contexts/DisplayPage/DisplayPageContext.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contexts { - internal sealed class DisplayPageContext : ObservableObject, IDisplayPageContext + internal sealed partial class DisplayPageContext : ObservableObject, IDisplayPageContext { private readonly IMultiPanesContext context = Ioc.Default.GetRequiredService(); private readonly IFoldersSettingsService settings = Ioc.Default.GetRequiredService(); @@ -29,8 +29,8 @@ public LayoutTypes LayoutType case LayoutTypes.List: viewModel.ToggleLayoutModeList(true); break; - case LayoutTypes.Tiles: - viewModel.ToggleLayoutModeTiles(true); + case LayoutTypes.Cards: + viewModel.ToggleLayoutModeCards(true); break; case LayoutTypes.Grid: viewModel.ToggleLayoutModeGridView(true); @@ -230,11 +230,11 @@ private LayoutTypes GetLayoutType() { FolderLayoutModes.DetailsView => LayoutTypes.Details, FolderLayoutModes.ListView => LayoutTypes.List, - FolderLayoutModes.TilesView => LayoutTypes.Tiles, + FolderLayoutModes.CardsView => LayoutTypes.Cards, FolderLayoutModes.GridView => LayoutTypes.Grid, FolderLayoutModes.ColumnView => LayoutTypes.Columns, _ => throw new InvalidEnumArgumentException(), }; } } -} +} \ No newline at end of file diff --git a/src/Files.App/Data/Contexts/DisplayPage/IDisplayPageContext.cs b/src/Files.App/Data/Contexts/DisplayPage/IDisplayPageContext.cs index 1bf0a919891f..feb20ba06c25 100644 --- a/src/Files.App/Data/Contexts/DisplayPage/IDisplayPageContext.cs +++ b/src/Files.App/Data/Contexts/DisplayPage/IDisplayPageContext.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contexts { diff --git a/src/Files.App/Data/Contexts/DisplayPage/LayoutTypes.cs b/src/Files.App/Data/Contexts/DisplayPage/LayoutTypes.cs index c5fd4ecfa6c3..4d6309a34712 100644 --- a/src/Files.App/Data/Contexts/DisplayPage/LayoutTypes.cs +++ b/src/Files.App/Data/Contexts/DisplayPage/LayoutTypes.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contexts { @@ -8,7 +8,7 @@ public enum LayoutTypes : ushort None, Details, List, - Tiles, + Cards, Grid, Columns, Adaptive, diff --git a/src/Files.App/Data/Contexts/HomePage/HomePageContext.cs b/src/Files.App/Data/Contexts/HomePage/HomePageContext.cs index 01c32bbd81ed..dcc552a83b1e 100644 --- a/src/Files.App/Data/Contexts/HomePage/HomePageContext.cs +++ b/src/Files.App/Data/Contexts/HomePage/HomePageContext.cs @@ -1,18 +1,19 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.App.UserControls.Widgets; using Microsoft.UI.Xaml.Controls; using System.Collections.Immutable; namespace Files.App.Data.Contexts { - public sealed class HomePageContext : ObservableObject, IHomePageContext + public sealed partial class HomePageContext : ObservableObject, IHomePageContext { private static readonly ImmutableList emptyTaggedItems = Enumerable.Empty().ToImmutableList(); public bool IsAnyItemRightClicked => rightClickedItem is not null; + public IHomeFolder HomeFolder { get; } = new HomeFolder(); + private WidgetCardItem? rightClickedItem = null; public WidgetCardItem? RightClickedItem => rightClickedItem; diff --git a/src/Files.App/Data/Contexts/HomePage/IHomePageContext.cs b/src/Files.App/Data/Contexts/HomePage/IHomePageContext.cs index 37a0e3d8afad..f793a0914755 100644 --- a/src/Files.App/Data/Contexts/HomePage/IHomePageContext.cs +++ b/src/Files.App/Data/Contexts/HomePage/IHomePageContext.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Controls; @@ -26,5 +26,10 @@ public interface IHomePageContext /// Tells whether any item has been right clicked ///
bool IsAnyItemRightClicked { get; } + + /// + /// Gets the instance of . + /// + IHomeFolder HomeFolder { get; } } } diff --git a/src/Files.App/Data/Contexts/MultiPanes/IMultiPanesContext.cs b/src/Files.App/Data/Contexts/MultiPanes/IMultiPanesContext.cs index 9cf3b45d74eb..0dc46771b8f0 100644 --- a/src/Files.App/Data/Contexts/MultiPanes/IMultiPanesContext.cs +++ b/src/Files.App/Data/Contexts/MultiPanes/IMultiPanesContext.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contexts { diff --git a/src/Files.App/Data/Contexts/MultiPanes/MultiPanesContext.cs b/src/Files.App/Data/Contexts/MultiPanes/MultiPanesContext.cs index 4ef2fa38e34e..6c528d1f9ce7 100644 --- a/src/Files.App/Data/Contexts/MultiPanes/MultiPanesContext.cs +++ b/src/Files.App/Data/Contexts/MultiPanes/MultiPanesContext.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contexts { diff --git a/src/Files.App/Data/Contexts/Multitasking/IMultitaskingContext.cs b/src/Files.App/Data/Contexts/Multitasking/IMultitaskingContext.cs index 947784d48ac6..d68068155de2 100644 --- a/src/Files.App/Data/Contexts/Multitasking/IMultitaskingContext.cs +++ b/src/Files.App/Data/Contexts/Multitasking/IMultitaskingContext.cs @@ -1,8 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.App.UserControls.TabBar; -using System.ComponentModel; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contexts { diff --git a/src/Files.App/Data/Contexts/Multitasking/MultitaskingContext.cs b/src/Files.App/Data/Contexts/Multitasking/MultitaskingContext.cs index c158c523eaa0..15dfcb901c87 100644 --- a/src/Files.App/Data/Contexts/Multitasking/MultitaskingContext.cs +++ b/src/Files.App/Data/Contexts/Multitasking/MultitaskingContext.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Input; @@ -7,7 +7,7 @@ namespace Files.App.Data.Contexts { - internal sealed class MultitaskingContext : ObservableObject, IMultitaskingContext + internal sealed partial class MultitaskingContext : ObservableObject, IMultitaskingContext { private bool isPopupOpen = false; @@ -39,6 +39,7 @@ public MultitaskingContext() private void AppInstances_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { UpdateTabCount(); + NavigationHelpers.RefreshTabPathHints(); } private void AppModel_PropertyChanged(object? sender, PropertyChangedEventArgs e) { diff --git a/src/Files.App/Data/Contexts/SideBar/ISideBarContext.cs b/src/Files.App/Data/Contexts/SideBar/ISideBarContext.cs index 92f1f3e199c1..60aa1bdd47f3 100644 --- a/src/Files.App/Data/Contexts/SideBar/ISideBarContext.cs +++ b/src/Files.App/Data/Contexts/SideBar/ISideBarContext.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contexts { diff --git a/src/Files.App/Data/Contexts/SideBar/SideBarContext.cs b/src/Files.App/Data/Contexts/SideBar/SideBarContext.cs index b8412d9f8e3a..142771ba7155 100644 --- a/src/Files.App/Data/Contexts/SideBar/SideBarContext.cs +++ b/src/Files.App/Data/Contexts/SideBar/SideBarContext.cs @@ -1,10 +1,10 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contexts { /// - internal sealed class SidebarContext : ObservableObject, ISidebarContext + internal sealed partial class SidebarContext : ObservableObject, ISidebarContext { private readonly PinnedFoldersManager favoriteModel = App.QuickAccessManager.Model; diff --git a/src/Files.App/Data/Contexts/Tags/ITagsContext.cs b/src/Files.App/Data/Contexts/Tags/ITagsContext.cs index 8ed69b9d68d0..8103d422f096 100644 --- a/src/Files.App/Data/Contexts/Tags/ITagsContext.cs +++ b/src/Files.App/Data/Contexts/Tags/ITagsContext.cs @@ -1,10 +1,10 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contexts { - interface ITagsContext: INotifyPropertyChanged - { + interface ITagsContext : INotifyPropertyChanged + { IEnumerable<(string path, bool isFolder)> TaggedItems { get; } - } + } } diff --git a/src/Files.App/Data/Contexts/Tags/TagsContext.cs b/src/Files.App/Data/Contexts/Tags/TagsContext.cs index 8a61f82221df..5ae3fe925207 100644 --- a/src/Files.App/Data/Contexts/Tags/TagsContext.cs +++ b/src/Files.App/Data/Contexts/Tags/TagsContext.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Collections.Immutable; namespace Files.App.Data.Contexts { - sealed class TagsContext : ITagsContext - { + sealed partial class TagsContext : ITagsContext + { private static readonly IReadOnlyList<(string path, bool isFolder)> _emptyTaggedItemsList = Enumerable.Empty<(string path, bool isFolder)>().ToImmutableList(); diff --git a/src/Files.App/Data/Contexts/Window/IWindowContext.cs b/src/Files.App/Data/Contexts/Window/IWindowContext.cs index e6271f95c25f..98a2117c740e 100644 --- a/src/Files.App/Data/Contexts/Window/IWindowContext.cs +++ b/src/Files.App/Data/Contexts/Window/IWindowContext.cs @@ -1,12 +1,16 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System.ComponentModel; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contexts { public interface IWindowContext : INotifyPropertyChanged { bool IsCompactOverlay { get; } + + /// + bool IsRunningAsAdmin { get; } + + /// + bool CanDragAndDrop { get; } } } diff --git a/src/Files.App/Data/Contexts/Window/WindowContext.cs b/src/Files.App/Data/Contexts/Window/WindowContext.cs index 58140f8bf533..c99560efd16f 100644 --- a/src/Files.App/Data/Contexts/Window/WindowContext.cs +++ b/src/Files.App/Data/Contexts/Window/WindowContext.cs @@ -1,27 +1,42 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using CommunityToolkit.Mvvm.ComponentModel; using Microsoft.UI.Windowing; namespace Files.App.Data.Contexts { - internal sealed class WindowContext : ObservableObject, IWindowContext + /// + internal sealed partial class WindowContext : ObservableObject, IWindowContext { + private IWindowsSecurityService WindowsSecurityService = Ioc.Default.GetRequiredService(); + private bool isCompactOverlay; + /// public bool IsCompactOverlay => isCompactOverlay; + /// + public bool IsRunningAsAdmin { get; private set; } + + /// + public bool CanDragAndDrop { get; private set; } + public WindowContext() { - MainWindow.Instance.PresenterChanged += Window_PresenterChanged; + IsRunningAsAdmin = WindowsSecurityService.IsAppElevated(); + CanDragAndDrop = WindowsSecurityService.CanDragAndDrop(); + + MainWindow.Instance.AppWindow.Changed += AppWindow_Changed; } - private void Window_PresenterChanged(object? sender, AppWindowPresenter e) + private void AppWindow_Changed(AppWindow sender, AppWindowChangedEventArgs args) { - SetProperty( - ref isCompactOverlay, - e.Kind is AppWindowPresenterKind.CompactOverlay, - nameof(IsCompactOverlay)); + if (args.DidPresenterChange) + { + SetProperty( + ref isCompactOverlay, + sender.Presenter.Kind is AppWindowPresenterKind.CompactOverlay, + nameof(IsCompactOverlay)); + } } } } diff --git a/src/Files.App/Data/Contracts/IActionsSettingsService.cs b/src/Files.App/Data/Contracts/IActionsSettingsService.cs index 2d0fe6cf6fd7..6f2b66fd42fd 100644 --- a/src/Files.App/Data/Contracts/IActionsSettingsService.cs +++ b/src/Files.App/Data/Contracts/IActionsSettingsService.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { diff --git a/src/Files.App/Data/Contracts/IAddItemService.cs b/src/Files.App/Data/Contracts/IAddItemService.cs index 7d461b352fd6..58c65aff5c97 100644 --- a/src/Files.App/Data/Contracts/IAddItemService.cs +++ b/src/Files.App/Data/Contracts/IAddItemService.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { diff --git a/src/Files.App/Data/Contracts/IAddressToolbarViewModel.cs b/src/Files.App/Data/Contracts/IAddressToolbarViewModel.cs index 145c186c6408..56ce2713f646 100644 --- a/src/Files.App/Data/Contracts/IAddressToolbarViewModel.cs +++ b/src/Files.App/Data/Contracts/IAddressToolbarViewModel.cs @@ -1,19 +1,10 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { public interface IAddressToolbarViewModel { - public bool IsSearchBoxVisible { get; set; } - - public bool IsEditModeEnabled { get; set; } - - /// - /// Gets or sets the value that indicates whether the command palette is open. - /// - public bool IsCommandPaletteOpen { get; set; } - public bool CanRefresh { get; set; } public bool CanCopyPathInPage { get; set; } @@ -36,16 +27,9 @@ public interface IAddressToolbarViewModel public event ToolbarQuerySubmittedEventHandler PathBoxQuerySubmitted; - public event EventHandler EditModeEnabled; public event ItemDraggedOverPathItemEventHandler ItemDraggedOverPathItem; - public event EventHandler RefreshRequested; - public event EventHandler RefreshWidgetsRequested; - - public void SwitchSearchBoxVisibility(); - - public ISearchBoxViewModel SearchBox { get; } } } diff --git a/src/Files.App/Data/Contracts/IAppSettingsService.cs b/src/Files.App/Data/Contracts/IAppSettingsService.cs index 05919f1d3ea5..d40c9b635178 100644 --- a/src/Files.App/Data/Contracts/IAppSettingsService.cs +++ b/src/Files.App/Data/Contracts/IAppSettingsService.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { diff --git a/src/Files.App/Data/Contracts/IAppThemeModeService.cs b/src/Files.App/Data/Contracts/IAppThemeModeService.cs index bb9dbfefbb13..f955ff7f3d64 100644 --- a/src/Files.App/Data/Contracts/IAppThemeModeService.cs +++ b/src/Files.App/Data/Contracts/IAppThemeModeService.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Windowing; using Microsoft.UI.Xaml; diff --git a/src/Files.App/Data/Contracts/IAppearanceSettingsService.cs b/src/Files.App/Data/Contracts/IAppearanceSettingsService.cs index 8e5aaf412a0d..527a84869d84 100644 --- a/src/Files.App/Data/Contracts/IAppearanceSettingsService.cs +++ b/src/Files.App/Data/Contracts/IAppearanceSettingsService.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Media; @@ -101,10 +101,26 @@ public interface IAppearanceSettingsService : IBaseSettingsService, INotifyPrope /// Gets or sets a value whether the toolbar should be displayed. ///
bool ShowToolbar { get; set; } - + + /// + /// Gets or sets a value whether the status bar should be displayed. + /// + bool ShowStatusBar { get; set; } + /// /// Gets or sets a value whether the tab actions button should be displayed. /// bool ShowTabActions { get; set; } + + /// + /// Gets or sets a value whether the shelf pane toggle button should be displayed. + /// + bool ShowShelfPaneToggleButton { get; set; } + + + /// + /// Gets or sets a value indicating when to display the Status Center button. + /// + StatusCenterVisibility StatusCenterVisibility { get; set; } } } diff --git a/src/Files.App/Data/Contracts/IApplicationSettingsService.cs b/src/Files.App/Data/Contracts/IApplicationSettingsService.cs index a2bda48e27ca..a809120dd5c6 100644 --- a/src/Files.App/Data/Contracts/IApplicationSettingsService.cs +++ b/src/Files.App/Data/Contracts/IApplicationSettingsService.cs @@ -1,19 +1,29 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { public interface IApplicationSettingsService : IBaseSettingsService { /// - /// Gets or sets a value indicating whether or not the user clicked to review the app. + /// Gets or sets a value indicating whether or not the user clicked the 'review' prompt. /// - bool ClickedToReviewApp { get; set; } - + bool HasClickedReviewPrompt { get; set; } + + /// + /// Gets or sets a value indicating whether or not the user clicked the 'sponsor' prompt. + /// + bool HasClickedSponsorPrompt { get; set; } + /// /// Gets or sets a value indicating whether or not to display a prompt when running the app as administrator. /// bool ShowRunningAsAdminPrompt { get; set; } + /// + /// Gets or sets a value indicating whether or not to display a prompt when creating an alternate data stream. + /// + bool ShowDataStreamsAreHiddenPrompt { get; set; } + } } diff --git a/src/Files.App/Data/Contracts/IBaseSettingsService.cs b/src/Files.App/Data/Contracts/IBaseSettingsService.cs index a4ec2d22a9b9..965811bcbca9 100644 --- a/src/Files.App/Data/Contracts/IBaseSettingsService.cs +++ b/src/Files.App/Data/Contracts/IBaseSettingsService.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { diff --git a/src/Files.App/Data/Contracts/ICommonDialogService.cs b/src/Files.App/Data/Contracts/ICommonDialogService.cs index 7362a786e495..3ba3e897524d 100644 --- a/src/Files.App/Data/Contracts/ICommonDialogService.cs +++ b/src/Files.App/Data/Contracts/ICommonDialogService.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { @@ -43,6 +43,6 @@ public interface ICommonDialogService /// The name of the remote network. /// The value indicating whether to enter the most recently used paths into the combination box. /// True if the 'OK' button was clicked; otherwise, false. - bool Open_NetworkConnectionDialog(nint hWind, bool hideRestoreConnectionCheckBox = false, bool persistConnectionAtLogon = false, bool readOnlyPath = false, string? remoteNetworkName = null, bool useMostRecentPath = false); + bool Open_NetworkConnectionDialog(nint hWnd, bool hideRestoreConnectionCheckBox = false, bool persistConnectionAtLogon = false, bool readOnlyPath = false, string? remoteNetworkName = null, bool useMostRecentPath = false); } } diff --git a/src/Files.App/Data/Contracts/ICompressArchiveModel.cs b/src/Files.App/Data/Contracts/ICompressArchiveModel.cs index 5d11ea5a85c0..b847b37da789 100644 --- a/src/Files.App/Data/Contracts/ICompressArchiveModel.cs +++ b/src/Files.App/Data/Contracts/ICompressArchiveModel.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { @@ -48,6 +48,21 @@ public interface ICompressArchiveModel /// ArchiveSplittingSizes SplittingSize { get; } + /// + /// 7zip archive dictionary size. + /// + ArchiveDictionarySizes DictionarySize { get; } + + /// + /// 7zip archive word size (fast bytes). + /// + ArchiveWordSizes WordSize { get; } + + /// + /// Number of CPU Threads to use. + /// + int CPUThreads { get; } + /// /// Archiving progress. /// diff --git a/src/Files.App/Data/Contracts/IDataTransferManagerInterop.cs b/src/Files.App/Data/Contracts/IDataTransferManagerInterop.cs index b9bd34b0a2ff..2ab3dab945a4 100644 --- a/src/Files.App/Data/Contracts/IDataTransferManagerInterop.cs +++ b/src/Files.App/Data/Contracts/IDataTransferManagerInterop.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Runtime.InteropServices; diff --git a/src/Files.App/Data/Contracts/IDevToolsSettingsService.cs b/src/Files.App/Data/Contracts/IDevToolsSettingsService.cs index 86b88914e864..b942dec691cb 100644 --- a/src/Files.App/Data/Contracts/IDevToolsSettingsService.cs +++ b/src/Files.App/Data/Contracts/IDevToolsSettingsService.cs @@ -1,8 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Media; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { @@ -12,5 +9,15 @@ public interface IDevToolsSettingsService : IBaseSettingsService, INotifyPropert /// Gets or sets a value when the Open in IDE button should be displayed on the status bar. /// OpenInIDEOption OpenInIDEOption { get; set; } + + /// + /// Gets or sets the path of the chosen IDE. + /// + string IDEPath { get; set; } + + /// + /// Gets or sets the name of the chosen IDE. + /// + string IDEName { get; set; } } } diff --git a/src/Files.App/Data/Contracts/IDialogService.cs b/src/Files.App/Data/Contracts/IDialogService.cs index 689715c23c48..b86216c2b95c 100644 --- a/src/Files.App/Data/Contracts/IDialogService.cs +++ b/src/Files.App/Data/Contracts/IDialogService.cs @@ -1,8 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.App.ViewModels.Dialogs; -using Files.App.Data.Enums; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { diff --git a/src/Files.App/Data/Contracts/IFileTagsService.cs b/src/Files.App/Data/Contracts/IFileTagsService.cs index 3fb69e5cbc5e..18d532a0f4e8 100644 --- a/src/Files.App/Data/Contracts/IFileTagsService.cs +++ b/src/Files.App/Data/Contracts/IFileTagsService.cs @@ -1,7 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.Core.Storage.Storables; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { @@ -23,7 +21,7 @@ public interface IFileTagsService /// The tag UIDs to set. /// A that cancels this action. /// A that represents the asynchronous operation. If successful, returns true, otherwise false. - Task SetFileTagAsync(ILocatableStorable storable, string[] tagUids, CancellationToken cancellationToken = default); + Task SetFileTagAsync(IStorable storable, string[] tagUids, CancellationToken cancellationToken = default); /// /// Gets all tags which are used to tag files and folders from the database. diff --git a/src/Files.App/Data/Contracts/IFileTagsSettingsService.cs b/src/Files.App/Data/Contracts/IFileTagsSettingsService.cs index 041640f0daf1..7a886a5e0eaa 100644 --- a/src/Files.App/Data/Contracts/IFileTagsSettingsService.cs +++ b/src/Files.App/Data/Contracts/IFileTagsSettingsService.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { diff --git a/src/Files.App/Data/Contracts/IFoldersSettingsService.cs b/src/Files.App/Data/Contracts/IFoldersSettingsService.cs index 0dffa3c13b8f..b8131e359492 100644 --- a/src/Files.App/Data/Contracts/IFoldersSettingsService.cs +++ b/src/Files.App/Data/Contracts/IFoldersSettingsService.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { @@ -31,9 +31,9 @@ public interface IFoldersSettingsService : IBaseSettingsService, INotifyProperty bool OpenItemsWithOneClick { get; set; } /// - /// Gets or sets a value indicating whether or not folders should open with two clicks in ColumnsLayout. + /// Gets or sets a value indicating whether or not folders should open with a single click. /// - bool ColumnLayoutOpenFoldersWithOneClick { get; set; } + OpenFoldersWithOneClickEnum OpenFoldersWithOneClick { get; set; } /// /// Gets or sets a value indicating whether or not to open folders in new tab. @@ -84,5 +84,10 @@ public interface IFoldersSettingsService : IBaseSettingsService, INotifyProperty /// Gets or sets a value indicating whether or not to show checkboxes when selecting items. /// bool ShowCheckboxesWhenSelectingItems { get; set; } + + /// + /// Gets or sets a value indicating which format to use when displaying item sizes. + /// + SizeUnitTypes SizeUnitFormat { get; set; } } } diff --git a/src/Files.App/Data/Contracts/IGeneralSettingsService.cs b/src/Files.App/Data/Contracts/IGeneralSettingsService.cs index 275819589cbd..33c566f65c6a 100644 --- a/src/Files.App/Data/Contracts/IGeneralSettingsService.cs +++ b/src/Files.App/Data/Contracts/IGeneralSettingsService.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { @@ -50,6 +50,16 @@ public interface IGeneralSettingsService : IBaseSettingsService, INotifyProperty /// List PathHistoryList { get; set; } + /// + /// A list containing previous search queries. + /// + List PreviousSearchQueriesList { get; set; } + + /// + /// Stores list of paths where archives have previously been extracted. + /// + List PreviousArchiveExtractionLocations { get; set; } + /// /// Gets or sets a value indicating which date and time format to use. /// @@ -60,6 +70,11 @@ public interface IGeneralSettingsService : IBaseSettingsService, INotifyProperty /// bool AlwaysOpenDualPaneInNewTab { get; set; } + /// + /// Gets or sets a value indicating whether or not to always switch to newly opened tab. + /// + bool AlwaysSwitchToNewlyOpenedTab { get; set; } + /// /// Gets or sets a value indicating whether or not to display the quick access widget. /// @@ -115,36 +130,71 @@ public interface IGeneralSettingsService : IBaseSettingsService, INotifyProperty /// bool ShowPinnedSection { get; set; } + /// + /// Gets or sets a value indicating if the favorites section should be expanded. + /// + bool IsPinnedSectionExpanded { get; set; } + /// /// Gets or sets a value indicating if the library section should be visible. /// bool ShowLibrarySection { get; set; } + /// + /// Gets or sets a value indicating if the library section should be expanded. + /// + bool IsLibrarySectionExpanded { get; set; } + /// /// Gets or sets a value indicating if the drive section should be visible. /// bool ShowDrivesSection { get; set; } + /// + /// Gets or sets a value indicating if the drive section should be expanded. + /// + bool IsDriveSectionExpanded { get; set; } + /// /// Gets or sets a value indicating if the cloud drive section should be visible. /// bool ShowCloudDrivesSection { get; set; } + /// + /// Gets or sets a value indicating if the cloud drive section should be expanded. + /// + bool IsCloudDriveSectionExpanded { get; set; } + /// /// Gets or sets a value indicating if the network section should be visible. /// bool ShowNetworkSection { get; set; } + /// + /// Gets or sets a value indicating if the network section should be expanded. + /// + bool IsNetworkSectionExpanded { get; set; } + /// /// Gets or sets a value indicating if the wsl section should be visible. /// bool ShowWslSection { get; set; } + /// + /// Gets or sets a value indicating if the wsl section should be expanded. + /// + bool IsWslSectionExpanded { get; set; } + /// /// Gets or sets a value indicating if the tags section should be visible. /// bool ShowFileTagsSection { get; set; } + /// + /// Gets or sets a value indicating if the file tags section should be expanded. + /// + bool IsFileTagsSectionExpanded { get; set; } + /// /// Gets or sets a value indicating whether or not to move shell extensions into a sub menu. /// @@ -155,6 +205,16 @@ public interface IGeneralSettingsService : IBaseSettingsService, INotifyProperty /// bool ShowEditTagsMenu { get; set; } + /// + /// Gets or sets a value indicating whether or not to show the option to pin to the sidebar. + /// + bool ShowPinToSideBar { get; set; } + + /// + /// Gets or sets a value indicating whether or not to show the option to pin to the start menu. + /// + bool ShowPinToStart { get; set; } + /// /// Gets or sets a value indicating whether or not to show the option to open folders in a new tab. /// @@ -170,11 +230,21 @@ public interface IGeneralSettingsService : IBaseSettingsService, INotifyProperty /// bool ShowOpenInNewPane { get; set; } + /// + /// Gets or sets a value indicating whether or not to show the option to open folders in Windows Terminal. + /// + bool ShowOpenTerminal { get; set; } + /// /// Gets or sets a value indicating whether or not to show the option to copy an items path. /// bool ShowCopyPath { get; set; } + /// + /// Gets or sets a value indicating whether or not to show the option to create alternate data stream. + /// + bool ShowCreateAlternateDataStream { get; set; } + /// /// Gets or sets a value indicating whether or not to show the option to create a shortcut. /// @@ -190,6 +260,11 @@ public interface IGeneralSettingsService : IBaseSettingsService, INotifyProperty /// bool ShowCompressionOptions { get; set; } + /// + /// Gets or sets a value indicating whether or not to show the flatten options e.g. single, recursive. + /// + bool ShowFlattenOptions { get; set; } + /// /// Gets or sets a value indicating whether or not to show the Send To menu. /// @@ -200,11 +275,41 @@ public interface IGeneralSettingsService : IBaseSettingsService, INotifyProperty /// bool LeaveAppRunning { get; set; } + /// + /// Gets or sets a value indicating whether or not to show Files in the system tray. + /// + bool ShowSystemTrayIcon { get; set; } + /// /// Gets or sets a value indicating the default option to resolve conflicts. /// FileNameConflictResolveOptionType ConflictsResolveOption { get; set; } + /// + /// Gets or sets a value indicating the default archive format. + /// + ArchiveFormats ArchiveFormatsOption { get; set; } + + /// + /// Gets or sets a value indicating the default archive compression level. + /// + ArchiveCompressionLevels ArchiveCompressionLevelsOption { get; set; } + + /// + /// Gets or sets a value indicating the default archive splitting size. + /// + ArchiveSplittingSizes ArchiveSplittingSizesOption { get; set; } + + /// + /// Gets or sets a value indicating the default archive dictionary size for 7z. + /// + ArchiveDictionarySizes ArchiveDictionarySizesOption { get; set; } + + /// + /// Gets or sets a value indicating the default archive word size for 7z. + /// + ArchiveWordSizes ArchiveWordSizesOption { get; set; } + /// /// A dictionary to determine which hashes should be shown. /// @@ -219,5 +324,20 @@ public interface IGeneralSettingsService : IBaseSettingsService, INotifyProperty /// Gets or sets a value indicating the default arrangement for Dual Pane. /// ShellPaneArrangement ShellPaneArrangementOption { get; set; } + + /// + /// Gets or sets a value indicating whether or not to show the shelf pane. + /// + bool ShowShelfPane { get; set; } + + /// + /// Gets or sets a value whether the filter header should be displayed. + /// + bool ShowFilterHeader { get; set; } + + /// + /// Gets or sets a value indicating whether smooth scrolling is enabled. + /// + bool EnableSmoothScrolling { get; set; } } } diff --git a/src/Files.App/Data/Contracts/IIconCacheService.cs b/src/Files.App/Data/Contracts/IIconCacheService.cs new file mode 100644 index 000000000000..8afbab133918 --- /dev/null +++ b/src/Files.App/Data/Contracts/IIconCacheService.cs @@ -0,0 +1,12 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Data.Contracts +{ + internal interface IIconCacheService + { + Task GetIconAsync(string itemPath, string? extension, bool isFolder); + + void Clear(); + } +} diff --git a/src/Files.App/Data/Contracts/IImageService.cs b/src/Files.App/Data/Contracts/IImageService.cs index 99b5c5c91933..1749e489941a 100644 --- a/src/Files.App/Data/Contracts/IImageService.cs +++ b/src/Files.App/Data/Contracts/IImageService.cs @@ -1,7 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.Core.Storage; using Files.Shared.Utils; namespace Files.App.Data.Contracts diff --git a/src/Files.App/Data/Contracts/IInfoPaneSettingsService.cs b/src/Files.App/Data/Contracts/IInfoPaneSettingsService.cs index 9142cb8f32c9..c32a1880632c 100644 --- a/src/Files.App/Data/Contracts/IInfoPaneSettingsService.cs +++ b/src/Files.App/Data/Contracts/IInfoPaneSettingsService.cs @@ -1,7 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System.ComponentModel; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { @@ -10,7 +8,7 @@ public interface IInfoPaneSettingsService : IBaseSettingsService, INotifyPropert /// /// Gets or sets a value indicating if the preview pane is enabled. /// - bool IsEnabled { get; set; } + bool IsInfoPaneEnabled { get; set; } /// /// Gets or sets a value indicating the height of the pane in a horizontal layout. diff --git a/src/Files.App/Data/Contracts/ILayoutSettingsService.cs b/src/Files.App/Data/Contracts/ILayoutSettingsService.cs index faee216def17..05b4363f7385 100644 --- a/src/Files.App/Data/Contracts/ILayoutSettingsService.cs +++ b/src/Files.App/Data/Contracts/ILayoutSettingsService.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { @@ -206,9 +206,9 @@ public interface ILayoutSettingsService : IBaseSettingsService, INotifyPropertyC ListViewSizeKind ListViewSize { get; set; } /// - /// Item size in the Tiles View + /// Item size in the Cards View /// - TilesViewSizeKind TilesViewSize { get; set; } + CardsViewSizeKind CardsViewSize { get; set; } /// /// Item size in the Grid View diff --git a/src/Files.App/Data/Contracts/ILocalizationService.cs b/src/Files.App/Data/Contracts/ILocalizationService.cs index e849e1756335..0390fa18ee58 100644 --- a/src/Files.App/Data/Contracts/ILocalizationService.cs +++ b/src/Files.App/Data/Contracts/ILocalizationService.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { diff --git a/src/Files.App/Data/Contracts/IMultiPaneInfo.cs b/src/Files.App/Data/Contracts/IMultiPaneInfo.cs index 5bd7768a4165..75f0cf29fa0e 100644 --- a/src/Files.App/Data/Contracts/IMultiPaneInfo.cs +++ b/src/Files.App/Data/Contracts/IMultiPaneInfo.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { diff --git a/src/Files.App/Data/Contracts/INavigationControlItem.cs b/src/Files.App/Data/Contracts/INavigationControlItem.cs index 80dfb5fd2f5b..da7f64839e41 100644 --- a/src/Files.App/Data/Contracts/INavigationControlItem.cs +++ b/src/Files.App/Data/Contracts/INavigationControlItem.cs @@ -1,7 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.App.UserControls.Sidebar; +using Files.App.Controls; namespace Files.App.Data.Contracts { @@ -42,6 +42,8 @@ public sealed class ContextMenuOptions { public bool IsLibrariesHeader { get; set; } + public bool IsTagsHeader { get; set; } + public bool ShowHideSection { get; set; } public bool IsLocationItem { get; set; } @@ -54,8 +56,6 @@ public sealed class ContextMenuOptions public bool ShowEjectDevice { get; set; } - public bool ShowFormatDrive { get; set; } - public bool ShowShellItems { get; set; } } } diff --git a/src/Files.App/Data/Contracts/INetworkService.cs b/src/Files.App/Data/Contracts/INetworkService.cs index c565cec0e84d..3ca39642002b 100644 --- a/src/Files.App/Data/Contracts/INetworkService.cs +++ b/src/Files.App/Data/Contracts/INetworkService.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { @@ -8,24 +8,24 @@ public interface INetworkService /// /// Gets enumerated network computers. /// - ObservableCollection Computers { get; } + ObservableCollection Computers { get; } /// /// Gets enumerated network shortcuts. /// - ObservableCollection Shortcuts { get; } + ObservableCollection Shortcuts { get; } /// /// Enumerates network computers. /// /// A collection of network computers - Task> GetComputersAsync(); + Task> GetComputersAsync(); /// /// Enumerates network shortcuts. /// /// A collection of network shortcuts - Task> GetShortcutsAsync(); + Task> GetShortcutsAsync(); /// /// Updates computers to up-to-date. @@ -50,7 +50,7 @@ public interface INetworkService /// /// An item representing the network storage device to disconnect from /// True or false to indicate status - bool DisconnectNetworkDrive(ILocatableFolder drive); + bool DisconnectNetworkDrive(IFolder drive); /// /// Authenticates the specified network share point. diff --git a/src/Files.App/Data/Contracts/IPreviewPopupProvider.cs b/src/Files.App/Data/Contracts/IPreviewPopupProvider.cs index 8b4b6ec3e077..1241037414b0 100644 --- a/src/Files.App/Data/Contracts/IPreviewPopupProvider.cs +++ b/src/Files.App/Data/Contracts/IPreviewPopupProvider.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { diff --git a/src/Files.App/Data/Contracts/IPreviewPopupService.cs b/src/Files.App/Data/Contracts/IPreviewPopupService.cs index 199f0418c6c7..3a7de9ed8dff 100644 --- a/src/Files.App/Data/Contracts/IPreviewPopupService.cs +++ b/src/Files.App/Data/Contracts/IPreviewPopupService.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { diff --git a/src/Files.App/Data/Contracts/IQuickAccessService.cs b/src/Files.App/Data/Contracts/IQuickAccessService.cs index 1608146c6e30..d3859e053809 100644 --- a/src/Files.App/Data/Contracts/IQuickAccessService.cs +++ b/src/Files.App/Data/Contracts/IQuickAccessService.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { diff --git a/src/Files.App/Data/Contracts/IRemovableDrivesService.cs b/src/Files.App/Data/Contracts/IRemovableDrivesService.cs index c99adb3214ba..0b9e1b821914 100644 --- a/src/Files.App/Data/Contracts/IRemovableDrivesService.cs +++ b/src/Files.App/Data/Contracts/IRemovableDrivesService.cs @@ -1,7 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.Core.Storage.Storables; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { @@ -14,7 +12,7 @@ public interface IRemovableDrivesService /// Gets the primary system drive. This item is typically excluded when enumerating removable drives /// /// The location of the drive which the operating system is installed to. - Task GetPrimaryDriveAsync(); + Task GetPrimaryDriveAsync(); /// /// Creates a watcher for storage devices @@ -26,13 +24,13 @@ public interface IRemovableDrivesService /// Enumerates all removable drives /// /// A collection of removable storage devices - IAsyncEnumerable GetDrivesAsync(); + IAsyncEnumerable GetDrivesAsync(); /// /// Refreshes the properties of a drive /// /// /// - Task UpdateDrivePropertiesAsync(ILocatableFolder drive); + Task UpdateDrivePropertiesAsync(IFolder drive); } } diff --git a/src/Files.App/Data/Contracts/IResourcesService.cs b/src/Files.App/Data/Contracts/IResourcesService.cs index 64da3fcdcffd..32784f369c33 100644 --- a/src/Files.App/Data/Contracts/IResourcesService.cs +++ b/src/Files.App/Data/Contracts/IResourcesService.cs @@ -1,7 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using System.Drawing; +using Windows.UI; namespace Files.App.Data.Contracts { @@ -26,7 +26,7 @@ public interface IResourcesService /// /// void SetAppThemeAddressBarBackgroundColor(Color appThemeAddressBarBackgroundColor); - + /// /// Overrides the XAML resource for App.Theme.Toolbar.BackgroundBrush /// @@ -62,5 +62,11 @@ public interface IResourcesService /// /// void SetAppThemeFontFamily(string contentControlThemeFontFamily); + + /// + /// Overrides the XAML resource for scroll inertia enabled state + /// + /// + void SetScrollInertiaEnabled(bool enableScrollInertia); } } diff --git a/src/Files.App/Data/Contracts/ISearchBoxViewModel.cs b/src/Files.App/Data/Contracts/ISearchBoxViewModel.cs deleted file mode 100644 index a1d603f046f1..000000000000 --- a/src/Files.App/Data/Contracts/ISearchBoxViewModel.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Windows.Foundation; - -namespace Files.App.Data.Contracts -{ - public interface ISearchBoxViewModel - { - event TypedEventHandler TextChanged; - - event TypedEventHandler QuerySubmitted; - - event EventHandler Escaped; - - bool WasQuerySubmitted { get; set; } - - string Query { get; set; } - - void ClearSuggestions(); - - void SetSuggestions(IEnumerable suggestions); - - void AddRecentQueries(); - } -} diff --git a/src/Files.App/Data/Contracts/IShellPage.cs b/src/Files.App/Data/Contracts/IShellPage.cs index b33d1f134d99..86b25b89949f 100644 --- a/src/Files.App/Data/Contracts/IShellPage.cs +++ b/src/Files.App/Data/Contracts/IShellPage.cs @@ -1,5 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Navigation; namespace Files.App.Data.Contracts { @@ -11,13 +13,17 @@ public interface IShellPage : ITabBarItemContent, IMultiPaneInfo, IDisposable, I StorageHistoryHelpers StorageHistoryHelpers { get; } + IList ForwardStack { get; } + + IList BackwardStack { get; } + IBaseLayoutPage SlimContentPage { get; } Type CurrentPageType { get; } IFilesystemHelpers FilesystemHelpers { get; } - AddressToolbarViewModel ToolbarViewModel { get; } + NavigationToolbarViewModel ToolbarViewModel { get; } bool CanNavigateBackward { get; } @@ -60,6 +66,11 @@ public interface IShellPage : ITabBarItemContent, IMultiPaneInfo, IDisposable, I /// public void NavigateHome(); + /// + /// Navigates to the release notes page + /// + public void NavigateToReleaseNotes(); + void NavigateWithArguments(Type sourcePageType, NavigationArguments navArgs); void RemoveLastPageFromBackStack(); diff --git a/src/Files.App/Data/Contracts/IShellPanesPage.cs b/src/Files.App/Data/Contracts/IShellPanesPage.cs index fd62a301e38b..91632061bbcf 100644 --- a/src/Files.App/Data/Contracts/IShellPanesPage.cs +++ b/src/Files.App/Data/Contracts/IShellPanesPage.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { @@ -54,9 +54,25 @@ public interface IShellPanesPage : IDisposable, INotifyPropertyChanged /// public void CloseActivePane(); + /// + /// Closes the non active/focused pane. + /// + public void CloseOtherPane(); + /// /// Focuses the other pane. /// public void FocusOtherPane(); + + /// + /// Focuses the active pane. + /// + public void FocusActivePane(); + + /// + /// Gets open panes. + /// + /// An enumerable containing open panes. + public IEnumerable GetPanes(); } } diff --git a/src/Files.App/Data/Contracts/IStartMenuService.cs b/src/Files.App/Data/Contracts/IStartMenuService.cs index f322d469291a..c5fb16d557b3 100644 --- a/src/Files.App/Data/Contracts/IStartMenuService.cs +++ b/src/Files.App/Data/Contracts/IStartMenuService.cs @@ -1,6 +1,4 @@ -using Files.Core.Storage; - -namespace Files.App.Data.Contracts +namespace Files.App.Data.Contracts { /// /// A service that manages actions associated with system Start Menu. diff --git a/src/Files.App/Data/Contracts/IStorageArchiveService.cs b/src/Files.App/Data/Contracts/IStorageArchiveService.cs index 97b31b86176b..ef4bfd6b949a 100644 --- a/src/Files.App/Data/Contracts/IStorageArchiveService.cs +++ b/src/Files.App/Data/Contracts/IStorageArchiveService.cs @@ -1,7 +1,8 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using SevenZip; +using System.Text; namespace Files.App.Data.Contracts { @@ -37,8 +38,9 @@ public interface IStorageArchiveService /// The archive file path to decompress. /// The destination folder path which the archive file will be decompressed to. /// The password to decrypt the archive file if applicable. + /// The file name encoding to decrypt the archive file. If set to null, system default encoding will be used. /// True if the decompression has done successfully; otherwise, false. - Task DecompressAsync(string archiveFilePath, string destinationFolderPath, string password = ""); + Task DecompressAsync(string archiveFilePath, string destinationFolderPath, string password = "", Encoding? encoding = null); /// /// Generates the archive file name from item names. @@ -54,6 +56,20 @@ public interface IStorageArchiveService /// True if the archive file is encrypted; otherwise, false. Task IsEncryptedAsync(string archiveFilePath); + /// + /// Gets the value that indicates whether the archive file's encoding is undetermined. + /// + /// The archive file path to check if the encoding is undetermined. + /// True if the archive file's encoding is undetermined; otherwise, false. + Task IsEncodingUndeterminedAsync(string archiveFilePath); + + /// + /// Detect encoding for a zip file whose encoding is undetermined. + /// + /// The archive file path to detect encoding + /// Null if the archive file doesn't need to detect encoding or its encoding can't be detected; otherwise, the encoding detected. + Task DetectEncodingAsync(string archiveFilePath); + /// /// Gets the instance from the archive file path. /// diff --git a/src/Files.App/Data/Contracts/IStorageCacheService.cs b/src/Files.App/Data/Contracts/IStorageCacheService.cs index cc403dee0f4c..ec4bbbfdfffd 100644 --- a/src/Files.App/Data/Contracts/IStorageCacheService.cs +++ b/src/Files.App/Data/Contracts/IStorageCacheService.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { diff --git a/src/Files.App/Data/Contracts/IStorageSecurityService.cs b/src/Files.App/Data/Contracts/IStorageSecurityService.cs index a408dda75983..e05658faf1df 100644 --- a/src/Files.App/Data/Contracts/IStorageSecurityService.cs +++ b/src/Files.App/Data/Contracts/IStorageSecurityService.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.Win32.Foundation; diff --git a/src/Files.App/Data/Contracts/IStorageTrashBinService.cs b/src/Files.App/Data/Contracts/IStorageTrashBinService.cs new file mode 100644 index 000000000000..477d75f5348e --- /dev/null +++ b/src/Files.App/Data/Contracts/IStorageTrashBinService.cs @@ -0,0 +1,67 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Data.Contracts +{ + /// + /// Provides service for Recycle Bin on Windows. + /// + public interface IStorageTrashBinService + { + /// + /// Gets the watcher of Recycle Bin folder. + /// + RecycleBinWatcher Watcher { get; } + + /// + /// Gets all Recycle Bin shell folders. + /// + /// A collection of Recycle Bin shell folders. + Task> GetAllRecycleBinFoldersAsync(); + + /// + /// Gets the info of Recycle Bin. + /// + /// The drive letter, Recycle Bin of which you want. + /// + (bool HasRecycleBin, long NumItems, long BinSize) QueryRecycleBin(string drive = ""); + + /// + /// Gets the used size of Recycle Bin. + /// + /// + ulong GetSize(); + + /// + /// Gets the value that indicates whether Recycle Bin folder has item(s). + /// + /// + bool HasItems(); + + /// + /// Gets the file or folder specified is already moved to Recycle Bin. + /// + /// The path that indicates to a file or folder. + /// True if the file or path is recycled; otherwise, false. + bool IsUnderTrashBin(string? path); + + /// + /// Gets the file or folder specified can be moved to Recycle Bin. + /// + /// + /// + Task CanGoTrashBin(string? path); + + /// + /// Deletes files and folders in Recycle Bin permanently. + /// + /// True if succeeded; otherwise, false + bool EmptyTrashBin(); + + /// + /// Restores files and folders in Recycle Bin to original paths. + /// + /// True if succeeded; otherwise, false + Task RestoreAllTrashesAsync(); + } +} diff --git a/src/Files.App/Data/Contracts/IThreadingService.cs b/src/Files.App/Data/Contracts/IThreadingService.cs index d04fde5ba730..7566fddd2add 100644 --- a/src/Files.App/Data/Contracts/IThreadingService.cs +++ b/src/Files.App/Data/Contracts/IThreadingService.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { diff --git a/src/Files.App/Data/Contracts/IUpdateService.cs b/src/Files.App/Data/Contracts/IUpdateService.cs index ce8d9b917c98..333994b22809 100644 --- a/src/Files.App/Data/Contracts/IUpdateService.cs +++ b/src/Files.App/Data/Contracts/IUpdateService.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { @@ -16,14 +16,14 @@ public interface IUpdateService : INotifyPropertyChanged bool IsUpdating { get; } /// - /// Gets a value indicating if the apps being used the first time after an update. + /// Gets a value indicating if the app is being used the first time after an update. /// bool IsAppUpdated { get; } /// - /// Gets a value indicating if release notes are available. + /// Gets a value that indicates if there are release notes available for the current version of the app. /// - bool IsReleaseNotesAvailable { get; } + bool AreReleaseNotesAvailable { get; } Task DownloadUpdatesAsync(); @@ -31,12 +31,7 @@ public interface IUpdateService : INotifyPropertyChanged Task CheckForUpdatesAsync(); - Task CheckLatestReleaseNotesAsync(CancellationToken cancellationToken = default); - - /// - /// Gets release notes for the latest release - /// - Task GetLatestReleaseNotesAsync(CancellationToken cancellationToken = default); + Task CheckForReleaseNotesAsync(); /// /// Replace Files.App.Launcher.exe if it is used and has been updated diff --git a/src/Files.App/Data/Contracts/IUserSettingsService.cs b/src/Files.App/Data/Contracts/IUserSettingsService.cs index 66c79ac1de51..71e3f67b7394 100644 --- a/src/Files.App/Data/Contracts/IUserSettingsService.cs +++ b/src/Files.App/Data/Contracts/IUserSettingsService.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { diff --git a/src/Files.App/Data/Contracts/IWidgetCardItem.cs b/src/Files.App/Data/Contracts/IWidgetCardItem.cs index 0766fa045373..7696304c8088 100644 --- a/src/Files.App/Data/Contracts/IWidgetCardItem.cs +++ b/src/Files.App/Data/Contracts/IWidgetCardItem.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Media.Imaging; diff --git a/src/Files.App/Data/Contracts/IWidgetViewModel.cs b/src/Files.App/Data/Contracts/IWidgetViewModel.cs index 0519e92b9c89..4a1ee2ce3f7f 100644 --- a/src/Files.App/Data/Contracts/IWidgetViewModel.cs +++ b/src/Files.App/Data/Contracts/IWidgetViewModel.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Controls; diff --git a/src/Files.App/Data/Contracts/IWindowsCompatibilityService.cs b/src/Files.App/Data/Contracts/IWindowsCompatibilityService.cs index cc61c64def09..12511376628a 100644 --- a/src/Files.App/Data/Contracts/IWindowsCompatibilityService.cs +++ b/src/Files.App/Data/Contracts/IWindowsCompatibilityService.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { diff --git a/src/Files.App/Data/Contracts/IWindowsIniService.cs b/src/Files.App/Data/Contracts/IWindowsIniService.cs index 0bb0cfd2479f..f8c1662dba71 100644 --- a/src/Files.App/Data/Contracts/IWindowsIniService.cs +++ b/src/Files.App/Data/Contracts/IWindowsIniService.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { diff --git a/src/Files.App/Data/Contracts/IWindowsJumpListService.cs b/src/Files.App/Data/Contracts/IWindowsJumpListService.cs index ab978d4a65b2..df4d8aa0ffb5 100644 --- a/src/Files.App/Data/Contracts/IWindowsJumpListService.cs +++ b/src/Files.App/Data/Contracts/IWindowsJumpListService.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Contracts { diff --git a/src/Files.App/Data/Contracts/IWindowsRecentItemsService.cs b/src/Files.App/Data/Contracts/IWindowsRecentItemsService.cs new file mode 100644 index 000000000000..c6a9f88af2d8 --- /dev/null +++ b/src/Files.App/Data/Contracts/IWindowsRecentItemsService.cs @@ -0,0 +1,58 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using System.Collections.Specialized; + +namespace Files.App.Data.Contracts +{ + /// + /// Provides manager of recent files and folders of File Explorer on Windows. + /// + public interface IWindowsRecentItemsService + { + /// + /// Gets recent files of File Explorer. + /// + IReadOnlyList RecentFiles { get; } + + /// + /// Gets recent folders of File Explorer. + /// + IReadOnlyList RecentFolders { get; } + + /// + /// Gets invoked when recent files of File Explorer have changed. + /// + event EventHandler? RecentFilesChanged; + + /// + /// Gets invoked when recent folders of File Explorer have changed. + /// + event EventHandler? RecentFoldersChanged; + + /// + /// Updates recent files of File Explorer. + /// + Task UpdateRecentFilesAsync(); + + /// + /// Updates recent folders of File Explorer. + /// + Task UpdateRecentFoldersAsync(); + + /// + /// Adds a recent file for File Explorer. + /// + bool Add(string path); + + /// + /// Removes a recent folder for File Explorer. + /// + bool Remove(RecentItem item); + + /// + /// Clears recent files and folders of File Explorer. + /// + bool Clear(); + } +} diff --git a/src/Files.App/Data/Contracts/IWindowsSecurityService.cs b/src/Files.App/Data/Contracts/IWindowsSecurityService.cs new file mode 100644 index 000000000000..f4e9c7e09416 --- /dev/null +++ b/src/Files.App/Data/Contracts/IWindowsSecurityService.cs @@ -0,0 +1,40 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Data.Contracts +{ + /// + /// Provides service for security APIs on Windows. + /// + public interface IWindowsSecurityService + { + /// + /// Gets a value that indicates whether the application is elevated. + /// + /// Returns true if the application is elevated; otherwise, false. + bool IsAppElevated(); + + /// + /// Gets a value that indicates whether the application can drag & drop. + /// + /// + /// Drag & drop onto an elevated app is not allowed (just crashes) due to UIPI. + ///
+ ///
+ /// For more info, visit: + ///
+ /// + ///
+ ///
+ ///
+ /// Returns true if the application can drag & drop; otherwise, false. + bool CanDragAndDrop(); + + /// + /// Gets a value that indicates whether the application needs to be elevated for some operations. + /// + /// + /// True if the application needs to be elevated for some operations; otherwise, false. + bool IsElevationRequired(string path); + } +} diff --git a/src/Files.App/Data/Contracts/IWindowsWallpaperService.cs b/src/Files.App/Data/Contracts/IWindowsWallpaperService.cs new file mode 100644 index 000000000000..d723192ee6b3 --- /dev/null +++ b/src/Files.App/Data/Contracts/IWindowsWallpaperService.cs @@ -0,0 +1,32 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Data.Contracts +{ + /// + /// Provides service for manipulating shell wallpapers on Windows. + /// + public interface IWindowsWallpaperService + { + /// + /// Sets desktop wallpaper using the specified image path. + /// + /// The image path to assign as the desktop wallpaper. + /// Thrown if any of COM methods failed to process successfully. + void SetDesktopWallpaper(string szPath); + + /// + /// Sets desktop slideshow using the specified image paths. + /// + /// The image paths to use to set as slideshow. + /// Thrown if any of COM methods failed to process successfully. + void SetDesktopSlideshow(string[] aszPaths); + + /// + /// Gets lock screen wallpaper using the specified image path. + /// + /// The image path to use to set as lock screen wallpaper. + /// Thrown if any of COM methods failed to process successfully. + Task SetLockScreenWallpaper(string szPath); + } +} diff --git a/src/Files.App/Data/Contracts/ImagingService.cs b/src/Files.App/Data/Contracts/ImagingService.cs index c4b6e4749f74..7970a72dd89e 100644 --- a/src/Files.App/Data/Contracts/ImagingService.cs +++ b/src/Files.App/Data/Contracts/ImagingService.cs @@ -1,8 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.Core.Storage; -using Files.Core.Storage.Storables; using Files.Shared.Utils; using Windows.Storage.FileProperties; @@ -13,18 +11,15 @@ internal sealed class ImagingService : IImageService /// public async Task GetIconAsync(IStorable storable, CancellationToken cancellationToken) { - if (storable is not ILocatableStorable locatableStorable) - return null; - - var iconData = await FileThumbnailHelper.LoadIconFromPathAsync(locatableStorable.Path, 24u, ThumbnailMode.ListView, ThumbnailOptions.ResizeThumbnail); + var iconData = await FileThumbnailHelper.GetIconAsync(storable.Id, Constants.ShellIconSizes.Small, storable is IFolder, IconOptions.ReturnIconOnly | IconOptions.UseCurrentScale); if (iconData is null) return null; var bitmapImage = await iconData.ToBitmapAsync(); - return new BitmapImageModel(bitmapImage); + return bitmapImage is null ? null : new BitmapImageModel(bitmapImage); } - public async Task GetImageModelFromDataAsync(byte[] rawData) + public async Task GetImageModelFromDataAsync(byte[]? rawData) { return new BitmapImageModel(await BitmapHelper.ToBitmapAsync(rawData)); } diff --git a/src/Files.App/Data/Enums/AccessControlEntryFlags.cs b/src/Files.App/Data/Enums/AccessControlEntryFlags.cs index 1f11ab3ffd4d..e1e5343ff818 100644 --- a/src/Files.App/Data/Enums/AccessControlEntryFlags.cs +++ b/src/Files.App/Data/Enums/AccessControlEntryFlags.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/AccessControlEntryType.cs b/src/Files.App/Data/Enums/AccessControlEntryType.cs index ee3ecc6076f2..8bb1351d928c 100644 --- a/src/Files.App/Data/Enums/AccessControlEntryType.cs +++ b/src/Files.App/Data/Enums/AccessControlEntryType.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/AccessControlPrincipalType.cs b/src/Files.App/Data/Enums/AccessControlPrincipalType.cs index 1af4946348c4..eab358401f7e 100644 --- a/src/Files.App/Data/Enums/AccessControlPrincipalType.cs +++ b/src/Files.App/Data/Enums/AccessControlPrincipalType.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/AccessMaskFlags.cs b/src/Files.App/Data/Enums/AccessMaskFlags.cs index 9b66811708b9..c8af2ef2ed93 100644 --- a/src/Files.App/Data/Enums/AccessMaskFlags.cs +++ b/src/Files.App/Data/Enums/AccessMaskFlags.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/ActionCategory.cs b/src/Files.App/Data/Enums/ActionCategory.cs new file mode 100644 index 000000000000..e7b2b1adbdb4 --- /dev/null +++ b/src/Files.App/Data/Enums/ActionCategory.cs @@ -0,0 +1,30 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Data.Enums +{ + public enum ActionCategory + { + Unspecified = 0, + Archive, + Display, + Edit, + FileSystem, + Grouping, + Git, + Image, + Install, + Layout, + Media, + Navigation, + Create, + Open, + Run, + Selection, + Show, + Sorting, + Start, + Window, + DualPane, + } +} \ No newline at end of file diff --git a/src/Files.App/Data/Enums/AddItemDialogItemType.cs b/src/Files.App/Data/Enums/AddItemDialogItemType.cs index 47889ded9def..793df6e25b9b 100644 --- a/src/Files.App/Data/Enums/AddItemDialogItemType.cs +++ b/src/Files.App/Data/Enums/AddItemDialogItemType.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/AppEnvironment.cs b/src/Files.App/Data/Enums/AppEnvironment.cs index 8ee11c048271..f00f89c3ec38 100644 --- a/src/Files.App/Data/Enums/AppEnvironment.cs +++ b/src/Files.App/Data/Enums/AppEnvironment.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { @@ -17,18 +17,23 @@ public enum AppEnvironment Dev, /// - /// Application distribution type is Stable. + /// Application distribution type is Sideload Stable. /// - Stable, + SideloadStable, /// - /// Application distribution type is Store. + /// Application distribution type is Store Stable. /// - Store, + StoreStable, /// - /// Application distribution type is Preview. + /// Application distribution type is Sideload Preview. /// - Preview + SideloadPreview, + + /// + /// Application distribution type is Store Preview. + /// + StorePreview } } diff --git a/src/Files.App/Data/Enums/ArchiveCompressionLevels.cs b/src/Files.App/Data/Enums/ArchiveCompressionLevels.cs index 74c695bf7784..3c85fa07f53f 100644 --- a/src/Files.App/Data/Enums/ArchiveCompressionLevels.cs +++ b/src/Files.App/Data/Enums/ArchiveCompressionLevels.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/ArchiveDictionarySizes.cs b/src/Files.App/Data/Enums/ArchiveDictionarySizes.cs new file mode 100644 index 000000000000..66e0fb0ac8b4 --- /dev/null +++ b/src/Files.App/Data/Enums/ArchiveDictionarySizes.cs @@ -0,0 +1,81 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Data.Enums +{ + /// + /// Defines constants that specify archive dictionary size for 7z LZMA/LZMA2. + /// + public enum ArchiveDictionarySizes + { + /// + /// Automatic (default based on compression level). + /// + Auto, + + /// + /// 64 KB dictionary. + /// + Kb64, + + /// + /// 256 KB dictionary. + /// + Kb256, + + /// + /// 1 MB dictionary. + /// + Mb1, + + /// + /// 2 MB dictionary. + /// + Mb2, + + /// + /// 4 MB dictionary. + /// + Mb4, + + /// + /// 8 MB dictionary. + /// + Mb8, + + /// + /// 16 MB dictionary. + /// + Mb16, + + /// + /// 32 MB dictionary. + /// + Mb32, + + /// + /// 64 MB dictionary. + /// + Mb64, + + /// + /// 128 MB dictionary. + /// + Mb128, + + /// + /// 256 MB dictionary. + /// + Mb256, + + /// + /// 512 MB dictionary. + /// + Mb512, + + /// + /// 1024 MB dictionary. + /// + Mb1024, + } +} diff --git a/src/Files.App/Data/Enums/ArchiveFormats.cs b/src/Files.App/Data/Enums/ArchiveFormats.cs index 5a354a7915f5..b72003354134 100644 --- a/src/Files.App/Data/Enums/ArchiveFormats.cs +++ b/src/Files.App/Data/Enums/ArchiveFormats.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/ArchiveSplittingSizes.cs b/src/Files.App/Data/Enums/ArchiveSplittingSizes.cs index 3c2386cf9fb4..ba618e46717c 100644 --- a/src/Files.App/Data/Enums/ArchiveSplittingSizes.cs +++ b/src/Files.App/Data/Enums/ArchiveSplittingSizes.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/ArchiveWordSizes.cs b/src/Files.App/Data/Enums/ArchiveWordSizes.cs new file mode 100644 index 000000000000..f79aad6724a6 --- /dev/null +++ b/src/Files.App/Data/Enums/ArchiveWordSizes.cs @@ -0,0 +1,51 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Data.Enums +{ + /// + /// Defines constants that specify archive word size (fast bytes) for 7z LZMA/LZMA2. + /// + public enum ArchiveWordSizes + { + /// + /// Automatic (default based on compression level). + /// + Auto, + + /// + /// 8 fast bytes. + /// + Fb8, + + /// + /// 16 fast bytes. + /// + Fb16, + + /// + /// 32 fast bytes. + /// + Fb32, + + /// + /// 64 fast bytes. + /// + Fb64, + + /// + /// 128 fast bytes. + /// + Fb128, + + /// + /// 256 fast bytes. + /// + Fb256, + + /// + /// 273 fast bytes (maximum). + /// + Fb273, + } +} diff --git a/src/Files.App/Data/Enums/BackdropMaterialType.cs b/src/Files.App/Data/Enums/BackdropMaterialType.cs index a38e37c59933..e4151a2b9fb4 100644 --- a/src/Files.App/Data/Enums/BackdropMaterialType.cs +++ b/src/Files.App/Data/Enums/BackdropMaterialType.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/CardsViewSizeKind.cs b/src/Files.App/Data/Enums/CardsViewSizeKind.cs new file mode 100644 index 000000000000..2f266326395e --- /dev/null +++ b/src/Files.App/Data/Enums/CardsViewSizeKind.cs @@ -0,0 +1,31 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Data.Enums +{ + /// + /// Defines constants that specify the size in the Cards View layout. + /// + public enum CardsViewSizeKind + { + /// + /// The size is small. + /// + Small = 1, + + /// + /// The size is medium. + /// + Medium = 2, + + /// + /// The size is large. + /// + Large = 3, + + /// + /// The size is extra large. + /// + ExtraLarge = 4, + } +} diff --git a/src/Files.App/Data/Enums/ColumnsViewSizeKind.cs b/src/Files.App/Data/Enums/ColumnsViewSizeKind.cs index c9b60990f54b..1b4cdec21fac 100644 --- a/src/Files.App/Data/Enums/ColumnsViewSizeKind.cs +++ b/src/Files.App/Data/Enums/ColumnsViewSizeKind.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/ContextMenuFlyoutItemType.cs b/src/Files.App/Data/Enums/ContextMenuFlyoutItemType.cs index 96d9f203a096..2abb1ce3605a 100644 --- a/src/Files.App/Data/Enums/ContextMenuFlyoutItemType.cs +++ b/src/Files.App/Data/Enums/ContextMenuFlyoutItemType.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/CopyEngineResult.cs b/src/Files.App/Data/Enums/CopyEngineResult.cs index 4a4f637365a6..9f04cc4f9400 100644 --- a/src/Files.App/Data/Enums/CopyEngineResult.cs +++ b/src/Files.App/Data/Enums/CopyEngineResult.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/DateTimeFormats.cs b/src/Files.App/Data/Enums/DateTimeFormats.cs index f36c54dcb0cf..42b5bfda56c2 100644 --- a/src/Files.App/Data/Enums/DateTimeFormats.cs +++ b/src/Files.App/Data/Enums/DateTimeFormats.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/DeleteConfirmationPolicy.cs b/src/Files.App/Data/Enums/DeleteConfirmationPolicy.cs index 35ed1880fb6c..a748203e6470 100644 --- a/src/Files.App/Data/Enums/DeleteConfirmationPolicy.cs +++ b/src/Files.App/Data/Enums/DeleteConfirmationPolicy.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/DetailsViewSizeKind.cs b/src/Files.App/Data/Enums/DetailsViewSizeKind.cs index b49c1cfd3dfc..9324c6ac7415 100644 --- a/src/Files.App/Data/Enums/DetailsViewSizeKind.cs +++ b/src/Files.App/Data/Enums/DetailsViewSizeKind.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/DialogResult.cs b/src/Files.App/Data/Enums/DialogResult.cs index 960a122079d1..225d8575a94e 100644 --- a/src/Files.App/Data/Enums/DialogResult.cs +++ b/src/Files.App/Data/Enums/DialogResult.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/DynamicDialogButtons.cs b/src/Files.App/Data/Enums/DynamicDialogButtons.cs index fa0219041e59..3b99bf389feb 100644 --- a/src/Files.App/Data/Enums/DynamicDialogButtons.cs +++ b/src/Files.App/Data/Enums/DynamicDialogButtons.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/DynamicDialogResult.cs b/src/Files.App/Data/Enums/DynamicDialogResult.cs index 9ca0e622fe72..5533cd408fff 100644 --- a/src/Files.App/Data/Enums/DynamicDialogResult.cs +++ b/src/Files.App/Data/Enums/DynamicDialogResult.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/FileNameConflictResolveOptionType.cs b/src/Files.App/Data/Enums/FileNameConflictResolveOptionType.cs index 5b0cec97b64f..c686f355bbaa 100644 --- a/src/Files.App/Data/Enums/FileNameConflictResolveOptionType.cs +++ b/src/Files.App/Data/Enums/FileNameConflictResolveOptionType.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/FileOperationType.cs b/src/Files.App/Data/Enums/FileOperationType.cs index 123003707b6e..dfa51b51c514 100644 --- a/src/Files.App/Data/Enums/FileOperationType.cs +++ b/src/Files.App/Data/Enums/FileOperationType.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { @@ -62,5 +62,15 @@ public enum FileOperationType : byte /// An item has been added to an archive ///
Compressed = 11, + + /// + /// A git repo has been cloned + /// + GitClone = 12, + + /// + /// A font has been installed + /// + InstallFont = 13, } } diff --git a/src/Files.App/Data/Enums/FileSystemStatusCode.cs b/src/Files.App/Data/Enums/FileSystemStatusCode.cs index 7365b66cd91e..2611d6283bb4 100644 --- a/src/Files.App/Data/Enums/FileSystemStatusCode.cs +++ b/src/Files.App/Data/Enums/FileSystemStatusCode.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/FilesystemItemType.cs b/src/Files.App/Data/Enums/FilesystemItemType.cs index 577b629273c1..74cae9db18b6 100644 --- a/src/Files.App/Data/Enums/FilesystemItemType.cs +++ b/src/Files.App/Data/Enums/FilesystemItemType.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/FilesystemOperationType.cs b/src/Files.App/Data/Enums/FilesystemOperationType.cs index 08623018b173..1c66418c929c 100644 --- a/src/Files.App/Data/Enums/FilesystemOperationType.cs +++ b/src/Files.App/Data/Enums/FilesystemOperationType.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/FolderLayoutModes.cs b/src/Files.App/Data/Enums/FolderLayoutModes.cs index 58a4d805887b..46d61ab87a75 100644 --- a/src/Files.App/Data/Enums/FolderLayoutModes.cs +++ b/src/Files.App/Data/Enums/FolderLayoutModes.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { @@ -16,9 +16,9 @@ public enum FolderLayoutModes ListView = 1, /// - /// Tiles view + /// Cards view /// - TilesView = 2, + CardsView = 2, /// /// Column view diff --git a/src/Files.App/Data/Enums/GitCheckoutOptions.cs b/src/Files.App/Data/Enums/GitCheckoutOptions.cs index e9f6384d5bf5..9a2abdcdef55 100644 --- a/src/Files.App/Data/Enums/GitCheckoutOptions.cs +++ b/src/Files.App/Data/Enums/GitCheckoutOptions.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { @@ -23,6 +23,11 @@ public enum GitCheckoutOptions /// DiscardChanges, + /// + /// Abort merge and check out to the branch. + /// + AbortMerge, + /// /// No operation to perform. /// diff --git a/src/Files.App/Data/Enums/GitOperationResult.cs b/src/Files.App/Data/Enums/GitOperationResult.cs index e1e0cdb48e28..1e6a7ecfcb7a 100644 --- a/src/Files.App/Data/Enums/GitOperationResult.cs +++ b/src/Files.App/Data/Enums/GitOperationResult.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/GridViewSizeKind.cs b/src/Files.App/Data/Enums/GridViewSizeKind.cs index 49f39bd52fff..91fd393a608c 100644 --- a/src/Files.App/Data/Enums/GridViewSizeKind.cs +++ b/src/Files.App/Data/Enums/GridViewSizeKind.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/GroupByDateUnit.cs b/src/Files.App/Data/Enums/GroupByDateUnit.cs index 47945d4d2834..fdc226ab3cdf 100644 --- a/src/Files.App/Data/Enums/GroupByDateUnit.cs +++ b/src/Files.App/Data/Enums/GroupByDateUnit.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/GroupOption.cs b/src/Files.App/Data/Enums/GroupOption.cs index 2e0dd857ee5b..8b62ee92870d 100644 --- a/src/Files.App/Data/Enums/GroupOption.cs +++ b/src/Files.App/Data/Enums/GroupOption.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/IconOptions.cs b/src/Files.App/Data/Enums/IconOptions.cs index 7dd2d3eb65a1..0f4298637668 100644 --- a/src/Files.App/Data/Enums/IconOptions.cs +++ b/src/Files.App/Data/Enums/IconOptions.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/IconPersistenceOptions.cs b/src/Files.App/Data/Enums/IconPersistenceOptions.cs index caeee020fd05..efd0fc8f3b97 100644 --- a/src/Files.App/Data/Enums/IconPersistenceOptions.cs +++ b/src/Files.App/Data/Enums/IconPersistenceOptions.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/ImpossibleActionResponseTypes.cs b/src/Files.App/Data/Enums/ImpossibleActionResponseTypes.cs index 4a3fd03dcec8..c39841f25929 100644 --- a/src/Files.App/Data/Enums/ImpossibleActionResponseTypes.cs +++ b/src/Files.App/Data/Enums/ImpossibleActionResponseTypes.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/InfoPaneTabs.cs b/src/Files.App/Data/Enums/InfoPaneTabs.cs index 3abec135026e..0eeb7103d501 100644 --- a/src/Files.App/Data/Enums/InfoPaneTabs.cs +++ b/src/Files.App/Data/Enums/InfoPaneTabs.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/ListViewSizeKind.cs b/src/Files.App/Data/Enums/ListViewSizeKind.cs index 2833a7cf5bac..b3de53d4b60c 100644 --- a/src/Files.App/Data/Enums/ListViewSizeKind.cs +++ b/src/Files.App/Data/Enums/ListViewSizeKind.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/OpenFoldersWithOneClickEnum.cs b/src/Files.App/Data/Enums/OpenFoldersWithOneClickEnum.cs new file mode 100644 index 000000000000..c791aabe7064 --- /dev/null +++ b/src/Files.App/Data/Enums/OpenFoldersWithOneClickEnum.cs @@ -0,0 +1,23 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Data.Enums +{ + public enum OpenFoldersWithOneClickEnum + { + /// + /// Open folders with a single click in Columns View. + /// + OnlyInColumnsView, + + /// + /// Always open folders with a single click. + /// + Always, + + /// + /// Never open folders with a single click. + /// + Never, + } +} diff --git a/src/Files.App/Data/Enums/OpenInIDEOption.cs b/src/Files.App/Data/Enums/OpenInIDEOption.cs index f55c68a418bd..8d81970510bd 100644 --- a/src/Files.App/Data/Enums/OpenInIDEOption.cs +++ b/src/Files.App/Data/Enums/OpenInIDEOption.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/ParsedCommandType.cs b/src/Files.App/Data/Enums/ParsedCommandType.cs index 0c4d46f39991..26d9e6d02dc6 100644 --- a/src/Files.App/Data/Enums/ParsedCommandType.cs +++ b/src/Files.App/Data/Enums/ParsedCommandType.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/PowerShellExecutionOptions.cs b/src/Files.App/Data/Enums/PowerShellExecutionOptions.cs new file mode 100644 index 000000000000..6b585b5ccc67 --- /dev/null +++ b/src/Files.App/Data/Enums/PowerShellExecutionOptions.cs @@ -0,0 +1,27 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Data.Enums +{ + /// + /// Represents options for PowerShell execution. + /// + [Flags] + public enum PowerShellExecutionOptions + { + /// + /// Default options. + /// + None = 0x0, + + /// + /// Run the PowerShell command hidden. + /// + Hidden = 0x1, + + /// + /// Run PowerShell with elevated privileges. + /// + Elevated = 0x2 + } +} \ No newline at end of file diff --git a/src/Files.App/Data/Enums/PreviewPaneStates.cs b/src/Files.App/Data/Enums/PreviewPaneStates.cs index 36e076f797dd..8978263edb6d 100644 --- a/src/Files.App/Data/Enums/PreviewPaneStates.cs +++ b/src/Files.App/Data/Enums/PreviewPaneStates.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { @@ -31,6 +31,11 @@ public enum PreviewPaneStates /// /// Loading preview status. /// - LoadingPreview + LoadingPreview, + + /// + /// Drive preview and details available status. + /// + DriveStorageDetailsAvailable, } } diff --git a/src/Files.App/Data/Enums/PropertiesNavigationViewItemType.cs b/src/Files.App/Data/Enums/PropertiesNavigationViewItemType.cs index 9df54411253f..811d70c3474e 100644 --- a/src/Files.App/Data/Enums/PropertiesNavigationViewItemType.cs +++ b/src/Files.App/Data/Enums/PropertiesNavigationViewItemType.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { @@ -47,5 +47,10 @@ public enum PropertiesNavigationViewItemType /// Shortcut page type ///
Shortcut, + + /// + /// Signatures page type + /// + Signatures, } } diff --git a/src/Files.App/Data/Enums/ReturnResult.cs b/src/Files.App/Data/Enums/ReturnResult.cs index 743d3f34d118..91c4db236315 100644 --- a/src/Files.App/Data/Enums/ReturnResult.cs +++ b/src/Files.App/Data/Enums/ReturnResult.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/SearchBoxTextChangeReason.cs b/src/Files.App/Data/Enums/SearchBoxTextChangeReason.cs index 06632a610671..4c2895f95db3 100644 --- a/src/Files.App/Data/Enums/SearchBoxTextChangeReason.cs +++ b/src/Files.App/Data/Enums/SearchBoxTextChangeReason.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/SettingsPageKind.cs b/src/Files.App/Data/Enums/SettingsPageKind.cs index 49f690a461d0..386c9409409e 100644 --- a/src/Files.App/Data/Enums/SettingsPageKind.cs +++ b/src/Files.App/Data/Enums/SettingsPageKind.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/ShellPaneArrangement.cs b/src/Files.App/Data/Enums/ShellPaneArrangement.cs index 760a567cf8cd..81fc0f8e7550 100644 --- a/src/Files.App/Data/Enums/ShellPaneArrangement.cs +++ b/src/Files.App/Data/Enums/ShellPaneArrangement.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/SizeUnitTypes.cs b/src/Files.App/Data/Enums/SizeUnitTypes.cs new file mode 100644 index 000000000000..d3f93c2f176a --- /dev/null +++ b/src/Files.App/Data/Enums/SizeUnitTypes.cs @@ -0,0 +1,18 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Data.Enums +{ + public enum SizeUnitTypes + { + /// + /// Displays sizes using Binary values. + /// + BinaryUnits, + + /// + /// Displays sizes using Decimal values. + /// + DecimalUnits, + } +} diff --git a/src/Files.App/Data/Enums/SortDirection.cs b/src/Files.App/Data/Enums/SortDirection.cs index ea3ef579dbf2..e3593cfa8586 100644 --- a/src/Files.App/Data/Enums/SortDirection.cs +++ b/src/Files.App/Data/Enums/SortDirection.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/SortOption.cs b/src/Files.App/Data/Enums/SortOption.cs index ddb2c9a205f7..8eafffe0971d 100644 --- a/src/Files.App/Data/Enums/SortOption.cs +++ b/src/Files.App/Data/Enums/SortOption.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/StatusCenterItemIconKind.cs b/src/Files.App/Data/Enums/StatusCenterItemIconKind.cs index 672139c75a0e..a24585ebbce7 100644 --- a/src/Files.App/Data/Enums/StatusCenterItemIconKind.cs +++ b/src/Files.App/Data/Enums/StatusCenterItemIconKind.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { @@ -16,5 +16,7 @@ public enum StatusCenterItemIconKind Compress, Successful, Error, + GitClone, + InstallFont } } diff --git a/src/Files.App/Data/Enums/StatusCenterItemKind.cs b/src/Files.App/Data/Enums/StatusCenterItemKind.cs index a837782b8284..0dec9f20a37b 100644 --- a/src/Files.App/Data/Enums/StatusCenterItemKind.cs +++ b/src/Files.App/Data/Enums/StatusCenterItemKind.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/StatusCenterVisibility.cs b/src/Files.App/Data/Enums/StatusCenterVisibility.cs new file mode 100644 index 000000000000..5266d5c9342b --- /dev/null +++ b/src/Files.App/Data/Enums/StatusCenterVisibility.cs @@ -0,0 +1,18 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Data.Enums +{ + public enum StatusCenterVisibility + { + /// + /// Always displayed. + /// + Always, + + /// + /// During active file operations. + /// + DuringOngoingFileOperations, + } +} diff --git a/src/Files.App/Data/Enums/TilesViewSizeKind.cs b/src/Files.App/Data/Enums/TilesViewSizeKind.cs deleted file mode 100644 index 896de5d2591b..000000000000 --- a/src/Files.App/Data/Enums/TilesViewSizeKind.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -namespace Files.App.Data.Enums -{ - /// - /// Defines constants that specify the size in the Tiles View layout. - /// - public enum TilesViewSizeKind - { - /// - /// The size is small. - /// - Small = 1, - } -} diff --git a/src/Files.App/Data/Enums/WallpaperType.cs b/src/Files.App/Data/Enums/WallpaperType.cs index ea8bf18bee2d..3b7c0ec396ef 100644 --- a/src/Files.App/Data/Enums/WallpaperType.cs +++ b/src/Files.App/Data/Enums/WallpaperType.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/Enums/WindowsCompatibilityModes.cs b/src/Files.App/Data/Enums/WindowsCompatibilityModes.cs index 0da8c6e9a0d7..743df05e83cc 100644 --- a/src/Files.App/Data/Enums/WindowsCompatibilityModes.cs +++ b/src/Files.App/Data/Enums/WindowsCompatibilityModes.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Enums { diff --git a/src/Files.App/Data/EventArguments/AddressBarTextEnteredEventArgs.cs b/src/Files.App/Data/EventArguments/AddressBarTextEnteredEventArgs.cs deleted file mode 100644 index e9ed5144a6c2..000000000000 --- a/src/Files.App/Data/EventArguments/AddressBarTextEnteredEventArgs.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.App.Views; -using Microsoft.UI.Xaml.Controls; -using Windows.ApplicationModel.DataTransfer; - -namespace Files.App.Data.EventArguments -{ - public sealed class AddressBarTextEnteredEventArgs - { - public AutoSuggestBox AddressBarTextField { get; set; } - } -} diff --git a/src/Files.App/Data/EventArguments/CurrentInstanceChangedEventArgs.cs b/src/Files.App/Data/EventArguments/CurrentInstanceChangedEventArgs.cs index fd61c59cfb2c..b4a2154d6ee3 100644 --- a/src/Files.App/Data/EventArguments/CurrentInstanceChangedEventArgs.cs +++ b/src/Files.App/Data/EventArguments/CurrentInstanceChangedEventArgs.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.EventArguments { diff --git a/src/Files.App/Data/EventArguments/EventArrivedEventArgs.cs b/src/Files.App/Data/EventArguments/EventArrivedEventArgs.cs index c946311a1a4d..7e8052a69bde 100644 --- a/src/Files.App/Data/EventArguments/EventArrivedEventArgs.cs +++ b/src/Files.App/Data/EventArguments/EventArrivedEventArgs.cs @@ -1,8 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.Management.Infrastructure; -using System; namespace Files.App.Data.EventArguments { diff --git a/src/Files.App/Data/EventArguments/LayoutModeEventArgs.cs b/src/Files.App/Data/EventArguments/LayoutModeEventArgs.cs index b2973490e58d..9b9db69068b2 100644 --- a/src/Files.App/Data/EventArguments/LayoutModeEventArgs.cs +++ b/src/Files.App/Data/EventArguments/LayoutModeEventArgs.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.EventArguments { diff --git a/src/Files.App/Data/EventArguments/LayoutPreferenceEventArgs.cs b/src/Files.App/Data/EventArguments/LayoutPreferenceEventArgs.cs index 3f6353a3dcc7..8bfa0a83a148 100644 --- a/src/Files.App/Data/EventArguments/LayoutPreferenceEventArgs.cs +++ b/src/Files.App/Data/EventArguments/LayoutPreferenceEventArgs.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.EventArguments { diff --git a/src/Files.App/Data/EventArguments/MainPageNavigationArguments.cs b/src/Files.App/Data/EventArguments/MainPageNavigationArguments.cs index 4d08b11831d1..b931f519a452 100644 --- a/src/Files.App/Data/EventArguments/MainPageNavigationArguments.cs +++ b/src/Files.App/Data/EventArguments/MainPageNavigationArguments.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.EventArguments { diff --git a/src/Files.App/Data/EventArguments/NavigationArguments.cs b/src/Files.App/Data/EventArguments/NavigationArguments.cs index f3815c070df1..02a606fb6f95 100644 --- a/src/Files.App/Data/EventArguments/NavigationArguments.cs +++ b/src/Files.App/Data/EventArguments/NavigationArguments.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.EventArguments { diff --git a/src/Files.App/Data/EventArguments/PaneNavigationArguments.cs b/src/Files.App/Data/EventArguments/PaneNavigationArguments.cs index ad5aa8bffe9b..cfb588340165 100644 --- a/src/Files.App/Data/EventArguments/PaneNavigationArguments.cs +++ b/src/Files.App/Data/EventArguments/PaneNavigationArguments.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.EventArguments { @@ -34,5 +34,15 @@ internal sealed class PaneNavigationArguments { return !(a1 == a2); } + + public override bool Equals(object? obj) + { + return obj is PaneNavigationArguments args && this == args; + } + + public override int GetHashCode() + { + return HashCode.Combine(LeftPaneNavPathParam, LeftPaneSelectItemParam, RightPaneNavPathParam, RightPaneSelectItemParam, ShellPaneArrangement); + } } } diff --git a/src/Files.App/Data/EventArguments/PathBoxItemDroppedEventArgs.cs b/src/Files.App/Data/EventArguments/PathBoxItemDroppedEventArgs.cs index 3869bd21b4cb..e9b2c2c71439 100644 --- a/src/Files.App/Data/EventArguments/PathBoxItemDroppedEventArgs.cs +++ b/src/Files.App/Data/EventArguments/PathBoxItemDroppedEventArgs.cs @@ -1,9 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.App.Views; using Files.Shared.Helpers; -using Microsoft.UI.Xaml.Controls; using Windows.ApplicationModel.DataTransfer; namespace Files.App.Data.EventArguments diff --git a/src/Files.App/Data/EventArguments/PathNavigationEventArgs.cs b/src/Files.App/Data/EventArguments/PathNavigationEventArgs.cs index 93ef43f12876..f44bd27aeb46 100644 --- a/src/Files.App/Data/EventArguments/PathNavigationEventArgs.cs +++ b/src/Files.App/Data/EventArguments/PathNavigationEventArgs.cs @@ -1,9 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.App.Views; -using Microsoft.UI.Xaml.Controls; -using Windows.ApplicationModel.DataTransfer; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.EventArguments { diff --git a/src/Files.App/Data/EventArguments/QuickAccessCardEventArgs.cs b/src/Files.App/Data/EventArguments/QuickAccessCardEventArgs.cs index 3427ca2617c9..53f2f7e16433 100644 --- a/src/Files.App/Data/EventArguments/QuickAccessCardEventArgs.cs +++ b/src/Files.App/Data/EventArguments/QuickAccessCardEventArgs.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.EventArguments { diff --git a/src/Files.App/Data/EventArguments/SearchBoxQuerySubmittedEventArgs.cs b/src/Files.App/Data/EventArguments/SearchBoxQuerySubmittedEventArgs.cs index d0dbf3f4f1b5..a52f3154361d 100644 --- a/src/Files.App/Data/EventArguments/SearchBoxQuerySubmittedEventArgs.cs +++ b/src/Files.App/Data/EventArguments/SearchBoxQuerySubmittedEventArgs.cs @@ -1,9 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.App.Data.Models; -using Microsoft.UI.Xaml.Controls; -using Windows.Foundation; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.EventArguments { diff --git a/src/Files.App/Data/EventArguments/SearchBoxTextChangedEventArgs.cs b/src/Files.App/Data/EventArguments/SearchBoxTextChangedEventArgs.cs index c4a313c1f07c..3583e3d46a5a 100644 --- a/src/Files.App/Data/EventArguments/SearchBoxTextChangedEventArgs.cs +++ b/src/Files.App/Data/EventArguments/SearchBoxTextChangedEventArgs.cs @@ -1,9 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.App.Data.Models; using Microsoft.UI.Xaml.Controls; -using Windows.Foundation; namespace Files.App.Data.EventArguments { diff --git a/src/Files.App/Data/EventArguments/SelectedTagChangedEventArgs.cs b/src/Files.App/Data/EventArguments/SelectedTagChangedEventArgs.cs index 62db00201299..5501cb49223f 100644 --- a/src/Files.App/Data/EventArguments/SelectedTagChangedEventArgs.cs +++ b/src/Files.App/Data/EventArguments/SelectedTagChangedEventArgs.cs @@ -1,7 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.EventArguments { - public record SelectedTagChangedEventArgs(IEnumerable<(string path, bool isFolder)> Items); + public record SelectedTagChangedEventArgs(IEnumerable<(string path, bool isFolder)> Items); } diff --git a/src/Files.App/Data/EventArguments/SettingChangedEventArgs.cs b/src/Files.App/Data/EventArguments/SettingChangedEventArgs.cs index 2eb9f8cf82f7..d44e774d0ca5 100644 --- a/src/Files.App/Data/EventArguments/SettingChangedEventArgs.cs +++ b/src/Files.App/Data/EventArguments/SettingChangedEventArgs.cs @@ -1,7 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.EventArguments { diff --git a/src/Files.App/Data/EventArguments/ToolbarFlyoutOpenedEventArgs.cs b/src/Files.App/Data/EventArguments/ToolbarFlyoutOpenedEventArgs.cs deleted file mode 100644 index 36abd8bdb6d5..000000000000 --- a/src/Files.App/Data/EventArguments/ToolbarFlyoutOpenedEventArgs.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.App.Views; -using Microsoft.UI.Xaml.Controls; -using Windows.ApplicationModel.DataTransfer; - -namespace Files.App.Data.EventArguments -{ - public sealed class ToolbarFlyoutOpenedEventArgs - { - public MenuFlyout OpenedFlyout { get; set; } - } -} diff --git a/src/Files.App/Data/EventArguments/ToolbarPathItemLoadedEventArgs.cs b/src/Files.App/Data/EventArguments/ToolbarPathItemLoadedEventArgs.cs deleted file mode 100644 index 248ed43c0225..000000000000 --- a/src/Files.App/Data/EventArguments/ToolbarPathItemLoadedEventArgs.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.App.Views; -using Microsoft.UI.Xaml.Controls; -using Windows.ApplicationModel.DataTransfer; - -namespace Files.App.Data.EventArguments -{ - public sealed class ToolbarPathItemLoadedEventArgs - { - public MenuFlyout OpenedFlyout { get; set; } - - public PathBoxItem Item { get; set; } - } -} diff --git a/src/Files.App/Data/EventArguments/ToolbarQuerySubmittedEventArgs.cs b/src/Files.App/Data/EventArguments/ToolbarQuerySubmittedEventArgs.cs index 74d5fe23f961..8e83c9b4f6f9 100644 --- a/src/Files.App/Data/EventArguments/ToolbarQuerySubmittedEventArgs.cs +++ b/src/Files.App/Data/EventArguments/ToolbarQuerySubmittedEventArgs.cs @@ -1,9 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.App.Views; -using Microsoft.UI.Xaml.Controls; -using Windows.ApplicationModel.DataTransfer; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.EventArguments { diff --git a/src/Files.App/Data/EventArguments/WidgetsRightClickedItemChangedEventArgs.cs b/src/Files.App/Data/EventArguments/WidgetsRightClickedItemChangedEventArgs.cs index 92563156256e..6184e211d129 100644 --- a/src/Files.App/Data/EventArguments/WidgetsRightClickedItemChangedEventArgs.cs +++ b/src/Files.App/Data/EventArguments/WidgetsRightClickedItemChangedEventArgs.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Controls; diff --git a/src/Files.App/Data/Exceptions/FileAlreadyExistsException.cs b/src/Files.App/Data/Exceptions/FileAlreadyExistsException.cs deleted file mode 100644 index f95274d09fa4..000000000000 --- a/src/Files.App/Data/Exceptions/FileAlreadyExistsException.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.IO; - -namespace Files.App.Data.Exceptions -{ - public sealed class FileAlreadyExistsException : IOException - { - public string FileName { get; private set; } - - public FileAlreadyExistsException(string message, string fileName) : base(message) - { - FileName = fileName; - } - } -} diff --git a/src/Files.App/Data/Factories/AppThemeResourcesFactory.cs b/src/Files.App/Data/Factories/AppThemeResourcesFactory.cs index d87f6ee44c82..f1fbbbaaac54 100644 --- a/src/Files.App/Data/Factories/AppThemeResourcesFactory.cs +++ b/src/Files.App/Data/Factories/AppThemeResourcesFactory.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Factories { @@ -10,102 +10,102 @@ public static class AppThemeResourceFactory new AppThemeResourceItem { BackgroundColor = "#00000000", /* Transparent */ - Name = "Default".GetLocalizedResource() + Name = Strings.Default.GetLocalizedResource() }, new AppThemeResourceItem { BackgroundColor = "#32FFB900", /* #FFB900 */ - Name = "YellowGold".GetLocalizedResource() + Name = Strings.YellowGold.GetLocalizedResource() }, new AppThemeResourceItem { BackgroundColor = "#32F7630C", /* #F7630C */ - Name = "OrangeBright".GetLocalizedResource() + Name = Strings.OrangeBright.GetLocalizedResource() }, new AppThemeResourceItem { BackgroundColor = "#32D13438", /* #D13438 */ - Name = "BrickRed".GetLocalizedResource() + Name = Strings.BrickRed.GetLocalizedResource() }, new AppThemeResourceItem { BackgroundColor = "#32FF4343", /* #FF4343 */ - Name = "ModRed".GetLocalizedResource() + Name = Strings.ModRed.GetLocalizedResource() }, new AppThemeResourceItem { BackgroundColor = "#32EA005E", /* #EA005E */ - Name = "Red".GetLocalizedResource() + Name = Strings.Red.GetLocalizedResource() }, new AppThemeResourceItem { BackgroundColor = "#32EA005E", /* #EA005E */ - Name = "RoseBright".GetLocalizedResource() + Name = Strings.RoseBright.GetLocalizedResource() }, new AppThemeResourceItem { BackgroundColor = "#320078D7", /* #0078D7 */ - Name = "Blue".GetLocalizedResource() + Name = Strings.Blue.GetLocalizedResource() }, new AppThemeResourceItem { BackgroundColor = "#328764B8", /* #8764B8 */ - Name = "IrisPastel".GetLocalizedResource() + Name = Strings.IrisPastel.GetLocalizedResource() }, new AppThemeResourceItem { BackgroundColor = "#32B146C2", /* #B146C2 */ - Name = "VioletRedLight".GetLocalizedResource() + Name = Strings.VioletRedLight.GetLocalizedResource() }, new AppThemeResourceItem { BackgroundColor = "#320099BC", /* #0099BC */ - Name = "CoolBlueBright".GetLocalizedResource() + Name = Strings.CoolBlueBright.GetLocalizedResource() }, new AppThemeResourceItem { BackgroundColor = "#3200B7C3", /* #00B7C3 */ - Name = "Seafoam".GetLocalizedResource() + Name = Strings.Seafoam.GetLocalizedResource() }, new AppThemeResourceItem { BackgroundColor = "#3200B294", /* #00B294 */ - Name = "MintLight".GetLocalizedResource() + Name = Strings.MintLight.GetLocalizedResource() }, new AppThemeResourceItem { BackgroundColor = "#327A7574", /* #7A7574 */ - Name = "Gray".GetLocalizedResource() + Name = Strings.Gray.GetLocalizedResource() }, new AppThemeResourceItem { BackgroundColor = "#32107C10", /* #107C10 */ - Name = "Green".GetLocalizedResource() + Name = Strings.Green.GetLocalizedResource() }, new AppThemeResourceItem { BackgroundColor = "#32767676", /* #767676 */ - Name = "Overcast".GetLocalizedResource() + Name = Strings.Overcast.GetLocalizedResource() }, new AppThemeResourceItem { BackgroundColor = "#324C4A48", /* #4C4A48 */ - Name = "Storm".GetLocalizedResource() + Name = Strings.Storm.GetLocalizedResource() }, new AppThemeResourceItem { BackgroundColor = "#3269797E", /* #69797E */ - Name = "BlueGray".GetLocalizedResource() + Name = Strings.BlueGray.GetLocalizedResource() }, new AppThemeResourceItem { BackgroundColor = "#324A5459", /* #4A5459 */ - Name = "GrayDark".GetLocalizedResource() + Name = Strings.GrayDark.GetLocalizedResource() }, new AppThemeResourceItem { BackgroundColor = "#327E735F", /* #7E735F */ - Name = "Camouflage".GetLocalizedResource() + Name = Strings.Camouflage.GetLocalizedResource() } ]; } diff --git a/src/Files.App/Data/Factories/ContentPageContextFlyoutFactory.cs b/src/Files.App/Data/Factories/ContentPageContextFlyoutFactory.cs index 22cac88fe906..c587b97598ad 100644 --- a/src/Files.App/Data/Factories/ContentPageContextFlyoutFactory.cs +++ b/src/Files.App/Data/Factories/ContentPageContextFlyoutFactory.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.Helpers.ContextFlyouts; using Files.App.ViewModels.Layouts; @@ -98,9 +98,18 @@ public static List GetBaseItemMenuItems( return new List() { + new ContextMenuFlyoutItemViewModelBuilder(Commands.CloseActivePane) + { + IsVisible = !itemsSelected && Commands.CloseActivePane.IsExecutable, + }.Build(), new ContextMenuFlyoutItemViewModel() { - Text = "Layout".GetLocalizedResource(), + ItemType = ContextMenuFlyoutItemType.Separator, + ShowItem = !itemsSelected && Commands.CloseActivePane.IsExecutable + }, + new ContextMenuFlyoutItemViewModel() + { + Text = Strings.Layout.GetLocalizedResource(), Glyph = "\uE8A9", ShowItem = !itemsSelected, ShowInRecycleBin = true, @@ -113,7 +122,7 @@ public static List GetBaseItemMenuItems( { IsToggle = true }.Build(), - new ContextMenuFlyoutItemViewModelBuilder(Commands.LayoutTiles) + new ContextMenuFlyoutItemViewModelBuilder(Commands.LayoutCards) { IsToggle = true }.Build(), @@ -137,10 +146,10 @@ public static List GetBaseItemMenuItems( }, new ContextMenuFlyoutItemViewModel() { - Text = "SortBy".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() + Text = Strings.SortBy.GetLocalizedResource(), + ThemedIconModel = new ThemedIconModel() { - OpacityIconStyle = "ColorIconSort", + ThemedIconStyle = "App.ThemedIcons.Sorting", }, ShowItem = !itemsSelected, ShowInRecycleBin = true, @@ -209,7 +218,7 @@ public static List GetBaseItemMenuItems( }, new ContextMenuFlyoutItemViewModel() { - Text = "GroupBy".GetLocalizedResource(), + Text = Strings.GroupBy.GetLocalizedResource(), Glyph = "\uF168", ShowItem = !itemsSelected, ShowInRecycleBin = true, @@ -228,7 +237,7 @@ public static List GetBaseItemMenuItems( }.Build(), new ContextMenuFlyoutItemViewModel() { - Text = "DateModifiedLowerCase".GetLocalizedResource(), + Text = Strings.DateModifiedLowerCase.GetLocalizedResource(), ShowInRecycleBin = true, ShowInSearchPage = true, ShowInFtpPage = true, @@ -251,7 +260,7 @@ public static List GetBaseItemMenuItems( }, new ContextMenuFlyoutItemViewModel() { - Text = "DateCreated".GetLocalizedResource(), + Text = Strings.DateCreated.GetLocalizedResource(), ShowInRecycleBin = true, ShowInSearchPage = true, ShowInFtpPage = true, @@ -294,7 +303,7 @@ public static List GetBaseItemMenuItems( }.Build(), new ContextMenuFlyoutItemViewModel() { - Text = "DateDeleted".GetLocalizedResource(), + Text = Strings.DateDeleted.GetLocalizedResource(), ShowInRecycleBin = true, IsHidden = !currentInstanceViewModel.IsPageTypeRecycleBin, Items = @@ -350,16 +359,15 @@ public static List GetBaseItemMenuItems( }, new ContextMenuFlyoutItemViewModel() { - OpacityIcon = new OpacityIconModel() + ThemedIconModel = new ThemedIconModel() { - OpacityIconStyle = Commands.AddItem.Glyph.OpacityStyle + ThemedIconStyle = Commands.AddItem.Glyph.ThemedIconStyle }, Text = Commands.AddItem.Label, Items = GetNewItemItems(commandsViewModel, currentInstanceViewModel.CanCreateFileInPage), ShowItem = !itemsSelected, ShowInFtpPage = true }, - new ContextMenuFlyoutItemViewModelBuilder(Commands.FormatDrive).Build(), new ContextMenuFlyoutItemViewModelBuilder(Commands.EmptyRecycleBin) { IsVisible = currentInstanceViewModel.IsPageTypeRecycleBin && !itemsSelected, @@ -381,9 +389,9 @@ public static List GetBaseItemMenuItems( { // TODO add back text and icon when https://github.com/microsoft/microsoft-ui-xaml/issues/9409 is resolved //Text = "OpenWith".GetLocalizedResource(), - //OpacityIcon = new OpacityIconModel() + //ThemedIconModel = new ThemedIconModel() //{ - // OpacityIconStyle = "ColorIconOpenWith" + // ThemedIconStyle = "ColorIconOpenWith" //}, Tag = "OpenWithOverflow", IsHidden = true, @@ -399,12 +407,21 @@ public static List GetBaseItemMenuItems( ShowItem = itemsSelected && showOpenItemWith }, new ContextMenuFlyoutItemViewModelBuilder(Commands.OpenFileLocation).Build(), - new ContextMenuFlyoutItemViewModelBuilder(Commands.OpenInNewTabAction).Build(), - new ContextMenuFlyoutItemViewModelBuilder(Commands.OpenInNewWindowAction).Build(), - new ContextMenuFlyoutItemViewModelBuilder(Commands.OpenInNewPaneAction).Build(), + new ContextMenuFlyoutItemViewModelBuilder(Commands.OpenInNewTab) + { + IsVisible = UserSettingsService.GeneralSettingsService.ShowOpenInNewTab && Commands.OpenInNewTab.IsExecutable + }.Build(), + new ContextMenuFlyoutItemViewModelBuilder(Commands.OpenInNewWindow) + { + IsVisible = UserSettingsService.GeneralSettingsService.ShowOpenInNewWindow && Commands.OpenInNewWindow.IsExecutable + }.Build(), + new ContextMenuFlyoutItemViewModelBuilder(Commands.OpenInNewPane) + { + IsVisible = UserSettingsService.GeneralSettingsService.ShowOpenInNewPane && Commands.OpenInNewPane.IsExecutable + }.Build(), new ContextMenuFlyoutItemViewModel() { - Text = "BaseLayoutItemContextFlyoutSetAs/Text".GetLocalizedResource(), + Text = Strings.BaseLayoutItemContextFlyoutSetAs_Text.GetLocalizedResource(), ShowItem = itemsSelected && (selectedItemsPropertiesViewModel?.IsCompatibleToSetAsWindowsWallpaper ?? false), ShowInSearchPage = true, Items = @@ -440,17 +457,21 @@ public static List GetBaseItemMenuItems( new ContextMenuFlyoutItemViewModelBuilder(Commands.CutItem) { IsPrimary = true, + AccessKey="X" }.Build(), new ContextMenuFlyoutItemViewModelBuilder(Commands.CopyItem) { IsPrimary = true, + AccessKey="C" }.Build(), new ContextMenuFlyoutItemViewModelBuilder(Commands.PasteItemToSelection) { IsPrimary = true, + AccessKey="V", IsVisible = true, }.Build(), - new ContextMenuFlyoutItemViewModelBuilder(Commands.CopyPath) + new ContextMenuFlyoutItemViewModelBuilder(Commands.PasteItemAsShortcut).Build(), + new ContextMenuFlyoutItemViewModelBuilder(Commands.CopyItemPath) { IsVisible = UserSettingsService.GeneralSettingsService.ShowCopyPath && itemsSelected @@ -467,51 +488,59 @@ public static List GetBaseItemMenuItems( && (!selectedItems.FirstOrDefault()?.IsShortcut ?? false) && !currentInstanceViewModel.IsPageTypeRecycleBin, }.Build(), + new ContextMenuFlyoutItemViewModelBuilder(Commands.CreateAlternateDataStream) + { + IsVisible = UserSettingsService.GeneralSettingsService.ShowCreateAlternateDataStream && + Commands.CreateAlternateDataStream.IsExecutable, + }.Build(), new ContextMenuFlyoutItemViewModelBuilder(Commands.Rename) { IsPrimary = true, + AccessKey="M", IsVisible = itemsSelected }.Build(), new ContextMenuFlyoutItemViewModelBuilder(Commands.ShareItem) { - IsPrimary = true + IsPrimary = true, + AccessKey = "H" }.Build(), new ContextMenuFlyoutItemViewModelBuilder(ModifiableCommands.DeleteItem) { IsVisible = itemsSelected, IsPrimary = true, }.Build(), - new ContextMenuFlyoutItemViewModelBuilder(Commands.OpenProperties) + new ContextMenuFlyoutItemViewModelBuilder(ModifiableCommands.OpenProperties) { IsPrimary = true, - IsVisible = Commands.OpenProperties.IsExecutable + AccessKey="O", + IsVisible = ModifiableCommands.OpenProperties.IsExecutable }.Build(), new ContextMenuFlyoutItemViewModelBuilder(Commands.OpenParentFolder).Build(), new ContextMenuFlyoutItemViewModelBuilder(Commands.PinFolderToSidebar) { - IsVisible = Commands.PinFolderToSidebar.IsExecutable && UserSettingsService.GeneralSettingsService.ShowPinnedSection, + IsVisible = Commands.PinFolderToSidebar.IsExecutable && UserSettingsService.GeneralSettingsService.ShowPinnedSection && UserSettingsService.GeneralSettingsService.ShowPinToSideBar, }.Build(), new ContextMenuFlyoutItemViewModelBuilder(Commands.UnpinFolderFromSidebar) { - IsVisible = Commands.UnpinFolderFromSidebar.IsExecutable && UserSettingsService.GeneralSettingsService.ShowPinnedSection, + IsVisible = Commands.UnpinFolderFromSidebar.IsExecutable && UserSettingsService.GeneralSettingsService.ShowPinnedSection && UserSettingsService.GeneralSettingsService.ShowPinToSideBar, }.Build(), new ContextMenuFlyoutItemViewModelBuilder(Commands.PinToStart) { - IsVisible = selectedItems.All(x => (x.PrimaryItemAttribute == StorageItemTypes.Folder || x.IsExecutable || (x is ShortcutItem shortcutItem && FileExtensionHelpers.IsExecutableFile(shortcutItem.TargetPath))) && !x.IsArchive && !x.IsItemPinnedToStart), + IsVisible = selectedItems.All(x => (x.PrimaryItemAttribute == StorageItemTypes.Folder || x.IsExecutable || (x is IShortcutItem shortcutItem && FileExtensionHelpers.IsExecutableFile(shortcutItem.TargetPath))) && !x.IsArchive && !x.IsItemPinnedToStart) && UserSettingsService.GeneralSettingsService.ShowPinToStart, ShowOnShift = true, }.Build(), new ContextMenuFlyoutItemViewModelBuilder(Commands.UnpinFromStart) { - IsVisible = selectedItems.All(x => (x.PrimaryItemAttribute == StorageItemTypes.Folder || x.IsExecutable|| (x is ShortcutItem shortcutItem && FileExtensionHelpers.IsExecutableFile(shortcutItem.TargetPath))) && !x.IsArchive && x.IsItemPinnedToStart), + IsVisible = selectedItems.All(x => (x.PrimaryItemAttribute == StorageItemTypes.Folder || x.IsExecutable|| (x is IShortcutItem shortcutItem && FileExtensionHelpers.IsExecutableFile(shortcutItem.TargetPath))) && !x.IsArchive && x.IsItemPinnedToStart) && UserSettingsService.GeneralSettingsService.ShowPinToStart, ShowOnShift = true, }.Build(), new ContextMenuFlyoutItemViewModel { - Text = "Compress".GetLocalizedResource(), + Text = Strings.Compress.GetLocalizedResource(), ShowInSearchPage = true, - OpacityIcon = new OpacityIconModel() + ThemedIconModel = new ThemedIconModel() { - OpacityIconStyle = "ColorIconZip", + ThemedIconStyle = "App.ThemedIcons.Zip", }, Items = [ @@ -523,11 +552,11 @@ public static List GetBaseItemMenuItems( }, new ContextMenuFlyoutItemViewModel { - Text = "Extract".GetLocalizedResource(), + Text = Strings.Extract.GetLocalizedResource(), ShowInSearchPage = true, - OpacityIcon = new OpacityIconModel() + ThemedIconModel = new ThemedIconModel() { - OpacityIconStyle = "ColorIconZip", + ThemedIconStyle = "App.ThemedIcons.Zip", }, Items = [ @@ -538,9 +567,10 @@ public static List GetBaseItemMenuItems( ], ShowItem = UserSettingsService.GeneralSettingsService.ShowCompressionOptions && StorageArchiveService.CanDecompress(selectedItems) }, + new ContextMenuFlyoutItemViewModelBuilder(Commands.FlattenFolder).Build(), new ContextMenuFlyoutItemViewModel() { - Text = "SendTo".GetLocalizedResource(), + Text = Strings.SendTo.GetLocalizedResource(), Tag = "SendTo", CollapseLabel = true, ShowInSearchPage = true, @@ -548,7 +578,7 @@ public static List GetBaseItemMenuItems( }, new ContextMenuFlyoutItemViewModel() { - Text = "SendTo".GetLocalizedResource(), + Text = Strings.SendTo.GetLocalizedResource(), Tag = "SendToOverflow", IsHidden = true, CollapseLabel = true, @@ -564,7 +594,7 @@ public static List GetBaseItemMenuItems( }, new ContextMenuFlyoutItemViewModel() { - Text = "TurnOnBitLocker".GetLocalizedResource(), + Text = Strings.TurnOnBitLocker.GetLocalizedResource(), Tag = "TurnOnBitLockerPlaceholder", CollapseLabel = true, IsEnabled = false, @@ -572,15 +602,32 @@ public static List GetBaseItemMenuItems( }, new ContextMenuFlyoutItemViewModel() { - Text = "ManageBitLocker".GetLocalizedResource(), + Text = Strings.ManageBitLocker.GetLocalizedResource(), Tag = "ManageBitLockerPlaceholder", CollapseLabel = true, ShowItem = isDriveRoot, IsEnabled = false }, + new ContextMenuFlyoutItemViewModelBuilder(Commands.EditInNotepad).Build(), + new ContextMenuFlyoutItemViewModel() + { + ItemType = ContextMenuFlyoutItemType.Separator, + ShowItem = (!itemsSelected && Commands.OpenTerminal.IsExecutable && UserSettingsService.GeneralSettingsService.ShowOpenTerminal) || + (areAllItemsFolders && Commands.OpenTerminal.IsExecutable && UserSettingsService.GeneralSettingsService.ShowOpenTerminal) || + Commands.OpenStorageSense.IsExecutable || + Commands.FormatDrive.IsExecutable + }, + new ContextMenuFlyoutItemViewModelBuilder(Commands.OpenTerminal) + { + IsVisible = (!itemsSelected || areAllItemsFolders) && + Commands.OpenTerminal.IsExecutable && + UserSettingsService.GeneralSettingsService.ShowOpenTerminal + }.Build(), + new ContextMenuFlyoutItemViewModelBuilder(Commands.OpenStorageSense).Build(), + new ContextMenuFlyoutItemViewModelBuilder(Commands.FormatDrive).Build(), // Shell extensions are not available on the FTP server or in the archive, // but following items are intentionally added because icons in the context menu will not appear - // unless there is at least one menu item with an icon that is not an OpacityIcon. (#12943) + // unless there is at least one menu item with an icon that is not an ThemedIconModel. (#12943) new ContextMenuFlyoutItemViewModel() { ItemType = ContextMenuFlyoutItemType.Separator, @@ -590,10 +637,9 @@ public static List GetBaseItemMenuItems( ShowInRecycleBin = true, ShowInSearchPage = true, }, - new ContextMenuFlyoutItemViewModelBuilder(Commands.EditInNotepad).Build(), new ContextMenuFlyoutItemViewModel() { - Text = "Loading".GetLocalizedResource(), + Text = Strings.Loading.GetLocalizedResource(), Glyph = "\xE712", Items = [], ID = "ItemOverflow", @@ -614,7 +660,7 @@ public static List GetNewItemItems(BaseLayoutVie new ContextMenuFlyoutItemViewModelBuilder(Commands.CreateFolder).Build(), new ContextMenuFlyoutItemViewModel() { - Text = "File".GetLocalizedResource(), + Text = Strings.File.GetLocalizedResource(), Glyph = "\uE7C3", Command = commandsViewModel.CreateNewFileCommand, ShowInFtpPage = true, diff --git a/src/Files.App/Data/Factories/LocalizedEnumDescriptionFactory.cs b/src/Files.App/Data/Factories/LocalizedEnumDescriptionFactory.cs index 293ea4aa1c60..9ac93861e5ae 100644 --- a/src/Files.App/Data/Factories/LocalizedEnumDescriptionFactory.cs +++ b/src/Files.App/Data/Factories/LocalizedEnumDescriptionFactory.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Factories { @@ -10,7 +10,7 @@ internal static class LocalizedEnumDescriptionFactory { private static Dictionary DetailsViewSizeKinds { get; } = []; private static Dictionary ListViewSizeKinds { get; } = []; - private static Dictionary TilesViewSizeKinds { get; } = []; + private static Dictionary CardsViewSizeKinds { get; } = []; private static Dictionary GridViewSizeKinds { get; } = []; private static Dictionary ColumnsViewSizeKinds { get; } = []; @@ -18,11 +18,11 @@ public static string Get(DetailsViewSizeKind value) { if (DetailsViewSizeKinds.Count == 0) { - DetailsViewSizeKinds.Add(DetailsViewSizeKind.Compact, "Compact".GetLocalizedResource()); - DetailsViewSizeKinds.Add(DetailsViewSizeKind.Small, "Small".GetLocalizedResource()); - DetailsViewSizeKinds.Add(DetailsViewSizeKind.Medium, "Medium".GetLocalizedResource()); - DetailsViewSizeKinds.Add(DetailsViewSizeKind.Large, "Large".GetLocalizedResource()); - DetailsViewSizeKinds.Add(DetailsViewSizeKind.ExtraLarge, "ExtraLarge".GetLocalizedResource()); + DetailsViewSizeKinds.Add(DetailsViewSizeKind.Compact, Strings.Compact.GetLocalizedResource()); + DetailsViewSizeKinds.Add(DetailsViewSizeKind.Small, Strings.Small.GetLocalizedResource()); + DetailsViewSizeKinds.Add(DetailsViewSizeKind.Medium, Strings.Medium.GetLocalizedResource()); + DetailsViewSizeKinds.Add(DetailsViewSizeKind.Large, Strings.Large.GetLocalizedResource()); + DetailsViewSizeKinds.Add(DetailsViewSizeKind.ExtraLarge, Strings.ExtraLarge.GetLocalizedResource()); } var stringValue = DetailsViewSizeKinds.GetValueOrDefault(value)!; @@ -33,25 +33,28 @@ public static string Get(ListViewSizeKind value) { if (ListViewSizeKinds.Count == 0) { - ListViewSizeKinds.Add(ListViewSizeKind.Compact, "Compact".GetLocalizedResource()); - ListViewSizeKinds.Add(ListViewSizeKind.Small, "Small".GetLocalizedResource()); - ListViewSizeKinds.Add(ListViewSizeKind.Medium, "Medium".GetLocalizedResource()); - ListViewSizeKinds.Add(ListViewSizeKind.Large, "Large".GetLocalizedResource()); - ListViewSizeKinds.Add(ListViewSizeKind.ExtraLarge, "ExtraLarge".GetLocalizedResource()); + ListViewSizeKinds.Add(ListViewSizeKind.Compact, Strings.Compact.GetLocalizedResource()); + ListViewSizeKinds.Add(ListViewSizeKind.Small, Strings.Small.GetLocalizedResource()); + ListViewSizeKinds.Add(ListViewSizeKind.Medium, Strings.Medium.GetLocalizedResource()); + ListViewSizeKinds.Add(ListViewSizeKind.Large, Strings.Large.GetLocalizedResource()); + ListViewSizeKinds.Add(ListViewSizeKind.ExtraLarge, Strings.ExtraLarge.GetLocalizedResource()); } var stringValue = ListViewSizeKinds.GetValueOrDefault(value)!; return stringValue; } - public static string Get(TilesViewSizeKind value) + public static string Get(CardsViewSizeKind value) { - if (TilesViewSizeKinds.Count == 0) + if (CardsViewSizeKinds.Count == 0) { - TilesViewSizeKinds.Add(TilesViewSizeKind.Small, "Small".GetLocalizedResource()); + CardsViewSizeKinds.Add(CardsViewSizeKind.Small, Strings.Small.GetLocalizedResource()); + CardsViewSizeKinds.Add(CardsViewSizeKind.Medium, Strings.Medium.GetLocalizedResource()); + CardsViewSizeKinds.Add(CardsViewSizeKind.Large, Strings.Large.GetLocalizedResource()); + CardsViewSizeKinds.Add(CardsViewSizeKind.ExtraLarge, Strings.ExtraLarge.GetLocalizedResource()); } - var stringValue = TilesViewSizeKinds.GetValueOrDefault(value)!; + var stringValue = CardsViewSizeKinds.GetValueOrDefault(value)!; return stringValue; } @@ -59,18 +62,18 @@ public static string Get(GridViewSizeKind value) { if (GridViewSizeKinds.Count == 0) { - GridViewSizeKinds.Add(GridViewSizeKind.Small, "Small".GetLocalizedResource()); - GridViewSizeKinds.Add(GridViewSizeKind.Medium, "Medium".GetLocalizedResource()); - GridViewSizeKinds.Add(GridViewSizeKind.Three, "Medium+".GetLocalizedResource()); - GridViewSizeKinds.Add(GridViewSizeKind.Four, "Medium++".GetLocalizedResource()); - GridViewSizeKinds.Add(GridViewSizeKind.Five, "Medium+++".GetLocalizedResource()); - GridViewSizeKinds.Add(GridViewSizeKind.Six, "Medium++++".GetLocalizedResource()); - GridViewSizeKinds.Add(GridViewSizeKind.Seven, "Medium+++++".GetLocalizedResource()); - GridViewSizeKinds.Add(GridViewSizeKind.Large, "Large".GetLocalizedResource()); - GridViewSizeKinds.Add(GridViewSizeKind.Nine, "Large+".GetLocalizedResource()); - GridViewSizeKinds.Add(GridViewSizeKind.Ten, "Large++".GetLocalizedResource()); - GridViewSizeKinds.Add(GridViewSizeKind.Eleven, "Large+++".GetLocalizedResource()); - GridViewSizeKinds.Add(GridViewSizeKind.ExtraLarge, "ExtraLarge".GetLocalizedResource()); + GridViewSizeKinds.Add(GridViewSizeKind.Small, Strings.Small.GetLocalizedResource()); + GridViewSizeKinds.Add(GridViewSizeKind.Medium, Strings.Medium.GetLocalizedResource()); + GridViewSizeKinds.Add(GridViewSizeKind.Three, Strings.MediumP.GetLocalizedResource()); + GridViewSizeKinds.Add(GridViewSizeKind.Four, Strings.MediumPP.GetLocalizedResource()); + GridViewSizeKinds.Add(GridViewSizeKind.Five, Strings.MediumPPP.GetLocalizedResource()); + GridViewSizeKinds.Add(GridViewSizeKind.Six, Strings.MediumPPPP.GetLocalizedResource()); + GridViewSizeKinds.Add(GridViewSizeKind.Seven, Strings.MediumPPPPP.GetLocalizedResource()); + GridViewSizeKinds.Add(GridViewSizeKind.Large, Strings.Large.GetLocalizedResource()); + GridViewSizeKinds.Add(GridViewSizeKind.Nine, Strings.LargeP.GetLocalizedResource()); + GridViewSizeKinds.Add(GridViewSizeKind.Ten, Strings.LargePP.GetLocalizedResource()); + GridViewSizeKinds.Add(GridViewSizeKind.Eleven, Strings.LargePPP.GetLocalizedResource()); + GridViewSizeKinds.Add(GridViewSizeKind.ExtraLarge, Strings.ExtraLarge.GetLocalizedResource()); } var stringValue = GridViewSizeKinds.GetValueOrDefault(value)!; @@ -81,11 +84,11 @@ public static string Get(ColumnsViewSizeKind value) { if (ColumnsViewSizeKinds.Count == 0) { - ColumnsViewSizeKinds.Add(ColumnsViewSizeKind.Compact, "Compact".GetLocalizedResource()); - ColumnsViewSizeKinds.Add(ColumnsViewSizeKind.Small, "Small".GetLocalizedResource()); - ColumnsViewSizeKinds.Add(ColumnsViewSizeKind.Medium, "Medium".GetLocalizedResource()); - ColumnsViewSizeKinds.Add(ColumnsViewSizeKind.Large, "Large".GetLocalizedResource()); - ColumnsViewSizeKinds.Add(ColumnsViewSizeKind.ExtraLarge, "ExtraLarge".GetLocalizedResource()); + ColumnsViewSizeKinds.Add(ColumnsViewSizeKind.Compact, Strings.Compact.GetLocalizedResource()); + ColumnsViewSizeKinds.Add(ColumnsViewSizeKind.Small, Strings.Small.GetLocalizedResource()); + ColumnsViewSizeKinds.Add(ColumnsViewSizeKind.Medium, Strings.Medium.GetLocalizedResource()); + ColumnsViewSizeKinds.Add(ColumnsViewSizeKind.Large, Strings.Large.GetLocalizedResource()); + ColumnsViewSizeKinds.Add(ColumnsViewSizeKind.ExtraLarge, Strings.ExtraLarge.GetLocalizedResource()); } var stringValue = ColumnsViewSizeKinds.GetValueOrDefault(value)!; diff --git a/src/Files.App/Data/Factories/PropertiesNavigationViewItemFactory.cs b/src/Files.App/Data/Factories/PropertiesNavigationViewItemFactory.cs index df4da3b33448..c39a8c4f341a 100644 --- a/src/Files.App/Data/Factories/PropertiesNavigationViewItemFactory.cs +++ b/src/Files.App/Data/Factories/PropertiesNavigationViewItemFactory.cs @@ -1,10 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.App.Helpers; +using Files.Shared.Helpers; using Microsoft.UI.Xaml; using Windows.Storage; -using Files.Shared.Helpers; namespace Files.App.Data.Factories { @@ -16,54 +15,61 @@ public static ObservableCollection Initialize var generalItem = new NavigationViewItemButtonStyleItem() { - Name = "General".GetLocalizedResource(), + Name = Strings.General.GetLocalizedResource(), ItemType = PropertiesNavigationViewItemType.General, - OpacityIconStyle = (Style)Application.Current.Resources["ColorIconGeneralProperties"], + ThemedIconStyle = (Style)Application.Current.Resources["App.ThemedIcons.Properties.General"], }; var securityItem = new NavigationViewItemButtonStyleItem() { - Name = "Security".GetLocalizedResource(), + Name = Strings.Security.GetLocalizedResource(), ItemType = PropertiesNavigationViewItemType.Security, - OpacityIconStyle = (Style)Application.Current.Resources["ColorIconSecurityProperties"], + ThemedIconStyle = (Style)Application.Current.Resources["App.ThemedIcons.Properties.Security"], }; var hashesItem = new NavigationViewItemButtonStyleItem() { - Name = "Hashes".GetLocalizedResource(), + Name = Strings.Hashes.GetLocalizedResource(), ItemType = PropertiesNavigationViewItemType.Hashes, - OpacityIconStyle = (Style)Application.Current.Resources["ColorIconHashesProperties"], + ThemedIconStyle = (Style)Application.Current.Resources["App.ThemedIcons.Properties.Hashes"], }; var shortcutItem = new NavigationViewItemButtonStyleItem() { - Name = "Shortcut".GetLocalizedResource(), + Name = Strings.Shortcut.GetLocalizedResource(), ItemType = PropertiesNavigationViewItemType.Shortcut, - OpacityIconStyle = (Style)Application.Current.Resources["ColorIconShortcutProperties"], + ThemedIconStyle = (Style)Application.Current.Resources["App.ThemedIcons.Properties.Shortcut"], }; var libraryItem = new NavigationViewItemButtonStyleItem() { - Name = "Library".GetLocalizedResource(), + Name = Strings.Library.GetLocalizedResource(), ItemType = PropertiesNavigationViewItemType.Library, - OpacityIconStyle = (Style)Application.Current.Resources["ColorIconLibraryProperties"], + ThemedIconStyle = (Style)Application.Current.Resources["App.ThemedIcons.Properties.Library"], }; var detailsItem = new NavigationViewItemButtonStyleItem() { - Name = "Details".GetLocalizedResource(), + Name = Strings.Details.GetLocalizedResource(), ItemType = PropertiesNavigationViewItemType.Details, - OpacityIconStyle = (Style)Application.Current.Resources["ColorIconDetailsProperties"], + ThemedIconStyle = (Style)Application.Current.Resources["App.ThemedIcons.Properties.Info"], }; var customizationItem = new NavigationViewItemButtonStyleItem() { - Name = "Customization".GetLocalizedResource(), + Name = Strings.Customization.GetLocalizedResource(), ItemType = PropertiesNavigationViewItemType.Customization, - OpacityIconStyle = (Style)Application.Current.Resources["ColorIconCustomizationProperties"], + ThemedIconStyle = (Style)Application.Current.Resources["App.ThemedIcons.Properties.CustomizeFolder"], }; var compatibilityItem = new NavigationViewItemButtonStyleItem() { - Name = "Compatibility".GetLocalizedResource(), + Name = Strings.Compatibility.GetLocalizedResource(), ItemType = PropertiesNavigationViewItemType.Compatibility, - OpacityIconStyle = (Style)Application.Current.Resources["ColorIconCompatibilityProperties"], + ThemedIconStyle = (Style)Application.Current.Resources["App.ThemedIcons.Properties.Compatability"], + }; + var signaturesItem = new NavigationViewItemButtonStyleItem() + { + Name = Strings.Signatures.GetLocalizedResource(), + ItemType = PropertiesNavigationViewItemType.Signatures, + ThemedIconStyle = (Style)Application.Current.Resources["App.ThemedIcons.Properties.Signatures"], }; PropertiesNavigationViewItems.Add(generalItem); + PropertiesNavigationViewItems.Add(signaturesItem); PropertiesNavigationViewItems.Add(securityItem); PropertiesNavigationViewItems.Add(hashesItem); PropertiesNavigationViewItems.Add(shortcutItem); @@ -76,7 +82,7 @@ public static ObservableCollection Initialize { var firstFileExtension = listedItems.FirstOrDefault()?.FileExtension; var commonFileExt = listedItems.All(x => x.FileExtension == firstFileExtension) ? firstFileExtension : null; - var compatibilityItemEnabled = listedItems.All(listedItem => FileExtensionHelpers.IsExecutableFile(listedItem is ShortcutItem sht ? sht.TargetPath : commonFileExt, true)); + var compatibilityItemEnabled = listedItems.All(listedItem => FileExtensionHelpers.IsExecutableFile(listedItem is IShortcutItem sht ? sht.TargetPath : commonFileExt, true)); var onlyFiles = listedItems.All(listedItem => listedItem.PrimaryItemAttribute == StorageItemTypes.File || listedItem.IsArchive); if (!compatibilityItemEnabled) @@ -90,6 +96,7 @@ public static ObservableCollection Initialize PropertiesNavigationViewItems.Remove(securityItem); PropertiesNavigationViewItems.Remove(customizationItem); PropertiesNavigationViewItems.Remove(hashesItem); + PropertiesNavigationViewItems.Remove(signaturesItem); } else if (item is ListedItem listedItem) { @@ -101,8 +108,13 @@ public static ObservableCollection Initialize var securityItemEnabled = !isLibrary && !listedItem.IsRecycleBinItem; var hashItemEnabled = !(isFolder && !listedItem.IsArchive) && !isLibrary && !listedItem.IsRecycleBinItem; var detailsItemEnabled = !(isFolder && !listedItem.IsArchive) && !isLibrary && !listedItem.IsRecycleBinItem; - var customizationItemEnabled = !isLibrary && (isFolder && !listedItem.IsArchive || isShortcut && !listedItem.IsLinkItem); - var compatibilityItemEnabled = FileExtensionHelpers.IsExecutableFile(listedItem is ShortcutItem sht ? sht.TargetPath : fileExt, true); + var customizationItemEnabled = !isLibrary && (isFolder && !listedItem.IsArchive || isShortcut); + var compatibilityItemEnabled = FileExtensionHelpers.IsExecutableFile(listedItem is IShortcutItem sht ? sht.TargetPath : fileExt, true); + var signaturesItemEnabled = + !isFolder && + !isLibrary && + !listedItem.IsRecycleBinItem && + FileExtensionHelpers.IsSignableFile(fileExt, true); if (!securityItemEnabled) PropertiesNavigationViewItems.Remove(securityItem); @@ -110,6 +122,9 @@ public static ObservableCollection Initialize if (!hashItemEnabled) PropertiesNavigationViewItems.Remove(hashesItem); + if (!signaturesItemEnabled) + PropertiesNavigationViewItems.Remove(signaturesItem); + if (!isShortcut) PropertiesNavigationViewItems.Remove(shortcutItem); @@ -133,6 +148,7 @@ public static ObservableCollection Initialize PropertiesNavigationViewItems.Remove(detailsItem); PropertiesNavigationViewItems.Remove(customizationItem); PropertiesNavigationViewItems.Remove(compatibilityItem); + PropertiesNavigationViewItems.Remove(signaturesItem); } return PropertiesNavigationViewItems; diff --git a/src/Files.App/Data/Factories/SecurityAdvancedAccessControlItemFactory.cs b/src/Files.App/Data/Factories/SecurityAdvancedAccessControlItemFactory.cs index c00b96a4514a..cfee423820e1 100644 --- a/src/Files.App/Data/Factories/SecurityAdvancedAccessControlItemFactory.cs +++ b/src/Files.App/Data/Factories/SecurityAdvancedAccessControlItemFactory.cs @@ -1,9 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.App.Extensions; -using System.Collections.Generic; -using System.Collections.ObjectModel; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Factories { @@ -28,109 +24,109 @@ public static ObservableCollection Initialize(AccessControlEntry new(current) { AccessMask = AccessMaskFlags.FullControl, - AccessMaskName = "SecurityFullControlLabel/Text".GetLocalizedResource(), + AccessMaskName = Strings.SecurityFullControlLabel_Text.GetLocalizedResource(), IsEditable = !isInherited }, new(current) { AccessMask = AccessMaskFlags.Traverse, - AccessMaskName = "SecurityTraverseLabel/Text".GetLocalizedResource(), + AccessMaskName = Strings.SecurityTraverseLabel_Text.GetLocalizedResource(), IsEditable = !isInherited }, new(current) { AccessMask = AccessMaskFlags.ExecuteFile, - AccessMaskName = "SecurityExecuteFileLabel/Text".GetLocalizedResource(), + AccessMaskName = Strings.SecurityExecuteFileLabel_Text.GetLocalizedResource(), IsEditable = !isInherited }, new(current) { AccessMask = AccessMaskFlags.ListDirectory, - AccessMaskName = "SecurityListDirectoryLabel/Text".GetLocalizedResource(), + AccessMaskName = Strings.SecurityListDirectoryLabel_Text.GetLocalizedResource(), IsEditable = !isInherited }, new(current) { AccessMask = AccessMaskFlags.ReadData, - AccessMaskName = "SecurityReadDataLabel/Text".GetLocalizedResource(), + AccessMaskName = Strings.SecurityReadDataLabel_Text.GetLocalizedResource(), IsEditable = !isInherited }, new(current) { AccessMask = AccessMaskFlags.ReadAttributes, - AccessMaskName = "SecurityReadAttributesLabel/Text".GetLocalizedResource(), + AccessMaskName = Strings.SecurityReadAttributesLabel_Text.GetLocalizedResource(), IsEditable = !isInherited }, new(current) { AccessMask = AccessMaskFlags.ReadExtendedAttributes, - AccessMaskName = "SecurityReadExtendedAttributesLabel/Text".GetLocalizedResource(), + AccessMaskName = Strings.SecurityReadExtendedAttributesLabel_Text.GetLocalizedResource(), IsEditable = !isInherited }, new(current) { AccessMask = AccessMaskFlags.CreateFiles, - AccessMaskName = "SecurityCreateFilesLabel/Text".GetLocalizedResource(), + AccessMaskName = Strings.SecurityCreateFilesLabel_Text.GetLocalizedResource(), IsEditable = !isInherited }, new(current) { AccessMask = AccessMaskFlags.CreateDirectories, - AccessMaskName = "SecurityCreateDirectoriesLabel/Text".GetLocalizedResource(), + AccessMaskName = Strings.SecurityCreateDirectoriesLabel_Text.GetLocalizedResource(), IsEditable = !isInherited }, new(current) { AccessMask = AccessMaskFlags.WriteData, - AccessMaskName = "SecurityWriteDataLabel/Text".GetLocalizedResource(), + AccessMaskName = Strings.SecurityWriteDataLabel_Text.GetLocalizedResource(), IsEditable = !isInherited }, new(current) { AccessMask = AccessMaskFlags.AppendData, - AccessMaskName = "SecurityAppendDataLabel/Text".GetLocalizedResource(), + AccessMaskName = Strings.SecurityAppendDataLabel_Text.GetLocalizedResource(), IsEditable = !isInherited }, new(current) { AccessMask = AccessMaskFlags.WriteAttributes, - AccessMaskName = "SecurityWriteAttributesLabel/Text".GetLocalizedResource(), + AccessMaskName = Strings.SecurityWriteAttributesLabel_Text.GetLocalizedResource(), IsEditable = !isInherited }, new(current) { AccessMask = AccessMaskFlags.WriteExtendedAttributes, - AccessMaskName = "SecurityWriteExtendedAttributesLabel/Text".GetLocalizedResource(), + AccessMaskName = Strings.SecurityWriteExtendedAttributesLabel_Text.GetLocalizedResource(), IsEditable = !isInherited }, new(current) { AccessMask = AccessMaskFlags.DeleteSubdirectoriesAndFiles, - AccessMaskName = "SecurityDeleteSubdirectoriesAndFilesLabel/Text".GetLocalizedResource(), + AccessMaskName = Strings.SecurityDeleteSubdirectoriesAndFilesLabel_Text.GetLocalizedResource(), IsEditable = !isInherited }, new(current) { AccessMask = AccessMaskFlags.Delete, - AccessMaskName = "Delete".GetLocalizedResource(), + AccessMaskName = Strings.Delete.GetLocalizedResource(), IsEditable = !isInherited }, new(current) { AccessMask = AccessMaskFlags.ReadPermissions, - AccessMaskName = "SecurityReadPermissionsLabel/Text".GetLocalizedResource(), + AccessMaskName = Strings.SecurityReadPermissionsLabel_Text.GetLocalizedResource(), IsEditable = !isInherited }, new(current) { AccessMask = AccessMaskFlags.ChangePermissions, - AccessMaskName = "SecurityChangePermissionsLabel/Text".GetLocalizedResource(), + AccessMaskName = Strings.SecurityChangePermissionsLabel_Text.GetLocalizedResource(), IsEditable = !isInherited }, new(current) { AccessMask = AccessMaskFlags.TakeOwnership, - AccessMaskName = "SecurityTakeOwnershipLabel/Text".GetLocalizedResource(), + AccessMaskName = Strings.SecurityTakeOwnershipLabel_Text.GetLocalizedResource(), IsEditable = !isInherited } ]; @@ -160,42 +156,42 @@ public static ObservableCollection Initialize(AccessControlEntry new(current) { AccessMask = AccessMaskFlags.FullControl, - AccessMaskName = "SecurityFullControlLabel/Text".GetLocalizedResource(), + AccessMaskName = Strings.SecurityFullControlLabel_Text.GetLocalizedResource(), IsEditable = !isInherited }, new(current) { AccessMask = AccessMaskFlags.Modify, - AccessMaskName = "Modify".GetLocalizedResource(), + AccessMaskName = Strings.Modify.GetLocalizedResource(), IsEditable = !isInherited }, new(current) { AccessMask = AccessMaskFlags.ReadAndExecute, - AccessMaskName = "SecurityReadAndExecuteLabel/Text".GetLocalizedResource(), + AccessMaskName = Strings.SecurityReadAndExecuteLabel_Text.GetLocalizedResource(), IsEditable = !isInherited }, new(current) { AccessMask = AccessMaskFlags.ListDirectory, - AccessMaskName = "SecurityListDirectoryLabel/Text".GetLocalizedResource(), + AccessMaskName = Strings.SecurityListDirectoryLabel_Text.GetLocalizedResource(), IsEditable = !isInherited }, new(current) { AccessMask = AccessMaskFlags.Read, - AccessMaskName = "SecurityReadLabel/Text".GetLocalizedResource(), + AccessMaskName = Strings.SecurityReadLabel_Text.GetLocalizedResource(), IsEditable = !isInherited }, new(current) { AccessMask = AccessMaskFlags.Write, - AccessMaskName = "Write".GetLocalizedResource(), + AccessMaskName = Strings.Write.GetLocalizedResource(), IsEditable = !isInherited }, new(current, false) { - AccessMaskName = "SecuritySpecialLabel/Text".GetLocalizedResource() + AccessMaskName = Strings.SecuritySpecialLabel_Text.GetLocalizedResource() } ]; diff --git a/src/Files.App/Data/Factories/ShellContextFlyoutHelper.cs b/src/Files.App/Data/Factories/ShellContextFlyoutHelper.cs index 38df8e2dc4a2..361766242fe7 100644 --- a/src/Files.App/Data/Factories/ShellContextFlyoutHelper.cs +++ b/src/Files.App/Data/Factories/ShellContextFlyoutHelper.cs @@ -1,7 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using CommunityToolkit.WinUI.UI; +using CommunityToolkit.WinUI; using Files.App.Helpers.ContextFlyouts; using Files.Shared.Helpers; using Microsoft.UI.Input; @@ -9,10 +9,10 @@ using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media.Imaging; using System.IO; -using Vanara.PInvoke; using Windows.System; using Windows.UI.Core; -using static Vanara.PInvoke.Kernel32; +using Windows.Win32; +using Windows.Win32.UI.WindowsAndMessaging; namespace Files.App.Helpers { @@ -45,6 +45,7 @@ Func FilterMenuItems(bool showOpenMenu) Win32Helper.ExtractStringFromDLL("shell32.dll", 5385), // Unpin from Start Win32Helper.ExtractStringFromDLL("shell32.dll", 5386), // Pin to taskbar Win32Helper.ExtractStringFromDLL("shell32.dll", 5387), // Unpin from taskbar + "{9F156763-7844-4DC4-B2B1-901F640F5155}", // Open in Terminal }; bool filterMenuItemsImpl(string menuItem) => !string.IsNullOrEmpty(menuItem) @@ -54,7 +55,7 @@ bool filterMenuItemsImpl(string menuItem) => !string.IsNullOrEmpty(menuItem) } var contextMenu = await ContextMenu.GetContextMenuForFiles(filePaths, - shiftPressed ? Shell32.CMF.CMF_EXTENDEDVERBS : Shell32.CMF.CMF_NORMAL, FilterMenuItems(showOpenMenu)); + shiftPressed ? PInvoke.CMF_EXTENDEDVERBS : PInvoke.CMF_NORMAL, FilterMenuItems(showOpenMenu)); if (contextMenu is not null) LoadMenuFlyoutItem(menuItemsList, contextMenu, contextMenu.Items, cancellationToken, true); @@ -77,17 +78,17 @@ private static void LoadMenuFlyoutItem( return; var itemsCount = 0; // Separators do not count for reaching the overflow threshold - var menuItems = menuFlyoutItems.TakeWhile(x => x.Type == MenuItemType.MFT_SEPARATOR || ++itemsCount <= itemsBeforeOverflow).ToList(); + var menuItems = menuFlyoutItems.TakeWhile(x => x.Type == MENU_ITEM_TYPE.MFT_SEPARATOR || ++itemsCount <= itemsBeforeOverflow).ToList(); var overflowItems = menuFlyoutItems.Except(menuItems).ToList(); - if (overflowItems.Any(x => x.Type != MenuItemType.MFT_SEPARATOR)) + if (overflowItems.Any(x => x.Type != MENU_ITEM_TYPE.MFT_SEPARATOR)) { var moreItem = menuItemsListLocal.FirstOrDefault(x => x.ID == "ItemOverflow"); if (moreItem is null) { var menuLayoutSubItem = new ContextMenuFlyoutItemViewModel() { - Text = "ShowMoreOptions".GetLocalizedResource(), + Text = Strings.ShowMoreOptions.GetLocalizedResource(), Glyph = "\xE712", }; LoadMenuFlyoutItem(menuLayoutSubItem.Items, contextMenu, overflowItems, cancellationToken, showIcons); @@ -100,15 +101,15 @@ private static void LoadMenuFlyoutItem( } foreach (var menuFlyoutItem in menuItems - .SkipWhile(x => x.Type == MenuItemType.MFT_SEPARATOR) // Remove leading separators + .SkipWhile(x => x.Type == MENU_ITEM_TYPE.MFT_SEPARATOR) // Remove leading separators .Reverse() - .SkipWhile(x => x.Type == MenuItemType.MFT_SEPARATOR)) // Remove trailing separators + .SkipWhile(x => x.Type == MENU_ITEM_TYPE.MFT_SEPARATOR)) // Remove trailing separators { if (cancellationToken.IsCancellationRequested) break; // Avoid duplicate separators - if ((menuFlyoutItem.Type == MenuItemType.MFT_SEPARATOR) && (menuItemsListLocal.FirstOrDefault()?.ItemType == ContextMenuFlyoutItemType.Separator)) + if ((menuFlyoutItem.Type == MENU_ITEM_TYPE.MFT_SEPARATOR) && (menuItemsListLocal.FirstOrDefault()?.ItemType == ContextMenuFlyoutItemType.Separator)) continue; BitmapImage? image = null; @@ -119,7 +120,7 @@ private static void LoadMenuFlyoutItem( image.SetSourceAsync(ms.AsRandomAccessStream()).AsTask().Wait(10); } - if (menuFlyoutItem.Type is MenuItemType.MFT_SEPARATOR) + if (menuFlyoutItem.Type is MENU_ITEM_TYPE.MFT_SEPARATOR) { var menuLayoutItem = new ContextMenuFlyoutItemViewModel() { @@ -198,6 +199,13 @@ async Task InvokeShellMenuItemAsync(ContextMenu contextMenu, object? tag) await Win32Helper.OpenFormatDriveDialog(drivePath); break; + case "Windows.PowerShell.Run": + await contextMenu.InvokeItem( + menuId, + contextMenu.ItemsPath[0].EndsWith(".ps1") ? Path.GetDirectoryName(contextMenu.ItemsPath[0]) : null + ); + break; + default: await contextMenu.InvokeItem(menuId); break; @@ -254,8 +262,8 @@ [new ListedItem(null) { ItemPath = path }], (showSendToMenu || !UserSettingsService.GeneralSettingsService.ShowSendToMenu)) shellMenuItems.Remove(sendToItem); - var turnOnBitLocker = shellMenuItems.FirstOrDefault(x => - x.Tag is Win32ContextMenuItem menuItem && + var turnOnBitLocker = shellMenuItems.FirstOrDefault(x => + x.Tag is Win32ContextMenuItem menuItem && (menuItem.CommandString?.StartsWith("encrypt-bde") ?? false)); if (turnOnBitLocker is not null) @@ -327,7 +335,7 @@ x.Tag is Win32ContextMenuItem menuItem && overflowItem.Visibility = overflowItems?.Any() ?? false ? Visibility.Visible : Visibility.Collapsed; overflowSeparator.Visibility = overflowItems?.Any() ?? false ? Visibility.Visible : Visibility.Collapsed; - overflowItem.Label = "ShowMoreOptions".GetLocalizedResource(); + overflowItem.Label = Strings.ShowMoreOptions.GetLocalizedResource(); overflowItem.IsEnabled = true; } @@ -336,9 +344,9 @@ x.Tag is Win32ContextMenuItem menuItem && { await openWithItem.LoadSubMenuAction(); - openWithItem.OpacityIcon = new OpacityIconModel() + openWithItem.ThemedIconModel = new ThemedIconModel() { - OpacityIconStyle = "ColorIconOpenWith", + ThemedIconStyle = "App.ThemedIcons.OpenWith", }; var (_, openWithItems) = ContextFlyoutModelToElementHelper.GetAppBarItemsFromModel([openWithItem]); var index = 0; @@ -376,8 +384,8 @@ x.Tag is Win32ContextMenuItem menuItem && { AddItemsToMainMenu(itemContextMenuFlyout.SecondaryCommands, item); } - else if (itemContextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton appBarButton - && appBarButton.Tag as string == "ItemOverflow") is AppBarButton overflowItem) + else if (itemContextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton appBarButton + && appBarButton.Tag as string == "ItemOverflow") is AppBarButton overflowItem) { AddItemsToOverflowMenu(overflowItem, item); } diff --git a/src/Files.App/Data/Items/AccessControlEntry.cs b/src/Files.App/Data/Items/AccessControlEntry.cs index 7462fc29a312..6a50acebc88e 100644 --- a/src/Files.App/Data/Items/AccessControlEntry.cs +++ b/src/Files.App/Data/Items/AccessControlEntry.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Items { /// /// Represents an access control entry (ACE). /// - public sealed class AccessControlEntry : ObservableObject + public sealed partial class AccessControlEntry : ObservableObject { /// /// Whether the path indicates folder or not @@ -36,8 +36,8 @@ public bool IsEditable public string AccessControlTypeHumanized => AccessControlType switch { - AccessControlEntryType.Allow => "Allow".GetLocalizedResource(), - _ => "Deny".GetLocalizedResource() // AccessControlType.Deny + AccessControlEntryType.Allow => Strings.Allow.GetLocalizedResource(), + _ => Strings.Deny.GetLocalizedResource() // AccessControlType.Deny }; /// @@ -60,22 +60,22 @@ public string AccessMaskFlagsHumanized var accessMaskStrings = new List(); if (AccessMaskFlags == AccessMaskFlags.NULL) - accessMaskStrings.Add("None".GetLocalizedResource()); + accessMaskStrings.Add(Strings.None.GetLocalizedResource()); if (FullControlAccess) - accessMaskStrings.Add("SecurityFullControlLabel/Text".GetLocalizedResource()); + accessMaskStrings.Add(Strings.SecurityFullControlLabel_Text.GetLocalizedResource()); else if (ModifyAccess) - accessMaskStrings.Add("Modify".GetLocalizedResource()); + accessMaskStrings.Add(Strings.Modify.GetLocalizedResource()); else if (ReadAndExecuteAccess) - accessMaskStrings.Add("SecurityReadAndExecuteLabel/Text".GetLocalizedResource()); + accessMaskStrings.Add(Strings.SecurityReadAndExecuteLabel_Text.GetLocalizedResource()); else if (ReadAccess) - accessMaskStrings.Add("SecurityReadLabel/Text".GetLocalizedResource()); + accessMaskStrings.Add(Strings.SecurityReadLabel_Text.GetLocalizedResource()); if (!FullControlAccess && !ModifyAccess && WriteAccess) - accessMaskStrings.Add("Write".GetLocalizedResource()); + accessMaskStrings.Add(Strings.Write.GetLocalizedResource()); if (SpecialAccess) - accessMaskStrings.Add("SecuritySpecialLabel/Text".GetLocalizedResource()); + accessMaskStrings.Add(Strings.SecuritySpecialLabel_Text.GetLocalizedResource()); return string.Join(", ", accessMaskStrings); } @@ -85,7 +85,7 @@ public string AccessMaskFlagsHumanized /// IsInheritedHumanized /// public string IsInheritedHumanized - => IsInherited ? "Yes".GetLocalizedResource() : "No".GetLocalizedResource(); + => IsInherited ? Strings.Yes.GetLocalizedResource() : Strings.No.GetLocalizedResource(); /// /// InheritanceFlagsHumanized @@ -98,13 +98,13 @@ public string InheritanceFlagsHumanized if (AccessControlEntryFlags == AccessControlEntryFlags.None || AccessControlEntryFlags == AccessControlEntryFlags.NoPropagateInherit) - inheritanceStrings.Add("SecurityAdvancedFlagsFolderLabel".GetLocalizedResource()); + inheritanceStrings.Add(Strings.SecurityAdvancedFlagsFolderLabel.GetLocalizedResource()); if (AccessControlEntryFlags.HasFlag(AccessControlEntryFlags.ContainerInherit)) - inheritanceStrings.Add("SecurityAdvancedFlagsSubfoldersLabel".GetLocalizedResource()); + inheritanceStrings.Add(Strings.SecurityAdvancedFlagsSubfoldersLabel.GetLocalizedResource()); if (AccessControlEntryFlags.HasFlag(AccessControlEntryFlags.ObjectInherit)) - inheritanceStrings.Add("SecurityAdvancedFlagsFilesLabel".GetLocalizedResource()); + inheritanceStrings.Add(Strings.SecurityAdvancedFlagsFilesLabel.GetLocalizedResource()); // Capitalize the first letter if (inheritanceStrings.Any()) diff --git a/src/Files.App/Data/Items/AccessControlList.cs b/src/Files.App/Data/Items/AccessControlList.cs index 8e31c0476f52..c409c8b471fa 100644 --- a/src/Files.App/Data/Items/AccessControlList.cs +++ b/src/Files.App/Data/Items/AccessControlList.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Items { /// /// Represents an access control list (ACL). /// - public sealed class AccessControlList : ObservableObject + public sealed partial class AccessControlList : ObservableObject { /// /// Object path. diff --git a/src/Files.App/Data/Items/AccessControlPrincipal.cs b/src/Files.App/Data/Items/AccessControlPrincipal.cs index 9e99f94defb1..4da0dc3ebcdd 100644 --- a/src/Files.App/Data/Items/AccessControlPrincipal.cs +++ b/src/Files.App/Data/Items/AccessControlPrincipal.cs @@ -1,8 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.Win32; -using Windows.Win32.Foundation; using Windows.Win32.Security; namespace Files.App.Data.Items @@ -10,7 +9,7 @@ namespace Files.App.Data.Items /// /// Represents a principal of an ACE or an owner of an ACL. /// - public sealed class AccessControlPrincipal : ObservableObject + public sealed partial class AccessControlPrincipal : ObservableObject { /// /// Account type. @@ -66,35 +65,28 @@ public string? FullNameHumanized public string FullNameHumanizedWithBrackes => string.IsNullOrEmpty(Domain) ? string.Empty : $"({Domain}\\{Name})"; - public unsafe AccessControlPrincipal(string sid) + public AccessControlPrincipal(string sid) { if (string.IsNullOrEmpty(sid)) return; Sid = sid; - PSID lpSid = default; - SID_NAME_USE snu = default; + PInvoke.ConvertStringSidToSid(sid, out var lpSid); - fixed (char* cSid = sid) - PInvoke.ConvertStringSidToSid(new PCWSTR(cSid), &lpSid); - - PWSTR lpName = default; - PWSTR lpDomain = default; + char[] lpName = []; + char[] lpDomain = []; uint cchName = 0, cchDomainName = 0; // Get size of account name and domain name - bool bResult = PInvoke.LookupAccountSid(new PCWSTR(), lpSid, lpName, &cchName, lpDomain, &cchDomainName, null); + bool bResult = PInvoke.LookupAccountSid(string.Empty, lpSid, lpName, ref cchName, lpDomain, ref cchDomainName, out _); // Ensure requested capacity - fixed (char* cName = new char[cchName]) - lpName = new(cName); - - fixed (char* cDomain = new char[cchDomainName]) - lpDomain = new(cDomain); + lpName = new char[cchName]; + lpDomain = new char[cchDomainName]; // Get account name and domain - bResult = PInvoke.LookupAccountSid(new PCWSTR(), lpSid, lpName, &cchName, lpDomain, &cchDomainName, &snu); - if(!bResult) + bResult = PInvoke.LookupAccountSid(string.Empty, lpSid, lpName, ref cchName, lpDomain, ref cchDomainName, out var snu); + if (!bResult) return; PrincipalType = snu switch @@ -118,16 +110,14 @@ var x when if (snu == SID_NAME_USE.SidTypeUser || snu == SID_NAME_USE.SidTypeAlias) { uint size = 256; - fixed (char* cDomain = new char[size]) - lpDomain = new(cDomain); - + lpDomain = new char[size]; bResult = PInvoke.GetComputerName(lpDomain, ref size); if (!bResult) return; } - Name = lpName.ToString(); - Domain = lpDomain.ToString().ToLower(); + Name = lpName.AsSpan().ToString(); + Domain = lpDomain.AsSpan().ToString().ToLower(); IsValid = true; } diff --git a/src/Files.App/Data/Items/AccessMaskItem.cs b/src/Files.App/Data/Items/AccessMaskItem.cs index 79b15c771bcb..8f5e1853cda6 100644 --- a/src/Files.App/Data/Items/AccessMaskItem.cs +++ b/src/Files.App/Data/Items/AccessMaskItem.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Items { /// /// Represents an access mask details, such as its name and changeability. /// - public sealed class AccessMaskItem : ObservableObject + public sealed partial class AccessMaskItem : ObservableObject { public string AccessMaskName { get; set; } diff --git a/src/Files.App/Data/Items/ActionWithParameterItem.cs b/src/Files.App/Data/Items/ActionWithParameterItem.cs index 4b86fb46ef0b..63ea8691b015 100644 --- a/src/Files.App/Data/Items/ActionWithParameterItem.cs +++ b/src/Files.App/Data/Items/ActionWithParameterItem.cs @@ -1,7 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System.Text.Json.Serialization; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Items { diff --git a/src/Files.App/Data/Items/AppLanguageItem.cs b/src/Files.App/Data/Items/AppLanguageItem.cs index c161d0413506..bbd9e7119943 100644 --- a/src/Files.App/Data/Items/AppLanguageItem.cs +++ b/src/Files.App/Data/Items/AppLanguageItem.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Globalization; @@ -30,7 +30,7 @@ public AppLanguageItem(string code, bool systemDefault = false) if (systemDefault || string.IsNullOrEmpty(code)) { Code = new CultureInfo(code).Name; - Name = "SettingsPreferencesSystemDefaultLanguageOption".GetLocalizedResource(); + Name = Strings.UseSystemSetting.GetLocalizedResource(); } else { diff --git a/src/Files.App/Data/Items/AppThemeResourceItem.cs b/src/Files.App/Data/Items/AppThemeResourceItem.cs index 9e69e55d2fe2..ee9023017aa9 100644 --- a/src/Files.App/Data/Items/AppThemeResourceItem.cs +++ b/src/Files.App/Data/Items/AppThemeResourceItem.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Items { diff --git a/src/Files.App/Data/Items/BranchItem.cs b/src/Files.App/Data/Items/BranchItem.cs index 79d8fa286e1a..7f2c1a515763 100644 --- a/src/Files.App/Data/Items/BranchItem.cs +++ b/src/Files.App/Data/Items/BranchItem.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Items { diff --git a/src/Files.App/Data/Items/CertNodeInfoItem.cs b/src/Files.App/Data/Items/CertNodeInfoItem.cs new file mode 100644 index 000000000000..abed64927c7a --- /dev/null +++ b/src/Files.App/Data/Items/CertNodeInfoItem.cs @@ -0,0 +1,18 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Data.Items +{ + public class CertNodeInfoItem + { + public string IssuedTo { get; set; } = string.Empty; + + public string IssuedBy { get; set; } = string.Empty; + + public string Version { get; set; } = string.Empty; + + public string ValidFrom { get; set; } = string.Empty; + + public string ValidTo { get; set; } = string.Empty; + } +} diff --git a/src/Files.App/Data/Items/ContextMenu.cs b/src/Files.App/Data/Items/ContextMenu.cs index b110d4b61209..36532585196e 100644 --- a/src/Files.App/Data/Items/ContextMenu.cs +++ b/src/Files.App/Data/Items/ContextMenu.cs @@ -1,22 +1,10 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Windows.Win32.UI.WindowsAndMessaging; namespace Files.App.Data.Items { - // Same definition of Vanara.PInvoke.User32.MenuItemType - public enum MenuItemType : uint - { - MFT_STRING = 0, - MFT_BITMAP = 4, - MFT_MENUBARBREAK = 32, - MFT_MENUBREAK = 64, - MFT_OWNERDRAW = 256, - MFT_RADIOCHECK = 512, - MFT_SEPARATOR = 2048, - MFT_RIGHTORDER = 8192, - MFT_RIGHTJUSTIFY = 16384 - } - public enum HBITMAP_HMENU : long { HBMMENU_CALLBACK = -1, @@ -43,7 +31,7 @@ public class Win32ContextMenuItem public int ID { get; set; } // Valid only in current menu to invoke item public string Label { get; set; } public string CommandString { get; set; } - public MenuItemType Type { get; set; } + public MENU_ITEM_TYPE Type { get; set; } public List SubItems { get; set; } } } diff --git a/src/Files.App/Data/Items/DetailsLayoutColumnItem.cs b/src/Files.App/Data/Items/DetailsLayoutColumnItem.cs index 95e7812780da..8592ad8a8004 100644 --- a/src/Files.App/Data/Items/DetailsLayoutColumnItem.cs +++ b/src/Files.App/Data/Items/DetailsLayoutColumnItem.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; @@ -8,7 +8,7 @@ namespace Files.App.Data.Items /// /// Represents item for a column shown in . /// - public sealed class DetailsLayoutColumnItem : ObservableObject + public sealed partial class DetailsLayoutColumnItem : ObservableObject { private const int GRID_SPLITTER_WIDTH = 12; @@ -145,5 +145,16 @@ public override int GetHashCode() return hashCode; } + + public void Update(DetailsLayoutColumnItem other) + { + UserLengthPixels = other.UserLengthPixels; + NormalMaxLength = other.NormalMaxLength; + UserCollapsed = other.UserCollapsed; + IsResizable = other.IsResizable; + IsHidden = other.IsHidden; + NormalMinLength = other.NormalMinLength; + UserLength = other.UserLength; + } } } diff --git a/src/Files.App/Data/Items/DeviceEvent.cs b/src/Files.App/Data/Items/DeviceEvent.cs index 229aa1a4e600..dfb3efb094c3 100644 --- a/src/Files.App/Data/Items/DeviceEvent.cs +++ b/src/Files.App/Data/Items/DeviceEvent.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Items { diff --git a/src/Files.App/Data/Items/DriveItem.cs b/src/Files.App/Data/Items/DriveItem.cs index 342597d87ca2..c569c528fd37 100644 --- a/src/Files.App/Data/Items/DriveItem.cs +++ b/src/Files.App/Data/Items/DriveItem.cs @@ -1,17 +1,18 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.App.Storage.Storables; +using Files.App.Controls; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media.Imaging; +using System.Runtime.CompilerServices; using Windows.Storage; using Windows.Storage.Streams; using ByteSize = ByteSizeLib.ByteSize; namespace Files.App.Data.Items { - public sealed class DriveItem : ObservableObject, INavigationControlItem, ILocatableFolder + public sealed partial class DriveItem : ObservableObject, INavigationControlItem, IFolder { private BitmapImage icon; public BitmapImage Icon @@ -20,7 +21,7 @@ public BitmapImage Icon set { SetProperty(ref icon, value, nameof(Icon)); - OnPropertyChanged(nameof(IconSource)); + OnPropertyChanged(nameof(IconElement)); } } @@ -48,7 +49,7 @@ public bool IsNetwork => Type == DriveType.Network; public bool IsPinned - => App.QuickAccessManager.Model.PinnedFolders.Contains(path); + => App.QuickAccessManager.Model.PinnedFolders.Contains(Path); public string MaxSpaceText => MaxSpace.ToSizeString(); @@ -115,15 +116,27 @@ public bool ShowDriveDetails private DriveType type; public DriveType Type { - get => type; set + get => type; + set { type = value; if (value is DriveType.Network or DriveType.CloudDrive) ToolTip = Text; + + OnPropertyChanged(nameof(TypeText)); } } + public string TypeText => $"DriveType{Type}".GetLocalizedResource(); + + private string filesystem = string.Empty; + public string Filesystem + { + get => filesystem; + set => SetProperty(ref filesystem, value); + } + private string text; public string Text { @@ -163,10 +176,8 @@ public bool ShowStorageSense set => SetProperty(ref showStorageSense, value); } - public string Id => DeviceID; - + public string Id => Path; public string Name => Root.DisplayName; - public object? Children => null; private object toolTip = ""; @@ -181,12 +192,13 @@ public object ToolTip public bool IsExpanded { get => false; set { } } - public IconSource? IconSource + public IconElement? IconElement { - get => new ImageIconSource() + get { - ImageSource = Icon - }; + var source = new ImageIconSource() { ImageSource = Icon }; + return source.CreateIconElement(); + } } public FrameworkElement? ItemDecorator @@ -194,20 +206,20 @@ public FrameworkElement? ItemDecorator get { if (!IsRemovable) - return null; // Removable items don't need the eject button + return null; // Non-removable items don't need the eject button var itemDecorator = new Button() { Style = Application.Current.Resources["SidebarEjectButtonStyle"] as Style, - Content = new OpacityIcon() + Content = new ThemedIcon() { - Style = Application.Current.Resources["ColorIconEject"] as Style, - Height = 16, - Width = 16 + Style = Application.Current.Resources["App.ThemedIcons.Actions.Eject.12"] as Style, + Height = 12, + Width = 12 } }; - ToolTipService.SetToolTip(itemDecorator, "Eject".GetLocalizedResource()); + ToolTipService.SetToolTip(itemDecorator, Strings.Eject.GetLocalizedResource()); itemDecorator.Click += ItemDecorator_Click; @@ -215,10 +227,11 @@ public FrameworkElement? ItemDecorator } } - private async void ItemDecorator_Click(object sender, RoutedEventArgs e) + public bool PaddedItem => false; + + private void ItemDecorator_Click(object sender, RoutedEventArgs e) { - var result = await DriveHelpers.EjectDeviceAsync(Path); - await UIHelpers.ShowDeviceEjectResultAsync(Type, result); + DriveHelpers.EjectDeviceAsync(Path); } public static async Task CreateFromPropertiesAsync(StorageFolder root, string deviceId, string label, DriveType type, IRandomAccessStream imageStream = null) @@ -239,7 +252,6 @@ public static async Task CreateFromPropertiesAsync(StorageFolder root IsLocationItem = true, ShowEjectDevice = item.IsRemovable, ShowShellItems = true, - ShowFormatDrive = !(item.Type == DriveType.Network || string.Equals(root.Path, $@"{Environment.GetEnvironmentVariable("SystemDrive")}\", StringComparison.OrdinalIgnoreCase)), ShowProperties = true }; item.Path = string.IsNullOrEmpty(root.Path) ? $"\\\\?\\{root.Name}\\" : root.Path; @@ -268,7 +280,31 @@ public async Task UpdatePropertiesAsync() { try { - var properties = await Root.Properties.RetrievePropertiesAsync(["System.FreeSpace", "System.Capacity"]) + // For cloud drives, try to get quota from the sync root provider first + if (Type == DriveType.CloudDrive) + { + try + { + var syncRootStatus = await SyncRootHelpers.GetSyncRootQuotaAsync(Path); + if (syncRootStatus.Success) + { + MaxSpace = ByteSize.FromBytes(syncRootStatus.Capacity); + SpaceUsed = ByteSize.FromBytes(syncRootStatus.Used); + FreeSpace = MaxSpace - SpaceUsed; + + SpaceText = GetSizeString(); + + if (MaxSpace.Bytes > 0) + PercentageUsed = 100.0f - (float)(FreeSpace.Bytes / MaxSpace.Bytes) * 100.0f; + + OnPropertyChanged(nameof(ShowDriveDetails)); + return; + } + } + catch { } + } + + var properties = await Root.Properties.RetrievePropertiesAsync(["System.FreeSpace", "System.Capacity", "System.Volume.FileSystem"]) .AsTask().WithTimeoutAsync(TimeSpan.FromSeconds(5)); if (properties is not null && properties["System.Capacity"] is not null && properties["System.FreeSpace"] is not null) @@ -284,22 +320,34 @@ public async Task UpdatePropertiesAsync() } else { - SpaceText = "Unknown".GetLocalizedResource(); + SpaceText = Strings.Unknown.GetLocalizedResource(); MaxSpace = SpaceUsed = FreeSpace = ByteSize.FromBytes(0); } + if (properties is not null && properties["System.Volume.FileSystem"] is not null) + Filesystem = (string)properties["System.Volume.FileSystem"]; + else + Filesystem = string.Empty; + OnPropertyChanged(nameof(ShowDriveDetails)); } catch (Exception) { - SpaceText = "Unknown".GetLocalizedResource(); + SpaceText = Strings.Unknown.GetLocalizedResource(); MaxSpace = SpaceUsed = FreeSpace = ByteSize.FromBytes(0); + Filesystem = string.Empty; OnPropertyChanged(nameof(ShowDriveDetails)); } } - public int CompareTo(INavigationControlItem other) + public async IAsyncEnumerable GetItemsAsync(StorableType storableType = StorableType.All, [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + yield break; + } + + public int CompareTo(INavigationControlItem? other) { var result = Type.CompareTo((other as DriveItem)?.Type ?? Type); return result == 0 ? Text.CompareTo(other.Text) : result; @@ -321,48 +369,24 @@ public async Task LoadThumbnailAsync() if (Root is not null) { using var thumbnail = await DriveHelpers.GetThumbnailAsync(Root); - IconData ??= await thumbnail.ToByteArrayAsync(); + IconData ??= thumbnail is not null ? await thumbnail.ToByteArrayAsync() : null; } if (string.Equals(DeviceID, "network-folder")) - IconData ??= UIHelpers.GetSidebarIconResourceInfo(Constants.ImageRes.Network).IconData; + IconData ??= UIHelpers.GetSidebarIconResourceInfo(Constants.ImageRes.Network)?.IconData; - IconData ??= UIHelpers.GetSidebarIconResourceInfo(Constants.ImageRes.Folder).IconData; + IconData ??= UIHelpers.GetSidebarIconResourceInfo(Constants.ImageRes.Folder)?.IconData; - Icon ??= await IconData.ToBitmapAsync(); + Icon ??= IconData is not null ? await IconData.ToBitmapAsync() : null; } private string GetSizeString() { return string.Format( - "DriveFreeSpaceAndCapacity".GetLocalizedResource(), + Strings.DriveFreeSpaceAndCapacity.GetLocalizedResource(), FreeSpace.ToSizeString(), MaxSpace.ToSizeString()); } - - public Task GetFileAsync(string fileName, CancellationToken cancellationToken = default) - { - var folder = new WindowsStorageFolder(Root); - return folder.GetFileAsync(fileName, cancellationToken); - } - - public Task GetFolderAsync(string folderName, CancellationToken cancellationToken = default) - { - var folder = new WindowsStorageFolder(Root); - return folder.GetFolderAsync(folderName, cancellationToken); - } - - public IAsyncEnumerable GetItemsAsync(StorableKind kind = StorableKind.All, CancellationToken cancellationToken = default) - { - var folder = new WindowsStorageFolder(Root); - return folder.GetItemsAsync(kind, cancellationToken); - } - - public Task GetParentAsync(CancellationToken cancellationToken = default) - { - var folder = new WindowsStorageFolder(Root); - return folder.GetParentAsync(cancellationToken); - } } public enum DriveType diff --git a/src/Files.App/Data/Items/EncodingItem.cs b/src/Files.App/Data/Items/EncodingItem.cs new file mode 100644 index 000000000000..51ec61b121e9 --- /dev/null +++ b/src/Files.App/Data/Items/EncodingItem.cs @@ -0,0 +1,80 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +using System.Text; + +namespace Files.App.Data.Items +{ + /// + /// Represents a text encoding in the application. + /// + public sealed class EncodingItem + { + + public Encoding? Encoding { get; set; } + + /// + /// Gets the encoding name. e.g. English (United States) + /// + public string Name { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The code of the language. + public EncodingItem(string? code) + { + if (string.IsNullOrEmpty(code)) + { + Encoding = null; + Name = Strings.Default.GetLocalizedResource(); + } + else + { + Encoding = Encoding.GetEncoding(code); + Name = Encoding.EncodingName; + } + } + + public EncodingItem(Encoding encoding, string name) + { + Encoding = encoding; + Name = name; + } + + public static EncodingItem[] Defaults = new string?[] { + null,//System Default + "UTF-8", + + //All possible Windows system encodings + //reference: https://en.wikipedia.org/wiki/Windows_code_page + //East Asian + "shift_jis", //Japanese + "gb2312", //Simplified Chinese + "big5", //Traditional Chinese + "ks_c_5601-1987", //Korean + + //Southeast Asian + "Windows-1258", //Vietnamese + "Windows-874", //Thai + + //Middle East + "Windows-1256", //Arabic + "Windows-1255", //Hebrew + "Windows-1254", //Turkish + + //European + "Windows-1252", //Western European + "Windows-1250", //Central European + "Windows-1251", //Cyrillic + "Windows-1253", //Greek + "Windows-1257", //Baltic + + "macintosh", + } + .Select(x => new EncodingItem(x)) + .ToArray(); + + public override string ToString() => Name; + } +} diff --git a/src/Files.App/Data/Items/FileTagItem.cs b/src/Files.App/Data/Items/FileTagItem.cs index e7283c69440a..092f0ef105b3 100644 --- a/src/Files.App/Data/Items/FileTagItem.cs +++ b/src/Files.App/Data/Items/FileTagItem.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using CommunityToolkit.WinUI.Helpers; using Microsoft.UI.Xaml; @@ -9,7 +9,7 @@ namespace Files.App.Data.Items { - public sealed class FileTagItem : ObservableObject, INavigationControlItem + public sealed partial class FileTagItem : ObservableObject, INavigationControlItem { public string Text { get; set; } @@ -20,7 +20,7 @@ public string Path set { path = value; - OnPropertyChanged(nameof(IconSource)); + OnPropertyChanged(nameof(IconElement)); OnPropertyChanged(nameof(ToolTip)); } } @@ -34,24 +34,30 @@ public string Path public NavigationControlItemType ItemType => NavigationControlItemType.FileTag; - public int CompareTo(INavigationControlItem other) + public int CompareTo(INavigationControlItem? other) => Text.CompareTo(other.Text); public TagViewModel FileTag { get; set; } public object? Children => null; - public IconSource? IconSource + public IconElement? IconElement { - get => new PathIconSource() + get { - Data = (Geometry)XamlBindingHelper.ConvertValue(typeof(Geometry), (string)Application.Current.Resources["ColorIconFilledTag"]), - Foreground = new SolidColorBrush(FileTag.Color.ToColor()) - }; + var source = new PathIconSource() + { + Data = (Geometry)XamlBindingHelper.ConvertValue(typeof(Geometry), (string)Application.Current.Resources["App.Theme.PathIcon.FilledTag"]), + Foreground = new SolidColorBrush(FileTag.Color.ToColor()) + }; + return source.CreateIconElement(); + } } public object ToolTip => Text; public bool IsExpanded { get => false; set { } } + + public bool PaddedItem => false; } } diff --git a/src/Files.App/Data/Items/IListedItem.cs b/src/Files.App/Data/Items/IListedItem.cs new file mode 100644 index 000000000000..3864f34f5bbd --- /dev/null +++ b/src/Files.App/Data/Items/IListedItem.cs @@ -0,0 +1,79 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Files.App.ViewModels.Properties; +using Microsoft.UI.Xaml.Media.Imaging; +using Windows.Storage; + +namespace Files.App.Utils +{ + public interface IListedItem + { + GitItem AsGitItem { get; } + RecycleBinItem AsRecycleBinItem { get; } + bool ContainsFilesOrFolders { get; set; } + string ContextualProperty { get; set; } + BitmapImage CustomIcon { get; set; } + Uri CustomIconSource { get; set; } + ObservableCollection FileDetails { get; set; } + string FileExtension { get; set; } + ulong? FileFRN { get; set; } + BitmapImage FileImage { get; set; } + string FileSize { get; set; } + long FileSizeBytes { get; set; } + string FileSizeDisplay { get; } + string[] FileTags { get; set; } + IList? FileTagsUI { get; } + string FileVersion { get; set; } + string FolderRelativeId { get; set; } + bool HasTags { get; set; } + BitmapImage IconOverlay { get; set; } + string ImageDimensions { get; set; } + bool IsAlternateStream { get; } + bool IsArchive { get; } + bool IsDriveRoot { get; } + bool IsElevationRequired { get; set; } + bool IsExecutable { get; } + bool IsFolder { get; } + bool IsFtpItem { get; } + bool IsGitItem { get; } + bool IsHiddenItem { get; set; } + bool IsItemPinnedToStart { get; } + bool IsLibrary { get; } + bool IsLinkItem { get; } + bool IsPinned { get; } + bool IsRecycleBinItem { get; } + bool IsScriptFile { get; } + bool IsShortcut { get; } + string ItemDateAccessed { get; } + DateTimeOffset ItemDateAccessedReal { get; set; } + string ItemDateCreated { get; } + DateTimeOffset ItemDateCreatedReal { get; set; } + string ItemDateModified { get; } + DateTimeOffset ItemDateModifiedReal { get; set; } + BaseStorageFile ItemFile { get; set; } + string ItemNameRaw { get; set; } + string ItemPath { get; set; } + ObservableCollection ItemProperties { get; set; } + bool ItemPropertiesInitialized { get; set; } + string ItemTooltipText { get; } + string ItemType { get; set; } + string Key { get; set; } + bool LoadCustomIcon { get; set; } + bool LoadFileIcon { get; set; } + ByteSizeLib.ByteSize MaxSpace { get; set; } + string MediaDuration { get; set; } + string Name { get; } + bool NeedsPlaceholderGlyph { get; set; } + double Opacity { get; set; } + StorageItemTypes PrimaryItemAttribute { get; set; } + BitmapImage ShieldIcon { get; set; } + bool ShowDriveStorageDetails { get; set; } + ByteSizeLib.ByteSize SpaceUsed { get; set; } + string SyncStatusString { get; } + CloudDriveSyncStatusUI SyncStatusUI { get; set; } + + string ToString(); + void UpdateContainsFilesFolders(); + } +} \ No newline at end of file diff --git a/src/Files.App/Data/Items/IconFileInfo.cs b/src/Files.App/Data/Items/IconFileInfo.cs index 21a066d9bc52..b70f81fb2439 100644 --- a/src/Files.App/Data/Items/IconFileInfo.cs +++ b/src/Files.App/Data/Items/IconFileInfo.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Items { diff --git a/src/Files.App/Data/Items/IniSectionDataItem.cs b/src/Files.App/Data/Items/IniSectionDataItem.cs index aa018aeba0c7..ae99f8c5830b 100644 --- a/src/Files.App/Data/Items/IniSectionDataItem.cs +++ b/src/Files.App/Data/Items/IniSectionDataItem.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Collections.Immutable; diff --git a/src/Files.App/Data/Items/ListedItem.cs b/src/Files.App/Data/Items/ListedItem.cs index 30f4c7d77317..aecfa53d8428 100644 --- a/src/Files.App/Data/Items/ListedItem.cs +++ b/src/Files.App/Data/Items/ListedItem.cs @@ -1,21 +1,22 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.ViewModels.Properties; using Files.Shared.Helpers; using FluentFTP; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Media.Imaging; -using System.Drawing; using System.IO; using System.Text; using Windows.Storage; +using Windows.Win32.UI.WindowsAndMessaging; +using ByteSize = ByteSizeLib.ByteSize; #pragma warning disable CS0618 // Type or member is obsolete namespace Files.App.Utils { - public class ListedItem : ObservableObject, IGroupableItem + public partial class ListedItem : ObservableObject, IGroupableItem, IListedItem { protected static IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetRequiredService(); @@ -29,6 +30,8 @@ public class ListedItem : ObservableObject, IGroupableItem public StorageItemTypes PrimaryItemAttribute { get; set; } + public byte[]? PreloadedIconData { get; set; } + private volatile int itemPropertiesInitialized = 0; public bool ItemPropertiesInitialized { @@ -41,15 +44,15 @@ public string ItemTooltipText get { var tooltipBuilder = new StringBuilder(); - tooltipBuilder.AppendLine($"{"NameWithColon".GetLocalizedResource()} {Name}"); - tooltipBuilder.AppendLine($"{"ItemType".GetLocalizedResource()} {itemType}"); - tooltipBuilder.Append($"{"ToolTipDescriptionDate".GetLocalizedResource()} {ItemDateModified}"); + tooltipBuilder.AppendLine($"{Strings.NameWithColon.GetLocalizedResource()} {Name}"); + tooltipBuilder.AppendLine($"{Strings.ItemType.GetLocalizedResource()} {itemType}"); + tooltipBuilder.Append($"{Strings.ToolTipDescriptionDate.GetLocalizedResource()} {ItemDateModified}"); if (!string.IsNullOrWhiteSpace(FileSize)) - tooltipBuilder.Append($"{Environment.NewLine}{"SizeLabel".GetLocalizedResource()} {FileSize}"); - if (SyncStatusUI.SyncStatus is not CloudDriveSyncStatus.FileOnline and not CloudDriveSyncStatus.FolderOnline && !string.IsNullOrWhiteSpace(DimensionsDisplay)) - tooltipBuilder.Append($"{Environment.NewLine}{"PropertyDimensionsColon".GetLocalizedResource()} {DimensionsDisplay}"); + tooltipBuilder.Append($"{Environment.NewLine}{Strings.SizeLabel.GetLocalizedResource()} {FileSize}"); + if (!string.IsNullOrWhiteSpace(ImageDimensions)) + tooltipBuilder.Append($"{Environment.NewLine}{Strings.PropertyDimensionsColon.GetLocalizedResource()} {ImageDimensions}"); if (SyncStatusUI.LoadSyncStatus) - tooltipBuilder.Append($"{Environment.NewLine}{"StatusWithColon".GetLocalizedResource()} {syncStatusUI.SyncStatusString}"); + tooltipBuilder.Append($"{Environment.NewLine}{Strings.StatusWithColon.GetLocalizedResource()} {syncStatusUI.SyncStatusString}"); return tooltipBuilder.ToString(); } @@ -166,7 +169,7 @@ public CloudDriveSyncStatusUI SyncStatusUI // This is used to avoid passing a null value to AutomationProperties.Name, which causes a crash public string SyncStatusString { - get => string.IsNullOrEmpty(SyncStatusUI?.SyncStatusString) ? "CloudDriveSyncStatus_Unknown".GetLocalizedResource() : SyncStatusUI.SyncStatusString; + get => string.IsNullOrEmpty(SyncStatusUI?.SyncStatusString) ? Strings.CloudDriveSyncStatus_Unknown.GetLocalizedResource() : SyncStatusUI.SyncStatusString; } private BitmapImage fileImage; @@ -186,7 +189,7 @@ public BitmapImage FileImage } } - public bool IsItemPinnedToStart => StartMenuService.IsPinned((this as ShortcutItem)?.TargetPath ?? ItemPath); + public bool IsItemPinnedToStart => StartMenuService.IsPinned((this as IShortcutItem)?.TargetPath ?? ItemPath); private BitmapImage iconOverlay; public BitmapImage IconOverlay @@ -276,7 +279,7 @@ public string FileSize } } - public string FileSizeDisplay => string.IsNullOrEmpty(FileSize) ? "ItemSizeNotCalculated".GetLocalizedResource() : FileSize; + public string FileSizeDisplay => string.IsNullOrEmpty(FileSize) ? Strings.ItemSizeNotCalculated.GetLocalizedResource() : FileSize; public long FileSizeBytes { get; set; } @@ -329,39 +332,58 @@ public ObservableCollection ItemProperties set => SetProperty(ref itemProperties, value); } - public string DimensionsDisplay + private bool showDriveStorageDetails; + public bool ShowDriveStorageDetails { - get - { - int imageHeight = 0; - int imageWidth = 0; + get => showDriveStorageDetails; + set => SetProperty(ref showDriveStorageDetails, value); + } - var isImageFile = FileExtensionHelpers.IsImageFile(FileExtension); - if (isImageFile) - { - try - { - // TODO: Consider to use 'System.Kind' instead. - using FileStream fileStream = new(ItemPath, FileMode.Open, FileAccess.Read, FileShare.Read); - using Image image = Image.FromStream(fileStream, false, false); - - if (image is not null) - { - imageHeight = image.Height; - imageWidth = image.Width; - } - } - catch { } - } + private ByteSize maxSpace; + public ByteSize MaxSpace + { + get => maxSpace; + set => SetProperty(ref maxSpace, value); + } + + private ByteSize spaceUsed; + public ByteSize SpaceUsed + { + get => spaceUsed; + set => SetProperty(ref spaceUsed, value); - return - isImageFile && - imageWidth > 0 && - imageHeight > 0 - ? $"{imageWidth} \u00D7 {imageHeight}" - : string.Empty; - } + } + + private string imageDimensions; + public string ImageDimensions + { + get => imageDimensions; + set => SetProperty(ref imageDimensions, value); + } + + private string fileVersion; + public string FileVersion + { + get => fileVersion; + set => SetProperty(ref fileVersion, value); + } + + private string mediaDuration; + public string MediaDuration + { + get => mediaDuration; + set => SetProperty(ref mediaDuration, value); + } + + /// + /// Contextual property that changes based on the item type. + /// + private string contextualProperty; + public string ContextualProperty + { + get => contextualProperty; + set => SetProperty(ref contextualProperty, value); } /// @@ -385,19 +407,19 @@ public override string ToString() string suffix; if (IsRecycleBinItem) { - suffix = "RecycleBinItemAutomation".GetLocalizedResource(); + suffix = Strings.RecycleBinItemAutomation.GetLocalizedResource(); } else if (IsShortcut) { - suffix = "ShortcutItemAutomation".GetLocalizedResource(); + suffix = Strings.ShortcutItemAutomation.GetLocalizedResource(); } else if (IsLibrary) { - suffix = "Library".GetLocalizedResource(); + suffix = Strings.Library.GetLocalizedResource(); } else { - suffix = PrimaryItemAttribute == StorageItemTypes.File ? "Folder".GetLocalizedResource() : "FolderItemAutomation".GetLocalizedResource(); + suffix = PrimaryItemAttribute == StorageItemTypes.File ? Strings.Folder.GetLocalizedResource() : "FolderItemAutomation".GetLocalizedResource(); } return $"{Name}, {suffix}"; @@ -405,14 +427,14 @@ public override string ToString() public bool IsFolder => PrimaryItemAttribute is StorageItemTypes.Folder; public bool IsRecycleBinItem => this is RecycleBinItem; - public bool IsShortcut => this is ShortcutItem; + public bool IsShortcut => this is IShortcutItem; public bool IsLibrary => this is LibraryItem; - public bool IsLinkItem => IsShortcut && ((ShortcutItem)this).IsUrl; + public bool IsLinkItem => IsShortcut && ((IShortcutItem)this).IsUrl; public bool IsFtpItem => this is FtpItem; public bool IsArchive => this is ZipItem; public bool IsAlternateStream => this is AlternateStreamItem; - public bool IsGitItem => this is GitItem; - public virtual bool IsExecutable => FileExtensionHelpers.IsExecutableFile(ItemPath); + public bool IsGitItem => this is IGitItem; + public virtual bool IsExecutable => !IsFolder && FileExtensionHelpers.IsExecutableFile(ItemPath); public virtual bool IsScriptFile => FileExtensionHelpers.IsScriptFile(ItemPath); public bool IsPinned => App.QuickAccessManager.Model.PinnedFolders.Contains(itemPath); public bool IsDriveRoot => ItemPath == PathNormalization.GetPathRoot(ItemPath); @@ -442,7 +464,7 @@ public void UpdateContainsFilesFolders() } } - public sealed class RecycleBinItem : ListedItem + public sealed partial class RecycleBinItem : ListedItem { public RecycleBinItem(string folderRelativeId) : base(folderRelativeId) { @@ -471,7 +493,7 @@ public DateTimeOffset ItemDateDeletedReal public string ItemOriginalFolderName => Path.GetFileName(ItemOriginalFolder); } - public sealed class FtpItem : ListedItem + public sealed partial class FtpItem : ListedItem { public FtpItem(FtpListItem item, string folder) : base(null) { @@ -484,7 +506,7 @@ public FtpItem(FtpListItem item, string folder) : base(null) PrimaryItemAttribute = isFile ? StorageItemTypes.File : StorageItemTypes.Folder; ItemPropertiesInitialized = false; - var itemType = isFile ? "File".GetLocalizedResource() : "Folder".GetLocalizedResource(); + var itemType = isFile ? Strings.File.GetLocalizedResource() : Strings.Folder.GetLocalizedResource(); if (isFile && Name.Contains('.', StringComparison.Ordinal)) { itemType = FileExtension.Trim('.') + " " + itemType; @@ -501,13 +523,13 @@ public FtpItem(FtpListItem item, string folder) : base(null) public async Task ToStorageItem() => PrimaryItemAttribute switch { - StorageItemTypes.File => await new FtpStorageFile(ItemPath, ItemNameRaw, ItemDateCreatedReal).ToStorageFileAsync(), - StorageItemTypes.Folder => new FtpStorageFolder(ItemPath, ItemNameRaw, ItemDateCreatedReal), + StorageItemTypes.File => await new Utils.Storage.FtpStorageFile(ItemPath, ItemNameRaw, ItemDateCreatedReal).ToStorageFileAsync(), + StorageItemTypes.Folder => new Utils.Storage.FtpStorageFolder(ItemPath, ItemNameRaw, ItemDateCreatedReal), _ => throw new InvalidDataException(), }; } - public sealed class ShortcutItem : ListedItem + public sealed partial class ShortcutItem : ListedItem, IShortcutItem { public ShortcutItem(string folderRelativeId) : base(folderRelativeId) { @@ -526,12 +548,14 @@ public override string Name public string Arguments { get; set; } public string WorkingDirectory { get; set; } public bool RunAsAdmin { get; set; } + public SHOW_WINDOW_CMD ShowWindowCommand { get; set; } public bool IsUrl { get; set; } public bool IsSymLink { get; set; } + public override bool IsScriptFile => FileExtensionHelpers.IsScriptFile(TargetPath); public override bool IsExecutable => FileExtensionHelpers.IsExecutableFile(TargetPath, true); } - public sealed class ZipItem : ListedItem + public sealed partial class ZipItem : ListedItem { public ZipItem(string folderRelativeId) : base(folderRelativeId) { @@ -555,14 +579,14 @@ public ZipItem() : base() { } } - public sealed class LibraryItem : ListedItem + public sealed partial class LibraryItem : ListedItem { public LibraryItem(LibraryLocationItem library) : base(null) { ItemPath = library.Path; ItemNameRaw = library.Text; PrimaryItemAttribute = StorageItemTypes.Folder; - ItemType = "Library".GetLocalizedResource(); + ItemType = Strings.Library.GetLocalizedResource(); LoadCustomIcon = true; CustomIcon = library.Icon; //CustomIconSource = library.IconSource; @@ -582,7 +606,7 @@ public LibraryItem(LibraryLocationItem library) : base(null) public ReadOnlyCollection Folders { get; } } - public sealed class AlternateStreamItem : ListedItem + public sealed partial class AlternateStreamItem : ListedItem { public string MainStreamPath => ItemPath.Substring(0, ItemPath.LastIndexOf(':')); public string MainStreamName => Path.GetFileName(MainStreamPath); @@ -602,7 +626,7 @@ public override string Name } } - public sealed class GitItem : ListedItem + public partial class GitItem : ListedItem, IGitItem { private volatile int statusPropertiesInitialized = 0; public bool StatusPropertiesInitialized @@ -678,4 +702,51 @@ public string? GitLastCommitFullSha set => SetProperty(ref _GitLastCommitFullSha, value); } } + public sealed partial class GitShortcutItem : GitItem, IShortcutItem + { + public string TargetPath { get; set; } + + public override string Name + => IsSymLink ? base.Name : Path.GetFileNameWithoutExtension(ItemNameRaw); // Always hide extension for shortcuts + + public string Arguments { get; set; } + public string WorkingDirectory { get; set; } + public bool RunAsAdmin { get; set; } + public SHOW_WINDOW_CMD ShowWindowCommand { get; set; } + public bool IsUrl { get; set; } + public bool IsSymLink { get; set; } + public override bool IsScriptFile => FileExtensionHelpers.IsScriptFile(TargetPath); + public override bool IsExecutable => FileExtensionHelpers.IsExecutableFile(TargetPath, true); + } + public interface IGitItem : IListedItem + { + public bool StatusPropertiesInitialized { get; set; } + public bool CommitPropertiesInitialized { get; set; } + + public Style? UnmergedGitStatusIcon { get; set; } + + public string? UnmergedGitStatusName { get; set; } + + public DateTimeOffset? GitLastCommitDate { get; set; } + + public string? GitLastCommitDateHumanized { get; set; } + + public string? GitLastCommitMessage { get; set; } + + public string? GitLastCommitAuthor { get; set; } + + public string? GitLastCommitSha { get; set; } + + public string? GitLastCommitFullSha { get; set; } + } + public interface IShortcutItem : IListedItem + { + public string TargetPath { get; set; } + public string Arguments { get; set; } + public string WorkingDirectory { get; set; } + public bool RunAsAdmin { get; set; } + public SHOW_WINDOW_CMD ShowWindowCommand { get; set; } + public bool IsUrl { get; set; } + public bool IsSymLink { get; set; } + } } diff --git a/src/Files.App/Data/Items/LocationItem.cs b/src/Files.App/Data/Items/LocationItem.cs index 6994d3f18a36..0d5b2dc5bed2 100644 --- a/src/Files.App/Data/Items/LocationItem.cs +++ b/src/Files.App/Data/Items/LocationItem.cs @@ -1,14 +1,16 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. +using Files.App.Controls; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Markup; using Microsoft.UI.Xaml.Media.Imaging; using System.IO; namespace Files.App.Data.Items { - public class LocationItem : ObservableObject, INavigationControlItem + public partial class LocationItem : ObservableObject, INavigationControlItem { public BitmapImage icon; public BitmapImage Icon @@ -17,7 +19,7 @@ public BitmapImage Icon set { SetProperty(ref icon, value, nameof(Icon)); - OnPropertyChanged(nameof(IconSource)); + OnPropertyChanged(nameof(IconElement)); } } @@ -47,7 +49,9 @@ public string Path Path.Contains('?', StringComparison.Ordinal) || Path.StartsWith("shell:", StringComparison.OrdinalIgnoreCase) || Path.EndsWith(ShellLibraryItem.EXTENSION, StringComparison.OrdinalIgnoreCase) || - Path == "Home" + Path == "Home" || + Path == "ReleaseNotes" || + Path == "Settings" ? Text : Path; } @@ -60,12 +64,17 @@ public NavigationControlItemType ItemType public object? Children => Section == SectionType.Home ? null : ChildItems; public BulkConcurrentObservableCollection? ChildItems { get; set; } - public IconSource? IconSource + + public IconElement? IconElement { - get => new ImageIconSource() + get { - ImageSource = icon - }; + var source = new ImageIconSource() + { + ImageSource = icon + }; + return source.CreateIconElement(); + } } public bool SelectsOnInvoked { get; set; } = true; @@ -103,7 +112,7 @@ public FrameworkElement? ItemDecorator { if (Section == SectionType.Pinned) { - return new OpacityIcon() + return new ThemedIcon() { Style = Application.Current.Resources["SidebarFavouritesPinnedIcon"] as Style }; @@ -112,7 +121,9 @@ public FrameworkElement? ItemDecorator } } - public int CompareTo(INavigationControlItem other) + public bool PaddedItem => Section == SectionType.Home; + + public int CompareTo(INavigationControlItem? other) => Text.CompareTo(other.Text); public static T Create() where T : LocationItem, new() @@ -121,13 +132,20 @@ public int CompareTo(INavigationControlItem other) } } - public sealed class RecycleBinLocationItem : LocationItem + public sealed partial class RecycleBinLocationItem : LocationItem { - public void RefreshSpaceUsed(object sender, FileSystemEventArgs e) + private readonly IStorageTrashBinService StorageTrashBinService = Ioc.Default.GetRequiredService(); + + public async void RefreshSpaceUsed(object? sender, FileSystemEventArgs e) + { + await RefreshSpaceUsedAsync(); + } + + private Task RefreshSpaceUsedAsync() { - MainWindow.Instance.DispatcherQueue.TryEnqueue(() => + return MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(async () => { - SpaceUsed = RecycleBinHelpers.GetSize(); + SpaceUsed = await Task.Run(() => StorageTrashBinService.GetSize()); }); } @@ -149,10 +167,10 @@ public override object ToolTip public RecycleBinLocationItem() { - SpaceUsed = RecycleBinHelpers.GetSize(); + StorageTrashBinService.Watcher.ItemAdded += RefreshSpaceUsed; + StorageTrashBinService.Watcher.ItemDeleted += RefreshSpaceUsed; - RecycleBinManager.Default.RecycleBinItemCreated += RefreshSpaceUsed; - RecycleBinManager.Default.RecycleBinItemDeleted += RefreshSpaceUsed; + _ = RefreshSpaceUsedAsync(); } } } diff --git a/src/Files.App/Data/Items/ModifiableActionItem.cs b/src/Files.App/Data/Items/ModifiableActionItem.cs index 92d4758bbf72..e0be7c53fd50 100644 --- a/src/Files.App/Data/Items/ModifiableActionItem.cs +++ b/src/Files.App/Data/Items/ModifiableActionItem.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Items { /// /// Represents item for a hotkey that is registered for a . /// - public class ModifiableActionItem : ObservableObject + public partial class ModifiableActionItem : ObservableObject { public CommandCodes CommandCode { get; set; } diff --git a/src/Files.App/Data/Items/NavigationBarSuggestionItem.cs b/src/Files.App/Data/Items/NavigationBarSuggestionItem.cs index e80ef6e61bae..12376354e783 100644 --- a/src/Files.App/Data/Items/NavigationBarSuggestionItem.cs +++ b/src/Files.App/Data/Items/NavigationBarSuggestionItem.cs @@ -1,16 +1,27 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Files.App.Controls; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Media; +using Windows.AI.Actions.Hosting; namespace Files.App.Data.Items { - public sealed class NavigationBarSuggestionItem : ObservableObject + [Obsolete("Remove once Omnibar goes out of experimental.")] + public sealed partial class NavigationBarSuggestionItem : ObservableObject, IOmnibarTextMemberPathProvider { + private ImageSource? _ActionIconSource; + public ImageSource? ActionIconSource { get => _ActionIconSource; set => SetProperty(ref _ActionIconSource, value); } + + private Style? _ThemedIconStyle; + public Style? ThemedIconStyle { get => _ThemedIconStyle; set => SetProperty(ref _ThemedIconStyle, value); } + + private string? _Glyph; + public string? Glyph { get => _Glyph; set => SetProperty(ref _Glyph, value); } + private string? _Text; - public string? Text - { - get => _Text; - set => SetProperty(ref _Text, value); - } + public string? Text { get => _Text; set => SetProperty(ref _Text, value); } private string? _PrimaryDisplay; public string? PrimaryDisplay @@ -41,6 +52,14 @@ public string? PrimaryDisplayPreMatched private set => SetProperty(ref _PrimaryDisplayPreMatched, value); } + + private ActionInstance? _ActionInstance; + public ActionInstance? ActionInstance + { + get => _ActionInstance; + set => SetProperty(ref _ActionInstance, value); + } + private string? _PrimaryDisplayMatched; public string? PrimaryDisplayMatched { @@ -87,5 +106,16 @@ private void UpdatePrimaryDisplay() } } } + + public string GetTextMemberPath(string textMemberPath) + { + return textMemberPath switch + { + nameof(Text) => Text, + nameof(PrimaryDisplay) => PrimaryDisplay, + nameof(SearchText) => SearchText, + _ => string.Empty + }; + } } } diff --git a/src/Files.App/Data/Items/NavigationViewItemButtonStyleItem.cs b/src/Files.App/Data/Items/NavigationViewItemButtonStyleItem.cs index 787d53a8bcdb..6232a04a268f 100644 --- a/src/Files.App/Data/Items/NavigationViewItemButtonStyleItem.cs +++ b/src/Files.App/Data/Items/NavigationViewItemButtonStyleItem.cs @@ -1,21 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; namespace Files.App.Data.Items { - public sealed class NavigationViewItemButtonStyleItem : ObservableObject + public sealed partial class NavigationViewItemButtonStyleItem : ObservableObject { public string? Name; public PropertiesNavigationViewItemType ItemType; - private Style _OpacityIconStyle = (Style)Application.Current.Resources["ColorIconGeneralProperties"]; - public Style OpacityIconStyle + private Style _ThemedIconStyle = (Style)Application.Current.Resources["App.ThemedIcons.Properties"]; + public Style ThemedIconStyle { - get => _OpacityIconStyle; - set => SetProperty(ref _OpacityIconStyle, value); + get => _ThemedIconStyle; + set => SetProperty(ref _ThemedIconStyle, value); } private bool _IsSelected; diff --git a/src/Files.App/Data/Items/OpenSourceLibraryItem.cs b/src/Files.App/Data/Items/OpenSourceLibraryItem.cs index 1e6d2428a07a..b86baf323442 100644 --- a/src/Files.App/Data/Items/OpenSourceLibraryItem.cs +++ b/src/Files.App/Data/Items/OpenSourceLibraryItem.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Items { diff --git a/src/Files.App/Data/Items/PathBoxItem.cs b/src/Files.App/Data/Items/PathBoxItem.cs index 20995631df36..88199efc841e 100644 --- a/src/Files.App/Data/Items/PathBoxItem.cs +++ b/src/Files.App/Data/Items/PathBoxItem.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Items { @@ -8,5 +8,7 @@ public sealed class PathBoxItem public string? Title { get; set; } public string? Path { get; set; } + + public string? ChevronToolTip { get; set; } } } diff --git a/src/Files.App/Data/Items/ShelfItem.cs b/src/Files.App/Data/Items/ShelfItem.cs new file mode 100644 index 000000000000..81aa98a8e4f9 --- /dev/null +++ b/src/Files.App/Data/Items/ShelfItem.cs @@ -0,0 +1,43 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Files.Shared.Utils; + +namespace Files.App.Data.Items +{ + [Bindable(true)] + public sealed partial class ShelfItem : ObservableObject, IWrapper, IAsyncInitialize + { + private readonly IImageService _imageService; + private readonly ICollection _sourceCollection; + + [ObservableProperty] public partial IImage? Icon { get; set; } + [ObservableProperty] public partial string? Name { get; set; } + [ObservableProperty] public partial string? Path { get; set; } + + /// + public IStorableChild Inner { get; } + + public ShelfItem(IStorableChild storable, ICollection sourceCollection, IImage? icon = null) + { + _imageService = Ioc.Default.GetRequiredService(); + _sourceCollection = sourceCollection; + Inner = storable; + Icon = icon; + Name = storable.Name; + Path = storable.Id; + } + + /// + public async Task InitAsync(CancellationToken cancellationToken = default) + { + Icon = await _imageService.GetIconAsync(Inner, cancellationToken); + } + + [RelayCommand] + public void Remove() + { + _sourceCollection.Remove(this); + } + } +} diff --git a/src/Files.App/Data/Items/ShellFileItem.cs b/src/Files.App/Data/Items/ShellFileItem.cs index 5c53603c6e2f..ead2e56294da 100644 --- a/src/Files.App/Data/Items/ShellFileItem.cs +++ b/src/Files.App/Data/Items/ShellFileItem.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Items { diff --git a/src/Files.App/Data/Items/ShellLibraryItem.cs b/src/Files.App/Data/Items/ShellLibraryItem.cs index a3db0bc5e145..d6a11e5ecd98 100644 --- a/src/Files.App/Data/Items/ShellLibraryItem.cs +++ b/src/Files.App/Data/Items/ShellLibraryItem.cs @@ -1,7 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System.IO; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Items { @@ -9,7 +7,7 @@ public sealed class ShellLibraryItem { public const string EXTENSION = ".library-ms"; - public static readonly string LibrariesPath = Win32PInvoke.GetFolderFromKnownFolderGUID(new Guid("1B3EA5DC-B587-4786-B4EF-BD1DC332AEAE")); + public static readonly string LibrariesPath = Win32Helper.GetFolderFromKnownFolderGUID(new Guid("1B3EA5DC-B587-4786-B4EF-BD1DC332AEAE")); /// /// Full path of library file.
diff --git a/src/Files.App/Data/Items/ShellLinkItem.cs b/src/Files.App/Data/Items/ShellLinkItem.cs index 91c68766fb7f..dc0acda2b37b 100644 --- a/src/Files.App/Data/Items/ShellLinkItem.cs +++ b/src/Files.App/Data/Items/ShellLinkItem.cs @@ -1,5 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Windows.Win32.UI.WindowsAndMessaging; namespace Files.App.Data.Items { @@ -15,6 +17,8 @@ public sealed class ShellLinkItem : ShellFileItem public bool InvalidTarget { get; set; } + public SHOW_WINDOW_CMD ShowWindowCommand { get; set; } + public ShellLinkItem() { } diff --git a/src/Files.App/Data/Items/ShellNewEntry.cs b/src/Files.App/Data/Items/ShellNewEntry.cs index f476954330f5..8c753737faad 100644 --- a/src/Files.App/Data/Items/ShellNewEntry.cs +++ b/src/Files.App/Data/Items/ShellNewEntry.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Items { diff --git a/src/Files.App/Data/Items/ShellOperationResult.cs b/src/Files.App/Data/Items/ShellOperationResult.cs index cac1133fd98b..7c93baddd4cd 100644 --- a/src/Files.App/Data/Items/ShellOperationResult.cs +++ b/src/Files.App/Data/Items/ShellOperationResult.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Items { diff --git a/src/Files.App/Data/Items/SidebarLibraryItem.cs b/src/Files.App/Data/Items/SidebarLibraryItem.cs index d5b445bf55c3..c9ab535aef32 100644 --- a/src/Files.App/Data/Items/SidebarLibraryItem.cs +++ b/src/Files.App/Data/Items/SidebarLibraryItem.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Items { - public sealed class LibraryLocationItem : LocationItem + public sealed partial class LibraryLocationItem : LocationItem { public string DefaultSaveFolder { get; } @@ -59,7 +59,7 @@ public async Task LoadLibraryIconAsync() public override int GetHashCode() => Path.GetHashCode(System.StringComparison.OrdinalIgnoreCase); - public override bool Equals(object obj) + public override bool Equals(object? obj) => obj is LibraryLocationItem other && GetType() == obj.GetType() && string.Equals(Path, other.Path, System.StringComparison.OrdinalIgnoreCase); } } diff --git a/src/Files.App/Data/Items/TagsListItem.cs b/src/Files.App/Data/Items/TagsListItem.cs index ac3e71319740..69542b8a6990 100644 --- a/src/Files.App/Data/Items/TagsListItem.cs +++ b/src/Files.App/Data/Items/TagsListItem.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Controls; diff --git a/src/Files.App/Data/Items/WidgetCardItem.cs b/src/Files.App/Data/Items/WidgetCardItem.cs index 192a500db3c0..50132786a412 100644 --- a/src/Files.App/Data/Items/WidgetCardItem.cs +++ b/src/Files.App/Data/Items/WidgetCardItem.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Items { diff --git a/src/Files.App/Data/Items/WidgetContainerItem.cs b/src/Files.App/Data/Items/WidgetContainerItem.cs index 471c9f00a458..8184c5f2300b 100644 --- a/src/Files.App/Data/Items/WidgetContainerItem.cs +++ b/src/Files.App/Data/Items/WidgetContainerItem.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Controls; @@ -8,7 +8,7 @@ namespace Files.App.Data.Items /// /// Represents an item of Files widget container. /// - public sealed class WidgetContainerItem : ObservableObject, IDisposable + public sealed partial class WidgetContainerItem : ObservableObject, IDisposable { // Fields diff --git a/src/Files.App/Data/Items/WidgetDriveCardItem.cs b/src/Files.App/Data/Items/WidgetDriveCardItem.cs index ab029bd80db6..c8d41ed31d4a 100644 --- a/src/Files.App/Data/Items/WidgetDriveCardItem.cs +++ b/src/Files.App/Data/Items/WidgetDriveCardItem.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Media.Imaging; namespace Files.App.Data.Items { - public sealed class WidgetDriveCardItem : WidgetCardItem, IWidgetCardItem, IComparable + public sealed partial class WidgetDriveCardItem : WidgetCardItem, IWidgetCardItem, IComparable { private byte[] thumbnailData; diff --git a/src/Files.App/Data/Items/WidgetFileTagCardItem.cs b/src/Files.App/Data/Items/WidgetFileTagCardItem.cs index 3134c7b9a6f7..b18d137ab66e 100644 --- a/src/Files.App/Data/Items/WidgetFileTagCardItem.cs +++ b/src/Files.App/Data/Items/WidgetFileTagCardItem.cs @@ -1,7 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.Core.Storage.Extensions; using Files.Shared.Utils; using System.Windows.Input; @@ -52,7 +51,7 @@ public WidgetFileTagCardItem(IStorable associatedStorable, IImage? icon) _associatedStorable = associatedStorable; _Icon = icon; _Name = associatedStorable.Name; - _Path = associatedStorable.TryGetPath(); + _Path = associatedStorable.Id; Item = this; ClickCommand = new AsyncRelayCommand(ClickAsync); diff --git a/src/Files.App/Data/Items/WidgetFileTagsContainerItem.cs b/src/Files.App/Data/Items/WidgetFileTagsContainerItem.cs index acf5f31cea4d..35d4056a647b 100644 --- a/src/Files.App/Data/Items/WidgetFileTagsContainerItem.cs +++ b/src/Files.App/Data/Items/WidgetFileTagsContainerItem.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.Shared.Utils; using System.Windows.Input; @@ -21,6 +21,8 @@ public sealed partial class WidgetFileTagsContainerItem : ObservableObject, IAsy public ObservableCollection Tags { get; } + public string Uid => _tagUid; + private string? _Color; public string? Color { @@ -57,7 +59,7 @@ public WidgetFileTagsContainerItem(string tagUid) /// public async Task InitAsync(CancellationToken cancellationToken = default) { - await foreach (var item in FileTagsService.GetItemsForTagAsync(_tagUid)) + await foreach (var item in FileTagsService.GetItemsForTagAsync(_tagUid, cancellationToken)) { var icon = await ImageService.GetIconAsync(item.Storable, default); Tags.Add(new(item.Storable, icon)); @@ -66,14 +68,14 @@ public async Task InitAsync(CancellationToken cancellationToken = default) private Task ViewMore() { - return NavigationHelpers.OpenPath($"tag:{Name}", ContentPageContext.ShellPage!); + return NavigationHelpers.OpenPath(FolderSearch.FormatTagQuery(Name!), ContentPageContext.ShellPage!); } private Task OpenAll() { SelectedTagChanged?.Invoke(this, new(Tags.Select(tag => (tag.Path, tag.IsFolder)))); - return Commands.OpenAllTaggedItems.ExecuteAsync(); + return Commands.OpenAllTagged.ExecuteAsync(); } } } diff --git a/src/Files.App/Data/Items/WidgetFolderCardItem.cs b/src/Files.App/Data/Items/WidgetFolderCardItem.cs index 4a104bc08bae..c3ea40506d40 100644 --- a/src/Files.App/Data/Items/WidgetFolderCardItem.cs +++ b/src/Files.App/Data/Items/WidgetFolderCardItem.cs @@ -1,64 +1,61 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Media.Imaging; +using Windows.Win32; +using Windows.Win32.UI.Shell; namespace Files.App.Data.Items { - public sealed class WidgetFolderCardItem : WidgetCardItem, IWidgetCardItem + public sealed partial class WidgetFolderCardItem : WidgetCardItem, IWidgetCardItem, IDisposable { - // Fields - - private byte[] _thumbnailData; - // Properties public string? AutomationProperties { get; set; } - public LocationItem? Item { get; private set; } + public new IWindowsStorable Item { get; private set; } public string? Text { get; set; } public bool IsPinned { get; set; } - public bool HasPath - => !string.IsNullOrEmpty(Path); + public string Tooltip { get; set; } private BitmapImage? _Thumbnail; - public BitmapImage? Thumbnail - { - get => _Thumbnail; - set => SetProperty(ref _Thumbnail, value); - } + public BitmapImage? Thumbnail { get => _Thumbnail; set => SetProperty(ref _Thumbnail, value); } // Constructor - public WidgetFolderCardItem(LocationItem item, string text, bool isPinned) + public WidgetFolderCardItem(IWindowsStorable item, string text, bool isPinned, string tooltip) { - if (!string.IsNullOrWhiteSpace(text)) - { - Text = text; - AutomationProperties = Text; - } - - IsPinned = isPinned; + AutomationProperties = text; Item = item; - Path = item.Path; + Text = text; + IsPinned = isPinned; + Path = item.GetDisplayName(SIGDN.SIGDN_DESKTOPABSOLUTEPARSING); + Tooltip = tooltip; } // Methods public async Task LoadCardThumbnailAsync() { - var result = await FileThumbnailHelper.GetIconAsync( - Path, - Constants.ShellIconSizes.Large, - true, - IconOptions.ReturnIconOnly | IconOptions.UseCurrentScale); + if (string.IsNullOrEmpty(Path)) + return; + + var thumbnailSize = (int)(Constants.ShellIconSizes.Large * App.AppModel.AppWindowDPI); + // Ensure thumbnail size is at least 1 to prevent layout errors + thumbnailSize = Math.Max(1, thumbnailSize); + Item.TryGetThumbnail(thumbnailSize, SIIGBF.SIIGBF_ICONONLY, out var rawThumbnailData); + if (rawThumbnailData is null) + return; - _thumbnailData = result; - if (_thumbnailData is not null) - Thumbnail = await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => _thumbnailData.ToBitmapAsync(), Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal); + Thumbnail = await rawThumbnailData.ToBitmapAsync(); + } + + public void Dispose() + { + Item.Dispose(); } } } diff --git a/src/Files.App/Data/Items/WidgetRecentItem.cs b/src/Files.App/Data/Items/WidgetRecentItem.cs new file mode 100644 index 000000000000..ec26009caefb --- /dev/null +++ b/src/Files.App/Data/Items/WidgetRecentItem.cs @@ -0,0 +1,65 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Media.Imaging; +using Windows.Win32; +using Windows.Win32.UI.Shell; + +namespace Files.App.Data.Items +{ + /// + /// Represents an item for recent item of File Explorer on Windows. + /// + public sealed partial class RecentItem : WidgetCardItem, IEquatable, IDisposable + { + private BitmapImage? _Icon; + /// + /// Gets or sets thumbnail icon of the recent item. + /// + public BitmapImage? Icon + { + get => _Icon; + set => SetProperty(ref _Icon, value); + } + + /// + /// Gets or sets name of the recent item. + /// + public required string Name { get; set; } + + /// + /// Gets or sets target path of the recent item. + /// + public required DateTime LastModified { get; set; } + + /// + /// Gets or initializes PIDL of the recent item. + /// + /// + /// This has to be removed in the future. + /// + public unsafe required ComPtr ShellItem { get; init; } + + /// + /// Loads thumbnail icon of the recent item. + /// + /// + public async Task LoadRecentItemIconAsync() + { + var result = await FileThumbnailHelper.GetIconAsync(Path, Constants.ShellIconSizes.Small, false, IconOptions.UseCurrentScale); + + var bitmapImage = await result.ToBitmapAsync(); + if (bitmapImage is not null) + Icon = bitmapImage; + } + + public override int GetHashCode() => (Path, Name).GetHashCode(); + public override bool Equals(object? other) => other is RecentItem item && Equals(item); + public bool Equals(RecentItem? other) => other is not null && other.Name == Name && other.Path == Path; + + public unsafe void Dispose() + { + ShellItem.Dispose(); + } + } +} diff --git a/src/Files.App/Data/Items/Win32Process.cs b/src/Files.App/Data/Items/Win32Process.cs index cbc5f4873cf6..1f26f03e2bc1 100644 --- a/src/Files.App/Data/Items/Win32Process.cs +++ b/src/Files.App/Data/Items/Win32Process.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Items { diff --git a/src/Files.App/Data/Items/WindowEx.cs b/src/Files.App/Data/Items/WindowEx.cs new file mode 100644 index 000000000000..b00cb0565279 --- /dev/null +++ b/src/Files.App/Data/Items/WindowEx.cs @@ -0,0 +1,290 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Windowing; +using Microsoft.UI.Xaml; +using System.Runtime.InteropServices; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.Storage; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.Graphics.Gdi; +using Windows.Win32.UI.WindowsAndMessaging; + +namespace Files.App.Data.Items +{ + /// + /// Represents base class to extend its features. + /// + public unsafe partial class WindowEx : Window, IDisposable + { + private bool _isInitialized; + private readonly WNDPROC _oldWndProc; + private readonly WNDPROC _newWndProc; + + private readonly ApplicationDataContainer _applicationDataContainer = ApplicationData.Current.LocalSettings; + + /// + /// Gets hWnd of this . + /// + public nint WindowHandle { get; } + + /// + /// Gets min width of this . + /// + public int MinWidth { get; } + + /// + /// Gets min height of this . + /// + public int MinHeight { get; } + + private bool _IsMaximizable = true; + /// + /// Gets or sets a value that indicates whether this can be maximizable. + /// + public bool IsMaximizable + { + get => _IsMaximizable; + set + { + _IsMaximizable = value; + + if (AppWindow.Presenter is OverlappedPresenter overlapped) + overlapped.IsMaximizable = value; + } + } + + private bool _IsMinimizable = true; + /// + /// Gets or sets a value that indicates whether this can be minimizable. + /// + public bool IsMinimizable + { + get => _IsMinimizable; + set + { + _IsMinimizable = value; + + if (AppWindow.Presenter is OverlappedPresenter overlapped) + overlapped.IsMinimizable = value; + } + } + + /// + /// Initializes class. + /// + /// Min width to set when initialized. + /// Min height to set when initialized. + public unsafe WindowEx(int minWidth = 400, int minHeight = 300) + { + WindowHandle = WinRT.Interop.WindowNative.GetWindowHandle(this); + MinWidth = minWidth; + MinHeight = minHeight; + IsMaximizable = true; + IsMinimizable = true; + + _newWndProc = new(NewWindowProc); + var pNewWndProc = Marshal.GetFunctionPointerForDelegate(_newWndProc); + var pOldWndProc = PInvoke.SetWindowLongPtr(new(WindowHandle), WINDOW_LONG_PTR_INDEX.GWL_WNDPROC, pNewWndProc); + _oldWndProc = Marshal.GetDelegateForFunctionPointer(pOldWndProc); + + Closed += WindowEx_Closed; + } + + private unsafe void StoreWindowPlacementData() + { + // Save window placement only for MainWindow + if (!GetType().Name.Equals(nameof(MainWindow), StringComparison.OrdinalIgnoreCase)) + return; + + // Store monitor info + using var data = new SystemIO.MemoryStream(); + using var sw = new SystemIO.BinaryWriter(data); + + var monitors = GetAllMonitorInfo(); + int nMonitors = PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CMONITORS); + sw.Write(nMonitors); + + foreach (var monitor in monitors) + { + sw.Write(monitor.Item1); + sw.Write(monitor.Item2.Left); + sw.Write(monitor.Item2.Top); + sw.Write(monitor.Item2.Right); + sw.Write(monitor.Item2.Bottom); + } + + WINDOWPLACEMENT placement = default; + PInvoke.GetWindowPlacement(new(WindowHandle), ref placement); + + int structSize = Marshal.SizeOf(typeof(WINDOWPLACEMENT)); + IntPtr buffer = Marshal.AllocHGlobal(structSize); + Marshal.StructureToPtr(placement, buffer, false); + byte[] placementData = new byte[structSize]; + Marshal.Copy(buffer, placementData, 0, structSize); + Marshal.FreeHGlobal(buffer); + + sw.Write(placementData); + sw.Flush(); + + var values = GetDataStore(out _, true); + + if (_applicationDataContainer.Containers.ContainsKey("WinUIEx")) + _applicationDataContainer.Values.Remove("WinUIEx"); + + values["MainWindowPlacementData"] = Convert.ToBase64String(data.ToArray()); + } + + private void RestoreWindowPlacementData() + { + // Save window placement only for MainWindow + if (!GetType().Name.Equals(nameof(MainWindow), StringComparison.OrdinalIgnoreCase)) + return; + + var values = GetDataStore(out var oldDataExists, false); + + byte[]? data = null; + if (values.TryGetValue(oldDataExists ? "WindowPersistance_FilesMainWindow" : "MainWindowPlacementData", out object? value)) + { + if (value is string base64) + data = Convert.FromBase64String(base64); + } + + if (data is null) + return; + + SystemIO.BinaryReader br = new(new SystemIO.MemoryStream(data)); + + // Check if monitor layout changed since we stored position + var monitors = GetAllMonitorInfo(); + int monitorCount = br.ReadInt32(); + int nMonitors = PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CMONITORS); + if (monitorCount != nMonitors) + return; + + for (int i = 0; i < monitorCount; i++) + { + var pMonitor = monitors[i]; + br.ReadString(); + if (pMonitor.Item2.Left != br.ReadDouble() || + pMonitor.Item2.Top != br.ReadDouble() || + pMonitor.Item2.Right != br.ReadDouble() || + pMonitor.Item2.Bottom != br.ReadDouble()) + return; + } + + int structSize = Marshal.SizeOf(typeof(WINDOWPLACEMENT)); + byte[] placementData = br.ReadBytes(structSize); + IntPtr buffer = Marshal.AllocHGlobal(structSize); + Marshal.Copy(placementData, 0, buffer, structSize); + var windowPlacementData = (WINDOWPLACEMENT)Marshal.PtrToStructure(buffer, typeof(WINDOWPLACEMENT))!; + + Marshal.FreeHGlobal(buffer); + + // Ignore anything by maximized or normal + if (windowPlacementData.showCmd == (SHOW_WINDOW_CMD)0x0002 /*SW_INVALIDATE*/ && + windowPlacementData.flags == WINDOWPLACEMENT_FLAGS.WPF_RESTORETOMAXIMIZED) + windowPlacementData.showCmd = SHOW_WINDOW_CMD.SW_MAXIMIZE; + else if (windowPlacementData.showCmd != SHOW_WINDOW_CMD.SW_MAXIMIZE) + windowPlacementData.showCmd = SHOW_WINDOW_CMD.SW_NORMAL; + + PInvoke.SetWindowPlacement(new(WindowHandle), in windowPlacementData); + + return; + } + + private IPropertySet GetDataStore(out bool oldDataExists, bool useNewStore = true) + { + IPropertySet values; + oldDataExists = false; + + if (_applicationDataContainer.Containers.TryGetValue("Files", out var dataContainer)) + { + values = dataContainer.Values; + } + else if (!useNewStore && _applicationDataContainer.Containers.TryGetValue("WinUIEx", out var oldDataContainer)) + { + values = oldDataContainer.Values; + oldDataExists = true; + } + else + { + values = _applicationDataContainer.CreateContainer( + "Files", + ApplicationDataCreateDisposition.Always).Values; + } + + return values; + } + + private unsafe List> GetAllMonitorInfo() + { + List> monitors = []; + + MONITORENUMPROC monitorEnumProc = new((HMONITOR monitor, HDC deviceContext, RECT* rect, LPARAM data) => + { + MONITORINFOEXW info = default; + info.monitorInfo.cbSize = (uint)Marshal.SizeOf(); + + PInvoke.GetMonitorInfo(monitor, (MONITORINFO*)&info); + + monitors.Add(new( + info.szDevice.ToString(), + new(new Point(rect->left, rect->top), new Point(rect->right, rect->bottom)))); + + return true; + }); + + var pMonitorEnumProc = Marshal.GetFunctionPointerForDelegate(monitorEnumProc); + var pfnMonitorEnumProc = (delegate* unmanaged[Stdcall])pMonitorEnumProc; + + LPARAM lParam = default; + BOOL fRes = PInvoke.EnumDisplayMonitors(new(nint.Zero), (RECT*)null, pfnMonitorEnumProc, lParam); + if (!fRes) + Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error()); + + return monitors; + } + + private LRESULT NewWindowProc(HWND param0, uint param1, WPARAM param2, LPARAM param3) + { + switch (param1) + { + case 0x0018 /*WM_SHOWWINDOW*/ when param2 == (WPARAM)1 && !_isInitialized: + { + _isInitialized = true; + RestoreWindowPlacementData(); + break; + } + case 0x0024: /*WM_GETMINMAXINFO*/ + { + var dpi = PInvoke.GetDpiForWindow(param0); + float scalingFactor = (float)dpi / 96; + + var minMaxInfo = Marshal.PtrToStructure(param3); + minMaxInfo.ptMinTrackSize.X = (int)(MinWidth * scalingFactor); + minMaxInfo.ptMinTrackSize.Y = (int)(MinHeight * scalingFactor); + Marshal.StructureToPtr(minMaxInfo, param3, true); + break; + } + } + + var pWindProc = Marshal.GetFunctionPointerForDelegate(_oldWndProc); + var pfnWndProc = (delegate* unmanaged[Stdcall])pWindProc; + + return PInvoke.CallWindowProc(pfnWndProc, param0, param1, param2, param3); + } + + private void WindowEx_Closed(object sender, WindowEventArgs args) + { + StoreWindowPlacementData(); + } + + public void Dispose() + { + Closed -= WindowEx_Closed; + } + } +} diff --git a/src/Files.App/Data/Items/WindowsCompatibilityOptions.cs b/src/Files.App/Data/Items/WindowsCompatibilityOptions.cs index 37b884295c0f..eb635ac5f19c 100644 --- a/src/Files.App/Data/Items/WindowsCompatibilityOptions.cs +++ b/src/Files.App/Data/Items/WindowsCompatibilityOptions.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Items { diff --git a/src/Files.App/Data/Items/WslDistroItem.cs b/src/Files.App/Data/Items/WslDistroItem.cs index 06ce36a1d123..4979369dda40 100644 --- a/src/Files.App/Data/Items/WslDistroItem.cs +++ b/src/Files.App/Data/Items/WslDistroItem.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Controls; namespace Files.App.Data.Items { - public sealed class WslDistroItem : ObservableObject, INavigationControlItem + public sealed partial class WslDistroItem : ObservableObject, INavigationControlItem { public string Text { get; set; } @@ -30,7 +30,7 @@ public Uri Icon set { SetProperty(ref icon, value, nameof(Icon)); - OnPropertyChanged(nameof(IconSource)); + OnPropertyChanged(nameof(IconElement)); } } @@ -52,15 +52,21 @@ public object ToolTip public bool IsExpanded { get => false; set { } } - public IconSource? IconSource + public IconElement? IconElement { - get => new BitmapIconSource() + get { - UriSource = icon, - ShowAsMonochrome = false, - }; + var source = new BitmapIconSource() + { + UriSource = icon, + ShowAsMonochrome = false, + }; + return source.CreateIconElement(); + } } - public int CompareTo(INavigationControlItem other) => Text.CompareTo(other.Text); + public bool PaddedItem => false; + + public int CompareTo(INavigationControlItem? other) => Text.CompareTo(other.Text); } } diff --git a/src/Files.App/Data/Messages/FileSystemDialogOptionChangedMessage.cs b/src/Files.App/Data/Messages/FileSystemDialogOptionChangedMessage.cs index c47416af0b5c..218418245c0a 100644 --- a/src/Files.App/Data/Messages/FileSystemDialogOptionChangedMessage.cs +++ b/src/Files.App/Data/Messages/FileSystemDialogOptionChangedMessage.cs @@ -1,8 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using CommunityToolkit.Mvvm.Messaging.Messages; -using Files.App.ViewModels.Dialogs.FileSystemDialog; namespace Files.App.Data.Messages { diff --git a/src/Files.App/Data/Models/AddItemDialogResultModel.cs b/src/Files.App/Data/Models/AddItemDialogResultModel.cs index e22061947d69..721458170cf3 100644 --- a/src/Files.App/Data/Models/AddItemDialogResultModel.cs +++ b/src/Files.App/Data/Models/AddItemDialogResultModel.cs @@ -1,7 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.Shared; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Models { diff --git a/src/Files.App/Data/Models/AppModel.cs b/src/Files.App/Data/Models/AppModel.cs index 82f010556568..9495842f0ae8 100644 --- a/src/Files.App/Data/Models/AppModel.cs +++ b/src/Files.App/Data/Models/AppModel.cs @@ -1,13 +1,15 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Controls; using System.Runtime.InteropServices; using Windows.ApplicationModel.DataTransfer; +using Windows.Win32; +using Windows.Win32.Foundation; namespace Files.App.Data.Models { - public sealed class AppModel : ObservableObject + public sealed partial class AppModel : ObservableObject { public AppModel() { @@ -40,9 +42,8 @@ public int TabStripSelectedIndex { if (value >= 0 && value < MainPageViewModel.AppInstances.Count) { - var rootFrame = (Frame)MainWindow.Instance.Content; - var mainView = (MainPage)rootFrame.Content; - mainView.ViewModel.SelectedTabItem = MainPageViewModel.AppInstances[value]; + if (MainWindow.Instance.Content is Frame rootFrame && rootFrame.Content is MainPage mainView) + mainView.ViewModel.SelectedTabItem = MainPageViewModel.AppInstances[value]; } } catch (COMException) @@ -127,12 +128,19 @@ public string PCloudDrivePath /// /// Gets or sets a value indicating the AppWindow DPI. - /// TODO update value if the DPI changes /// - private float _AppWindowDPI = Win32PInvoke.GetDpiForWindow(MainWindow.Instance.WindowHandle) / 96f; + private float? _AppWindowDPI = null; public float AppWindowDPI { - get => _AppWindowDPI; + get + { + if (_AppWindowDPI is null || _AppWindowDPI == 0f) + { + var dpi = PInvoke.GetDpiForWindow((HWND)MainWindow.Instance.WindowHandle); + _AppWindowDPI = dpi > 0 ? dpi / 96f : 1.0f; // Fallback to 1.0f if invalid DPI + } + return _AppWindowDPI.Value; + } set => SetProperty(ref _AppWindowDPI, value); } } diff --git a/src/Files.App/Data/Models/BitmapImageModel.cs b/src/Files.App/Data/Models/BitmapImageModel.cs index 0e611e209055..791920ddae42 100644 --- a/src/Files.App/Data/Models/BitmapImageModel.cs +++ b/src/Files.App/Data/Models/BitmapImageModel.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.Shared.Utils; using Microsoft.UI.Xaml.Media.Imaging; diff --git a/src/Files.App/Data/Models/BreadcrumbBarItemModel.cs b/src/Files.App/Data/Models/BreadcrumbBarItemModel.cs new file mode 100644 index 000000000000..d8620ca2ba79 --- /dev/null +++ b/src/Files.App/Data/Models/BreadcrumbBarItemModel.cs @@ -0,0 +1,7 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Data.Models +{ + internal record BreadcrumbBarItemModel(string Text); +} diff --git a/src/Files.App/Data/Models/ByteSize.cs b/src/Files.App/Data/Models/ByteSize.cs index b779bbb1fbc5..4367611286d8 100644 --- a/src/Files.App/Data/Models/ByteSize.cs +++ b/src/Files.App/Data/Models/ByteSize.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Collections.Frozen; @@ -7,15 +7,16 @@ namespace Files.App.Data.Models { public struct ByteSize : IEquatable, IComparable { + private static readonly FrozenDictionary units = new Dictionary { - [ByteSizeLib.ByteSize.BitSymbol] = "ByteSymbol".ToLocalized(), - [ByteSizeLib.ByteSize.ByteSymbol] = "ByteSymbol".ToLocalized(), - [ByteSizeLib.ByteSize.KibiByteSymbol] = "KiloByteSymbol".ToLocalized(), - [ByteSizeLib.ByteSize.MebiByteSymbol] = "MegaByteSymbol".ToLocalized(), - [ByteSizeLib.ByteSize.GibiByteSymbol] = "GigaByteSymbol".ToLocalized(), - [ByteSizeLib.ByteSize.TebiByteSymbol] = "TeraByteSymbol".ToLocalized(), - [ByteSizeLib.ByteSize.PebiByteSymbol] = "PetaByteSymbol".ToLocalized(), + [ByteSizeLib.ByteSize.BitSymbol] = Strings.ByteSymbol.ToLocalized(), + [ByteSizeLib.ByteSize.ByteSymbol] = Strings.ByteSymbol.ToLocalized(), + [ByteSizeLib.ByteSize.KiloByteSymbol] = Strings.KiloByteSymbol.ToLocalized(), + [ByteSizeLib.ByteSize.MegaByteSymbol] = Strings.MegaByteSymbol.ToLocalized(), + [ByteSizeLib.ByteSize.GigaByteSymbol] = Strings.GigaByteSymbol.ToLocalized(), + [ByteSizeLib.ByteSize.TeraByteSymbol] = Strings.TeraByteSymbol.ToLocalized(), + [ByteSizeLib.ByteSize.PetaByteSymbol] = Strings.PetaByteSymbol.ToLocalized(), }.ToFrozenDictionary(); private readonly ByteSizeLib.ByteSize size; @@ -28,7 +29,7 @@ public ulong Bytes => (ulong)size.Bytes; public string ShortString - => $"{size.LargestWholeNumberBinaryValue:0.##} {units[size.LargestWholeNumberBinarySymbol]}"; + => $"{size.LargestWholeNumberDecimalValue:0.##} {units[size.LargestWholeNumberDecimalSymbol]}"; public string LongString => $"{ShortString} ({size.Bytes:#,##0} {units[ByteSizeLib.ByteSize.ByteSymbol]})"; diff --git a/src/Files.App/Data/Models/ColumnsViewModel.cs b/src/Files.App/Data/Models/ColumnsViewModel.cs index f8fdda5e70d3..6a2dc1ece173 100644 --- a/src/Files.App/Data/Models/ColumnsViewModel.cs +++ b/src/Files.App/Data/Models/ColumnsViewModel.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; namespace Files.App.Data.Models { - public sealed class ColumnsViewModel : ObservableObject + public sealed partial class ColumnsViewModel : ObservableObject { private DetailsLayoutColumnItem iconColumn = new() { diff --git a/src/Files.App/Data/Models/CompressArchiveModel.cs b/src/Files.App/Data/Models/CompressArchiveModel.cs index cd2a6856e5f2..ff6b9ce6cbf5 100644 --- a/src/Files.App/Data/Models/CompressArchiveModel.cs +++ b/src/Files.App/Data/Models/CompressArchiveModel.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.Utils.Storage.Operations; using Microsoft.Extensions.Logging; @@ -106,17 +106,29 @@ public IProgress Progress /// public ArchiveSplittingSizes SplittingSize { get; init; } + /// + public ArchiveDictionarySizes DictionarySize { get; init; } + + /// + public ArchiveWordSizes WordSize { get; init; } + /// public CancellationToken CancellationToken { get; set; } + /// + public int CPUThreads { get; set; } + public CompressArchiveModel( string[] source, string directory, string fileName, + int cpuThreads, string? password = null, ArchiveFormats fileFormat = ArchiveFormats.Zip, ArchiveCompressionLevels compressionLevel = ArchiveCompressionLevels.Normal, - ArchiveSplittingSizes splittingSize = ArchiveSplittingSizes.None) + ArchiveSplittingSizes splittingSize = ArchiveSplittingSizes.None, + ArchiveDictionarySizes dictionarySize = ArchiveDictionarySizes.Auto, + ArchiveWordSizes wordSize = ArchiveWordSizes.Auto) { _Progress = new Progress(); @@ -128,6 +140,9 @@ public CompressArchiveModel( FileFormat = fileFormat; CompressionLevel = compressionLevel; SplittingSize = splittingSize; + DictionarySize = dictionarySize; + WordSize = wordSize; + CPUThreads = cpuThreads; } /// @@ -152,6 +167,25 @@ public async Task RunCreationAsync() PreserveDirectoryRoot = sources.Length > 1, }; + compressor.CustomParameters.Add("mt", CPUThreads.ToString()); + // Use UTF-8 encoding. + // References: 7-zip chm --> Command Line Version --> Switches + // --> -m --> cu=[off | on]. + // Don't add "cu" parameter for 7zip files, see https://github.com/files-community/Files/issues/17257 + if (FileFormat != ArchiveFormats.SevenZip) + compressor.CustomParameters.Add("cu", "on"); + + if (FileFormat is ArchiveFormats.SevenZip) + { + var dictParam = GetDictionarySizeParam(); + if (dictParam is not null) + compressor.CustomParameters.Add("d", dictParam); + + var wordParam = GetWordSizeParam(); + if (wordParam is not null) + compressor.CustomParameters.Add("fb", wordParam); + } + compressor.Compressing += Compressor_Compressing; compressor.FileCompressionStarted += Compressor_FileCompressionStarted; compressor.FileCompressionFinished += Compressor_FileCompressionFinished; @@ -255,5 +289,37 @@ private void Compressor_Compressing(object? _, ProgressEventArgs e) if (_fileSystemProgress.TotalSize > 0) _fileSystemProgress.Report((_fileSystemProgress.ProcessedSize + e.PercentDelta / 100.0 * e.BytesCount) / _fileSystemProgress.TotalSize * 100); } + + private string? GetDictionarySizeParam() => DictionarySize switch + { + ArchiveDictionarySizes.Auto => null, + ArchiveDictionarySizes.Kb64 => "64k", + ArchiveDictionarySizes.Kb256 => "256k", + ArchiveDictionarySizes.Mb1 => "1m", + ArchiveDictionarySizes.Mb2 => "2m", + ArchiveDictionarySizes.Mb4 => "4m", + ArchiveDictionarySizes.Mb8 => "8m", + ArchiveDictionarySizes.Mb16 => "16m", + ArchiveDictionarySizes.Mb32 => "32m", + ArchiveDictionarySizes.Mb64 => "64m", + ArchiveDictionarySizes.Mb128 => "128m", + ArchiveDictionarySizes.Mb256 => "256m", + ArchiveDictionarySizes.Mb512 => "512m", + ArchiveDictionarySizes.Mb1024 => "1024m", + _ => null, + }; + + private string? GetWordSizeParam() => WordSize switch + { + ArchiveWordSizes.Auto => null, + ArchiveWordSizes.Fb8 => "8", + ArchiveWordSizes.Fb16 => "16", + ArchiveWordSizes.Fb32 => "32", + ArchiveWordSizes.Fb64 => "64", + ArchiveWordSizes.Fb128 => "128", + ArchiveWordSizes.Fb256 => "256", + ArchiveWordSizes.Fb273 => "273", + _ => null, + }; } } diff --git a/src/Files.App/Data/Models/ContextMenuFlyoutItemViewModel.cs b/src/Files.App/Data/Models/ContextMenuFlyoutItemViewModel.cs index 35e888c8fa09..c8884e2eedac 100644 --- a/src/Files.App/Data/Models/ContextMenuFlyoutItemViewModel.cs +++ b/src/Files.App/Data/Models/ContextMenuFlyoutItemViewModel.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Input; using Microsoft.UI.Xaml.Media.Imaging; @@ -57,10 +57,12 @@ public sealed class ContextMenuFlyoutItemViewModel public bool CollapseLabel { get; set; } - public OpacityIconModel OpacityIcon { get; set; } + public ThemedIconModel ThemedIconModel { get; set; } public bool ShowLoadingIndicator { get; set; } public bool IsHidden { get; set; } + + public string AccessKey { get; set; } = string.Empty; } } diff --git a/src/Files.App/Data/Models/ContextMenuFlyoutItemViewModelBuilder.cs b/src/Files.App/Data/Models/ContextMenuFlyoutItemViewModelBuilder.cs index ebeaed3a74c2..16577012d18b 100644 --- a/src/Files.App/Data/Models/ContextMenuFlyoutItemViewModelBuilder.cs +++ b/src/Files.App/Data/Models/ContextMenuFlyoutItemViewModelBuilder.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.System; @@ -23,8 +23,11 @@ public bool IsVisible } public bool IsPrimary { get; init; } = false; + public bool IsToggle { get; init; } = false; + public string AccessKey { get; init; } = string.Empty; + public object Tag { get; init; } public bool ShowOnShift { get; init; } = false; @@ -64,14 +67,15 @@ public ContextMenuFlyoutItemViewModel Build() ShowInSearchPage = true, ShowInFtpPage = true, ShowInZipPage = true, + AccessKey = string.IsNullOrWhiteSpace(AccessKey) ? command.AccessKey : AccessKey, }; var glyph = command.Glyph; - if (!string.IsNullOrEmpty(glyph.OpacityStyle)) + if (!string.IsNullOrEmpty(glyph.ThemedIconStyle)) { - viewModel.OpacityIcon = new OpacityIconModel + viewModel.ThemedIconModel = new ThemedIconModel { - OpacityIconStyle = glyph.OpacityStyle, + ThemedIconStyle = glyph.ThemedIconStyle, }; } else diff --git a/src/Files.App/Data/Models/CurrentInstanceViewModel.cs b/src/Files.App/Data/Models/CurrentInstanceViewModel.cs index a2091b9fccda..ffc6a977f291 100644 --- a/src/Files.App/Data/Models/CurrentInstanceViewModel.cs +++ b/src/Files.App/Data/Models/CurrentInstanceViewModel.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Models { - public sealed class CurrentInstanceViewModel : ObservableObject + public sealed partial class CurrentInstanceViewModel : ObservableObject { // TODO: // In the future, we should consolidate these public variables into @@ -53,6 +53,30 @@ public bool IsPageTypeNotHome } } + private bool isPageTypeReleaseNotes = false; + public bool IsPageTypeReleaseNotes + { + get => isPageTypeReleaseNotes; + set + { + SetProperty(ref isPageTypeReleaseNotes, value); + OnPropertyChanged(nameof(CanCreateFileInPage)); + OnPropertyChanged(nameof(CanCopyPathInPage)); + } + } + + private bool isPageTypeSettings = false; + public bool IsPageTypeSettings + { + get => isPageTypeSettings; + set + { + SetProperty(ref isPageTypeSettings, value); + OnPropertyChanged(nameof(CanCreateFileInPage)); + OnPropertyChanged(nameof(CanCopyPathInPage)); + } + } + private bool isPageTypeMtpDevice = false; public bool IsPageTypeMtpDevice { @@ -124,12 +148,12 @@ public bool IsPageTypeLibrary public bool CanCopyPathInPage { - get => !isPageTypeMtpDevice && !isPageTypeRecycleBin && isPageTypeNotHome && !isPageTypeSearchResults; + get => !isPageTypeMtpDevice && !isPageTypeRecycleBin && isPageTypeNotHome && !isPageTypeSearchResults && !IsPageTypeReleaseNotes && !IsPageTypeSettings; } public bool CanCreateFileInPage { - get => !isPageTypeMtpDevice && !isPageTypeRecycleBin && isPageTypeNotHome && !isPageTypeSearchResults && !isPageTypeFtp && !isPageTypeZipFolder; + get => !isPageTypeMtpDevice && !isPageTypeRecycleBin && isPageTypeNotHome && !isPageTypeSearchResults && !isPageTypeFtp && !isPageTypeZipFolder && !IsPageTypeReleaseNotes && !IsPageTypeSettings; } public bool CanTagFilesInPage diff --git a/src/Files.App/Data/Models/DirectoryPropertiesViewModel.cs b/src/Files.App/Data/Models/DirectoryPropertiesViewModel.cs index aef2177c808a..d6fd99840b3e 100644 --- a/src/Files.App/Data/Models/DirectoryPropertiesViewModel.cs +++ b/src/Files.App/Data/Models/DirectoryPropertiesViewModel.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Windows.Input; namespace Files.App.ViewModels.UserControls { - public sealed class StatusBarViewModel : ObservableObject + public sealed partial class StatusBarViewModel : ObservableObject { private IContentPageContext ContentPageContext { get; } = Ioc.Default.GetRequiredService(); private IDevToolsSettingsService DevToolsSettingsService = Ioc.Default.GetRequiredService(); @@ -41,8 +41,8 @@ public int SelectedBranchIndex get => _SelectedBranchIndex; set { - if (SetProperty(ref _SelectedBranchIndex, value) && - value != -1 && + if (SetProperty(ref _SelectedBranchIndex, value) && + value != -1 && (value != ACTIVE_BRANCH_INDEX || !_ShowLocals) && value < Branches.Count) { @@ -90,8 +90,8 @@ public bool ShowOpenInIDEButton } } - public ObservableCollection Branches => _ShowLocals - ? _localBranches + public ObservableCollection Branches => _ShowLocals + ? _localBranches : _remoteBranches; public EventHandler? CheckoutRequested; @@ -125,7 +125,7 @@ ContentPageContext.ShellPage is not null && : null; _gitRepositoryPath = repositoryPath; - + // Change ShowLocals value only if branches flyout is closed if (!IsBranchesFlyoutExpanded) ShowLocals = true; @@ -133,7 +133,7 @@ ContentPageContext.ShellPage is not null && var behind = head is not null ? head.BehindBy ?? 0 : 0; var ahead = head is not null ? head.AheadBy ?? 0 : 0; - ExtendedStatusInfo = string.Format("GitSyncStatusExtendedInfo".GetLocalizedResource(), ahead, behind); + ExtendedStatusInfo = string.Format(Strings.GitSyncStatusExtendedInfo.GetLocalizedResource(), ahead, behind); StatusInfo = $"{ahead} / {behind}"; OnPropertyChanged(nameof(ShowOpenInIDEButton)); diff --git a/src/Files.App/Data/Models/DisposableArray.cs b/src/Files.App/Data/Models/DisposableArray.cs index 4aee254357f8..be17b2be6c6e 100644 --- a/src/Files.App/Data/Models/DisposableArray.cs +++ b/src/Files.App/Data/Models/DisposableArray.cs @@ -1,11 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.Shared.Extensions; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Models { - public sealed class DisposableArray : FreeableStore + public sealed partial class DisposableArray : FreeableStore { public byte[] Bytes { get; } @@ -19,7 +17,7 @@ public override DisposableArray CreateCopy() return new DisposableArray(Bytes.CloneArray()); } - public override bool Equals(DisposableArray other) + public override bool Equals(DisposableArray? other) { if (other?.Bytes is null || Bytes is null) return false; diff --git a/src/Files.App/Data/Models/DrivesViewModel.cs b/src/Files.App/Data/Models/DrivesViewModel.cs index a2d55c06892c..cf1976d82266 100644 --- a/src/Files.App/Data/Models/DrivesViewModel.cs +++ b/src/Files.App/Data/Models/DrivesViewModel.cs @@ -1,16 +1,15 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.Services.SizeProvider; -using Files.Core.Storage.Storables; using Microsoft.Extensions.Logging; using System.IO; namespace Files.App.Data.Models { - public sealed class DrivesViewModel : ObservableObject, IDisposable + public sealed partial class DrivesViewModel : ObservableObject, IDisposable { - public ObservableCollection Drives + public ObservableCollection Drives { get => drives; private set => SetProperty(ref drives, value); @@ -23,7 +22,7 @@ public bool ShowUserConsentOnInit } private bool showUserConsentOnInit; - private ObservableCollection drives; + private ObservableCollection drives; private readonly IRemovableDrivesService removableDrivesService; private readonly ISizeProvider folderSizeProvider; private readonly IStorageDeviceWatcher watcher; @@ -52,7 +51,7 @@ private async void Watcher_EnumerationCompleted(object? sender, System.EventArgs private async void Watcher_DeviceModified(object? sender, string e) { - var matchingDriveEjected = Drives.FirstOrDefault(x => Path.GetFullPath(x.Path) == Path.GetFullPath(e)); + var matchingDriveEjected = Drives.FirstOrDefault(x => Path.GetFullPath(x.Id) == Path.GetFullPath(e)); if (matchingDriveEjected != null) await removableDrivesService.UpdateDrivePropertiesAsync(matchingDriveEjected); } @@ -62,7 +61,7 @@ private void Watcher_DeviceRemoved(object? sender, string e) logger.LogInformation($"Drive removed: {e}"); lock (Drives) { - var drive = Drives.FirstOrDefault(x => x.Id == e); + var drive = Drives.FirstOrDefault(x => (x as DriveItem)?.DeviceID == e); if (drive is not null) Drives.Remove(drive); } @@ -71,22 +70,22 @@ private void Watcher_DeviceRemoved(object? sender, string e) Watcher_EnumerationCompleted(null, EventArgs.Empty); } - private void Watcher_DeviceAdded(object? sender, ILocatableFolder e) + private void Watcher_DeviceAdded(object? sender, IFolder e) { lock (Drives) { // If drive already in list, remove it first. var matchingDrive = Drives.FirstOrDefault(x => - x.Id == e.Id || - string.IsNullOrEmpty(e.Path) - ? x.Path.Contains(e.Name, StringComparison.OrdinalIgnoreCase) - : Path.GetFullPath(x.Path) == Path.GetFullPath(e.Path) + (x as DriveItem)?.DeviceID == (e as DriveItem)?.DeviceID || + string.IsNullOrEmpty(e.Id) + ? x.Id.Contains(e.Name, StringComparison.OrdinalIgnoreCase) + : Path.GetFullPath(x.Id) == Path.GetFullPath(e.Id) ); if (matchingDrive is not null) Drives.Remove(matchingDrive); - logger.LogInformation($"Drive added: {e.Path}"); + logger.LogInformation($"Drive added: {e.Id}"); Drives.Add(e); } @@ -96,15 +95,18 @@ private void Watcher_DeviceAdded(object? sender, ILocatableFolder e) public async Task UpdateDrivesAsync() { Drives.Clear(); - await foreach (ILocatableFolder item in removableDrivesService.GetDrivesAsync()) + await foreach (IFolder item in removableDrivesService.GetDrivesAsync()) { Drives.AddIfNotPresent(item); } var osDrive = await removableDrivesService.GetPrimaryDriveAsync(); + var osDrivePath = osDrive.Id.EndsWith(Path.DirectorySeparatorChar) + ? osDrive.Id + : $"{osDrive.Id}{Path.DirectorySeparatorChar}"; // Show consent dialog if the OS drive could not be accessed - if (!Drives.Any(x => Path.GetFullPath(x.Path) == Path.GetFullPath(osDrive.Path))) + if (Drives.All(x => Path.GetFullPath(x.Id) != osDrivePath)) ShowUserConsentOnInit = true; if (watcher.CanBeStarted) diff --git a/src/Files.App/Data/Models/FreeableStore.cs b/src/Files.App/Data/Models/FreeableStore.cs index 6fde59cecc75..0f779dfb8a18 100644 --- a/src/Files.App/Data/Models/FreeableStore.cs +++ b/src/Files.App/Data/Models/FreeableStore.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Models { diff --git a/src/Files.App/Data/Models/GitItemModel.cs b/src/Files.App/Data/Models/GitItemModel.cs index a71f4740d3a4..a82cd9d79e38 100644 --- a/src/Files.App/Data/Models/GitItemModel.cs +++ b/src/Files.App/Data/Models/GitItemModel.cs @@ -15,7 +15,7 @@ internal sealed class GitItemModel /// This is often showed as A(Added), D(Deleted), M(Modified), U(Untracked) in VS Code. /// public ChangeKind? Status { get; init; } - + /// /// Gets or initializes file change kind icon /// diff --git a/src/Files.App/Data/Models/HashInfoItem.cs b/src/Files.App/Data/Models/HashInfoItem.cs index 3550295bb072..6a266be32697 100644 --- a/src/Files.App/Data/Models/HashInfoItem.cs +++ b/src/Files.App/Data/Models/HashInfoItem.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Models { - public sealed class HashInfoItem : ObservableObject + public sealed partial class HashInfoItem : ObservableObject { private string _Algorithm; public string Algorithm diff --git a/src/Files.App/Data/Models/IStorageDeviceWatcher.cs b/src/Files.App/Data/Models/IStorageDeviceWatcher.cs index 624006077dc8..626ce8855eb7 100644 --- a/src/Files.App/Data/Models/IStorageDeviceWatcher.cs +++ b/src/Files.App/Data/Models/IStorageDeviceWatcher.cs @@ -1,8 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.Core.Storage.Storables; -using System; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Models { @@ -14,7 +11,7 @@ public interface IStorageDeviceWatcher /// /// Fires when a new device is detected by the storage device watcher /// - event EventHandler DeviceAdded; + event EventHandler DeviceAdded; /// /// Fires when a device removal is detected by the storage device watcher diff --git a/src/Files.App/Data/Models/ItemManipulationModel.cs b/src/Files.App/Data/Models/ItemManipulationModel.cs index ca8500a22bba..670376a761ba 100644 --- a/src/Files.App/Data/Models/ItemManipulationModel.cs +++ b/src/Files.App/Data/Models/ItemManipulationModel.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Models { diff --git a/src/Files.App/Data/Models/ListedTagViewModel.cs b/src/Files.App/Data/Models/ListedTagViewModel.cs index e5cf0f446246..e8390610c8df 100644 --- a/src/Files.App/Data/Models/ListedTagViewModel.cs +++ b/src/Files.App/Data/Models/ListedTagViewModel.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Models { - public sealed class ListedTagViewModel : ObservableObject + public sealed partial class ListedTagViewModel : ObservableObject { private TagViewModel _Tag; public TagViewModel Tag diff --git a/src/Files.App/Data/Models/OmnibarPathModeSuggestionModel.cs b/src/Files.App/Data/Models/OmnibarPathModeSuggestionModel.cs new file mode 100644 index 000000000000..4142d00f0941 --- /dev/null +++ b/src/Files.App/Data/Models/OmnibarPathModeSuggestionModel.cs @@ -0,0 +1,20 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Files.App.Controls; + +namespace Files.App.Data.Models +{ + internal record OmnibarPathModeSuggestionModel(string Path, string DisplayName) : IOmnibarTextMemberPathProvider + { + public string GetTextMemberPath(string textMemberPath) + { + return textMemberPath switch + { + nameof(Path) => Path, + nameof(DisplayName) => DisplayName, + _ => string.Empty + }; + } + } +} diff --git a/src/Files.App/Data/Models/OpacityIconModel.cs b/src/Files.App/Data/Models/OpacityIconModel.cs deleted file mode 100644 index 0c2d79dce66f..000000000000 --- a/src/Files.App/Data/Models/OpacityIconModel.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Microsoft.UI.Xaml; - -namespace Files.App.Data.Models -{ - public struct OpacityIconModel - { - public string OpacityIconStyle { get; set; } - - public readonly OpacityIcon ToOpacityIcon() - { - return new() - { - Style = (Style)Application.Current.Resources[OpacityIconStyle], - }; - } - - public readonly bool IsValid - => !string.IsNullOrEmpty(OpacityIconStyle); - } -} diff --git a/src/Files.App/Data/Models/PinnedFoldersManager.cs b/src/Files.App/Data/Models/PinnedFoldersManager.cs index 7e7c0518b390..33eef2120c63 100644 --- a/src/Files.App/Data/Models/PinnedFoldersManager.cs +++ b/src/Files.App/Data/Models/PinnedFoldersManager.cs @@ -1,9 +1,8 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Collections.Specialized; using System.IO; -using System.Text.Json.Serialization; namespace Files.App.Data.Models { @@ -39,9 +38,15 @@ public async Task UpdateItemsWithExplorerAsync() try { + var formerPinnedFolders = PinnedFolders.ToList(); + PinnedFolders = (await QuickAccessService.GetPinnedFoldersAsync()) .Where(link => (bool?)link.Properties["System.Home.IsPinned"] ?? false) .Select(link => link.FilePath).ToList(); + + if (formerPinnedFolders.SequenceEqual(PinnedFolders)) + return; + RemoveStaleSidebarItems(); await AddAllItemsToSidebarAsync(); } @@ -77,9 +82,9 @@ public async Task CreateLocationItemFromPathAsync(string path) locationItem = LocationItem.Create(); if (path.Equals(Constants.UserEnvironmentPaths.MyComputerPath, StringComparison.OrdinalIgnoreCase)) - locationItem.Text = "ThisPC".GetLocalizedResource(); + locationItem.Text = Strings.ThisPC.GetLocalizedResource(); else if (path.Equals(Constants.UserEnvironmentPaths.NetworkFolderPath, StringComparison.OrdinalIgnoreCase)) - locationItem.Text = "Network".GetLocalizedResource(); + locationItem.Text = Strings.Network.GetLocalizedResource(); } locationItem.Path = path; @@ -93,36 +98,59 @@ public async Task CreateLocationItemFromPathAsync(string path) ShowEmptyRecycleBin = string.Equals(path, Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.OrdinalIgnoreCase) }; locationItem.IsDefaultLocation = false; - locationItem.Text = res.Result?.DisplayName ?? Path.GetFileName(path.TrimEnd('\\')); + locationItem.Text = res?.Result?.DisplayName ?? Path.GetFileName(path.TrimEnd('\\')); if (res) { locationItem.IsInvalid = false; - if (res && res.Result is not null) - { - var result = await FileThumbnailHelper.GetIconAsync( - res.Result.Path, - Constants.ShellIconSizes.Small, - true, - IconOptions.ReturnIconOnly | IconOptions.UseCurrentScale); - - locationItem.IconData = result; - - var bitmapImage = await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => locationItem.IconData.ToBitmapAsync(), Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal); - if (bitmapImage is not null) - locationItem.Icon = bitmapImage; - } + if (res.Result is not null) + await LoadIconForLocationItemAsync(locationItem, res.Result.Path); } else { - locationItem.Icon = await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => UIHelpers.GetSidebarIconResource(Constants.ImageRes.Folder)); locationItem.IsInvalid = true; - Debug.WriteLine($"Pinned item was invalid {res.ErrorCode}, item: {path}"); + Debug.WriteLine($"Pinned item was invalid {res?.ErrorCode}, item: {path}"); + await LoadDefaultIconForLocationItemAsync(locationItem); } return locationItem; } + private async Task LoadIconForLocationItemAsync(LocationItem locationItem, string path) + { + try + { + var result = await FileThumbnailHelper.GetIconAsync( + path, + Constants.ShellIconSizes.Small, + true, + IconOptions.ReturnIconOnly | IconOptions.UseCurrentScale); + + locationItem.IconData = result; + + var bitmapImage = await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => locationItem.IconData.ToBitmapAsync(), Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal); + if (bitmapImage is not null) + locationItem.Icon = bitmapImage; + } + catch (Exception ex) + { + Debug.WriteLine($"Error loading icon for {path}: {ex.Message}"); + } + } + + private async Task LoadDefaultIconForLocationItemAsync(LocationItem locationItem) + { + try + { + var defaultIcon = await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => UIHelpers.GetSidebarIconResource(Constants.ImageRes.Folder)); + locationItem.Icon = defaultIcon; + } + catch (Exception ex) + { + Debug.WriteLine($"Error loading default icon: {ex.Message}"); + } + } + /// /// Adds the item (from a path) to the navigation sidebar /// diff --git a/src/Files.App/Data/Models/RemovableDevice.cs b/src/Files.App/Data/Models/RemovableDevice.cs index cd24e822e5ab..76fcee4b74de 100644 --- a/src/Files.App/Data/Models/RemovableDevice.cs +++ b/src/Files.App/Data/Models/RemovableDevice.cs @@ -1,9 +1,8 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using System; -using System.Diagnostics; -using System.Threading.Tasks; +using Windows.Win32; +using Windows.Win32.Storage.FileSystem; using static Files.App.Helpers.Win32PInvoke; namespace Files.App.Data.Models @@ -20,7 +19,7 @@ public RemovableDevice(string letter) string filename = @"\\.\" + driveLetter + ":"; handle = CreateFileFromAppW(filename, - GENERIC_READ | GENERIC_WRITE, + (uint)(FILE_ACCESS_RIGHTS.FILE_GENERIC_READ | FILE_ACCESS_RIGHTS.FILE_GENERIC_WRITE), FILE_SHARE_READ | FILE_SHARE_WRITE, nint.Zero, OPEN_EXISTING, 0, nint.Zero); } @@ -52,7 +51,7 @@ private async Task LockVolumeAsync() for (int i = 0; i < 5; i++) { - if (DeviceIoControl(handle, FSCTL_LOCK_VOLUME, nint.Zero, 0, nint.Zero, 0, out _, nint.Zero)) + if (DeviceIoControl(handle, PInvoke.FSCTL_LOCK_VOLUME, nint.Zero, 0, nint.Zero, 0, out _, nint.Zero)) { Debug.WriteLine("Lock successful!"); result = true; @@ -72,7 +71,7 @@ private async Task LockVolumeAsync() private bool DismountVolume() { - return DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, nint.Zero, 0, nint.Zero, 0, out _, nint.Zero); + return DeviceIoControl(handle, PInvoke.FSCTL_DISMOUNT_VOLUME, nint.Zero, 0, nint.Zero, 0, out _, nint.Zero); } private bool PreventRemovalOfVolume(bool prevent) diff --git a/src/Files.App/Data/Models/SelectedItemsPropertiesViewModel.cs b/src/Files.App/Data/Models/SelectedItemsPropertiesViewModel.cs index 68850b5f5037..ace192cbf17b 100644 --- a/src/Files.App/Data/Models/SelectedItemsPropertiesViewModel.cs +++ b/src/Files.App/Data/Models/SelectedItemsPropertiesViewModel.cs @@ -1,14 +1,15 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.ViewModels.Properties; using Files.Shared.Helpers; using System.Windows.Input; using TagLib; +using Windows.Win32.UI.WindowsAndMessaging; namespace Files.App.Data.Models { - public sealed class SelectedItemsPropertiesViewModel : ObservableObject + public sealed partial class SelectedItemsPropertiesViewModel : ObservableObject { private static readonly IDateTimeFormatter dateTimeFormatter = Ioc.Default.GetRequiredService(); @@ -183,8 +184,8 @@ public bool IsUncompressedItemSizeVisibile set => SetProperty(ref isUncompressedItemSizeVisibile, value); } - private long itemSizeBytes; - public long ItemSizeBytes + private decimal itemSizeBytes; + public decimal ItemSizeBytes { get => itemSizeBytes; set => SetProperty(ref itemSizeBytes, value); @@ -526,7 +527,6 @@ public bool IsItemSelected public SelectedItemsPropertiesViewModel() { - } private bool isSelectedItemImage = false; @@ -636,6 +636,13 @@ public bool ShortcutItemArgumentsVisibility set => SetProperty(ref shortcutItemArgumentsVisibility, value); } + private bool shortcutItemWindowArgsVisibility = false; + public bool ShortcutItemWindowArgsVisibility + { + get => shortcutItemWindowArgsVisibility; + set => SetProperty(ref shortcutItemWindowArgsVisibility, value); + } + private RelayCommand shortcutItemOpenLinkCommand; public RelayCommand ShortcutItemOpenLinkCommand { @@ -718,6 +725,40 @@ public bool? IsHiddenEditedValue set => SetProperty(ref isHiddenEditedValue, value); } + private bool? isContentCompressed; + /// + /// Applies to NTFS item compression. + /// + public bool? IsContentCompressed + { + get => isContentCompressed; + set + { + SetProperty(ref isContentCompressed, value); + IsContentCompressedEditedValue = value; + } + } + + private bool? isContentCompressedEditedValue; + /// + /// Applies to NTFS item compression. + /// + public bool? IsContentCompressedEditedValue + { + get => isContentCompressedEditedValue; + set => SetProperty(ref isContentCompressedEditedValue, value); + } + + private bool canCompressContent; + /// + /// Applies to NTFS item compression. + /// + public bool CanCompressContent + { + get => canCompressContent; + set => SetProperty(ref canCompressContent, value); + } + private bool runAsAdmin; public bool RunAsAdmin { @@ -748,6 +789,56 @@ public bool RunAsAdminEnabled set => SetProperty(ref runAsAdminEnabled, value); } + private static readonly IReadOnlyDictionary showWindowCommandTypes = new Dictionary + { + { SHOW_WINDOW_CMD.SW_NORMAL, Strings.NormalWindow.GetLocalizedResource() }, + { SHOW_WINDOW_CMD.SW_SHOWMINNOACTIVE, Strings.Minimized.GetLocalizedResource() }, + { SHOW_WINDOW_CMD.SW_MAXIMIZE, Strings.Maximized.GetLocalizedResource() } + }.AsReadOnly(); + + /// + /// The available show window command types. + /// + public IReadOnlyDictionary ShowWindowCommandTypes { get => showWindowCommandTypes; } + + /// + /// The localized string of the currently selected ShowWindowCommand. + /// This value can be used for display in the UI. + /// + public string SelectedShowWindowCommand + { + get => ShowWindowCommandTypes.GetValueOrDefault(ShowWindowCommandEditedValue)!; + set => ShowWindowCommandEditedValue = ShowWindowCommandTypes.First(e => e.Value == value).Key; + } + + private SHOW_WINDOW_CMD showWindowCommand; + /// + /// The current property of the item. + /// + public SHOW_WINDOW_CMD ShowWindowCommand + { + get => showWindowCommand; + set + { + if (SetProperty(ref showWindowCommand, value)) + ShowWindowCommandEditedValue = value; + } + } + + private SHOW_WINDOW_CMD showWindowCommandEditedValue; + /// + /// The edited property of the item. + /// + public SHOW_WINDOW_CMD ShowWindowCommandEditedValue + { + get => showWindowCommandEditedValue; + set + { + if (SetProperty(ref showWindowCommandEditedValue, value)) + OnPropertyChanged(nameof(SelectedShowWindowCommand)); + } + } + private bool isPropertiesLoaded; public bool IsPropertiesLoaded { diff --git a/src/Files.App/Data/Models/SignatureInfoItem.cs b/src/Files.App/Data/Models/SignatureInfoItem.cs new file mode 100644 index 000000000000..6b5e8ffc5022 --- /dev/null +++ b/src/Files.App/Data/Models/SignatureInfoItem.cs @@ -0,0 +1,92 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Files.App.Utils.Signatures; +using System.Windows.Input; +using Windows.Win32.Foundation; + +namespace Files.App.Data.Models +{ + public sealed partial class SignatureInfoItem : ObservableObject + { + private readonly string _fileName; + + private readonly HWND _hwndParent; + + private readonly int _index; + + private string _Version = string.Empty; + public string Version + { + get => _Version; + set => SetProperty(ref _Version, value); + } + + private string _IssuedBy = string.Empty; + public string IssuedBy + { + get => _IssuedBy; + set => SetProperty(ref _IssuedBy, value); + } + + private string _IssuedTo = string.Empty; + public string IssuedTo + { + get => _IssuedTo; + set => SetProperty(ref _IssuedTo, value); + } + + private string _ValidFromTimestamp = string.Empty; + public string ValidFromTimestamp + { + get => _ValidFromTimestamp; + set => SetProperty(ref _ValidFromTimestamp, value); + } + + private string _ValidToTimestamp = string.Empty; + public string ValidToTimestamp + { + get => _ValidToTimestamp; + set => SetProperty(ref _ValidToTimestamp, value); + } + + private string _VerifiedTimestamp = string.Empty; + public string VerifiedTimestamp + { + get => _VerifiedTimestamp; + set => SetProperty(ref _VerifiedTimestamp, value); + } + + private bool _Verified = false; + public bool Verified + { + get => _Verified; + set + { + if (SetProperty(ref _Verified, value)) + OnPropertyChanged(nameof(Glyph)); + } + } + + public List SignChain { get; } + + public string Glyph => Verified ? "\uE930" : "\uEA39"; + + public ICommand OpenDetailsCommand { get; } + + public SignatureInfoItem(string fileName, int index, HWND hWnd, List chain) + { + _fileName = fileName; + _hwndParent = hWnd; + _index = index; + SignChain = chain ?? new List(); + OpenDetailsCommand = new AsyncRelayCommand(DoOpenDetails); + } + + private Task DoOpenDetails() + { + DigitalSignaturesUtil.DisplaySignerInfoDialog(_fileName, _hwndParent, _index); + return Task.CompletedTask; + } + } +} diff --git a/src/Files.App/Data/Models/SuggestionModel.cs b/src/Files.App/Data/Models/SuggestionModel.cs index cdbb2ad0d1cf..df498be22afb 100644 --- a/src/Files.App/Data/Models/SuggestionModel.cs +++ b/src/Files.App/Data/Models/SuggestionModel.cs @@ -1,11 +1,12 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. +using Files.App.Controls; using Microsoft.UI.Xaml.Media.Imaging; namespace Files.App.Data.Models { - public sealed class SuggestionModel : ObservableObject + public sealed partial class SuggestionModel : ObservableObject, IOmnibarTextMemberPathProvider { public bool IsRecentSearch { get; set; } = false; @@ -76,5 +77,10 @@ public SuggestionModel(string itemName, bool isRecentSearch) IsRecentSearch = isRecentSearch; Name = itemName; } + + public string GetTextMemberPath(string textMemberPath) + { + return Name; + } } } diff --git a/src/Files.App/Data/Models/TagViewModel.cs b/src/Files.App/Data/Models/TagViewModel.cs index 6b50a087a1e5..cdcc36a8ed58 100644 --- a/src/Files.App/Data/Models/TagViewModel.cs +++ b/src/Files.App/Data/Models/TagViewModel.cs @@ -1,7 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System.Text.Json.Serialization; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Models { diff --git a/src/Files.App/Data/Models/TaggedItemModel.cs b/src/Files.App/Data/Models/TaggedItemModel.cs index 990d2ee092c7..c362df021d40 100644 --- a/src/Files.App/Data/Models/TaggedItemModel.cs +++ b/src/Files.App/Data/Models/TaggedItemModel.cs @@ -1,7 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.Core.Storage; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Models { diff --git a/src/Files.App/Data/Models/ThemedIconModel.cs b/src/Files.App/Data/Models/ThemedIconModel.cs new file mode 100644 index 000000000000..cabcf2009979 --- /dev/null +++ b/src/Files.App/Data/Models/ThemedIconModel.cs @@ -0,0 +1,24 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Files.App.Controls; +using Microsoft.UI.Xaml; + +namespace Files.App.Data.Models +{ + public struct ThemedIconModel + { + public string ThemedIconStyle { get; set; } + + public readonly ThemedIcon ToThemedIcon() + { + return new() + { + Style = (Style)Application.Current.Resources[ThemedIconStyle], + }; + } + + public readonly bool IsValid + => !string.IsNullOrEmpty(ThemedIconStyle); + } +} diff --git a/src/Files.App/Data/Models/ToolbarHistoryItemModel.cs b/src/Files.App/Data/Models/ToolbarHistoryItemModel.cs new file mode 100644 index 000000000000..f98c59cfa4be --- /dev/null +++ b/src/Files.App/Data/Models/ToolbarHistoryItemModel.cs @@ -0,0 +1,20 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Navigation; + +namespace Files.App.Data.Models +{ + internal sealed class ToolbarHistoryItemModel + { + public PageStackEntry PageStackEntry { get; } + + public bool IsBackMode { get; } + + public ToolbarHistoryItemModel(PageStackEntry pageStackEntry, bool isBackMode) + { + PageStackEntry = pageStackEntry; + IsBackMode = isBackMode; + } + } +} diff --git a/src/Files.App/Data/Models/VolumeInfo.cs b/src/Files.App/Data/Models/VolumeInfo.cs index cace46330bfa..b48266ab555f 100644 --- a/src/Files.App/Data/Models/VolumeInfo.cs +++ b/src/Files.App/Data/Models/VolumeInfo.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Models { diff --git a/src/Files.App/Data/Parameters/ColumnParam.cs b/src/Files.App/Data/Parameters/ColumnParam.cs index 97f719339bdd..2ce6e0e8f55d 100644 --- a/src/Files.App/Data/Parameters/ColumnParam.cs +++ b/src/Files.App/Data/Parameters/ColumnParam.cs @@ -1,8 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.App.Views; -using Files.App.Views.Layouts; using Microsoft.UI.Xaml.Controls; namespace Files.App.Data.Parameters diff --git a/src/Files.App/Data/Parameters/NavigationParams.cs b/src/Files.App/Data/Parameters/NavigationParams.cs index 89dd3d2a1fca..1654542996cd 100644 --- a/src/Files.App/Data/Parameters/NavigationParams.cs +++ b/src/Files.App/Data/Parameters/NavigationParams.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Parameters { diff --git a/src/Files.App/Data/Parameters/PropertiesPageNavigationParameter.cs b/src/Files.App/Data/Parameters/PropertiesPageNavigationParameter.cs index f90a8b77baf8..aff7aa5b5fbc 100644 --- a/src/Files.App/Data/Parameters/PropertiesPageNavigationParameter.cs +++ b/src/Files.App/Data/Parameters/PropertiesPageNavigationParameter.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; diff --git a/src/Files.App/Data/Parameters/SettingsNavigationParams.cs b/src/Files.App/Data/Parameters/SettingsNavigationParams.cs new file mode 100644 index 000000000000..842a748cbda9 --- /dev/null +++ b/src/Files.App/Data/Parameters/SettingsNavigationParams.cs @@ -0,0 +1,10 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Data.Parameters +{ + public sealed class SettingsNavigationParams + { + public SettingsPageKind PageKind { get; set; } + } +} diff --git a/src/Files.App/Data/Parameters/TabBarItemParameter.cs b/src/Files.App/Data/Parameters/TabBarItemParameter.cs index a4aa960ad3b1..8c4c44f6949c 100644 --- a/src/Files.App/Data/Parameters/TabBarItemParameter.cs +++ b/src/Files.App/Data/Parameters/TabBarItemParameter.cs @@ -1,7 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System.Text.Json; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Data.Parameters { diff --git a/src/Files.App/Data/TemplateSelectors/BaseTemplateSelector.cs b/src/Files.App/Data/TemplateSelectors/BaseTemplateSelector.cs index 17602956a6ac..873430bed136 100644 --- a/src/Files.App/Data/TemplateSelectors/BaseTemplateSelector.cs +++ b/src/Files.App/Data/TemplateSelectors/BaseTemplateSelector.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; diff --git a/src/Files.App/Data/TemplateSelectors/FileSystemDialogItemSelector.cs b/src/Files.App/Data/TemplateSelectors/FileSystemDialogItemSelector.cs index c5a794a811ca..6b5b82df50be 100644 --- a/src/Files.App/Data/TemplateSelectors/FileSystemDialogItemSelector.cs +++ b/src/Files.App/Data/TemplateSelectors/FileSystemDialogItemSelector.cs @@ -1,12 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.App.ViewModels.Dialogs.FileSystemDialog; using Microsoft.UI.Xaml; namespace Files.App.Data.TemplateSelectors { - internal sealed class FileSystemDialogItemSelector : BaseTemplateSelector + internal sealed partial class FileSystemDialogItemSelector : BaseTemplateSelector { public DataTemplate? ConflictItemDataTemplate { get; set; } diff --git a/src/Files.App/Data/TemplateSelectors/PathBreadcrumbItemSelector.cs b/src/Files.App/Data/TemplateSelectors/PathBreadcrumbItemSelector.cs deleted file mode 100644 index 2bc950189e96..000000000000 --- a/src/Files.App/Data/TemplateSelectors/PathBreadcrumbItemSelector.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; - -namespace Files.App.Data.TemplateSelectors -{ - /// - /// Provides template selector for Path Breadcrumb template items. - /// - internal sealed class PathBreadcrumbItemSelector : DataTemplateSelector - { - public DataTemplate? ParentItems { get; set; } - - public DataTemplate? CurrentItem { get; set; } - - protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) - { - var itemsControl = ItemsControl.ItemsControlFromItemContainer(container); - - if (itemsControl.ItemsSource is ObservableCollection items) - { - return - itemsControl.IndexFromContainer(container) == items.Count - 1 - ? CurrentItem! - : ParentItems!; - } - else - { - throw new ArgumentException($"Type of {nameof(itemsControl.ItemsSource)} doesn't match ObservableCollection<{nameof(PathBoxItem)}>"); - } - } - } -} diff --git a/src/Files.App/Dialogs/AddBranchDialog.xaml b/src/Files.App/Dialogs/AddBranchDialog.xaml index 88dcaa9db948..921160098dcc 100644 --- a/src/Files.App/Dialogs/AddBranchDialog.xaml +++ b/src/Files.App/Dialogs/AddBranchDialog.xaml @@ -1,4 +1,4 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App/Dialogs/BulkRenameDialog.xaml.cs b/src/Files.App/Dialogs/BulkRenameDialog.xaml.cs new file mode 100644 index 000000000000..ec14cec85dd6 --- /dev/null +++ b/src/Files.App/Dialogs/BulkRenameDialog.xaml.cs @@ -0,0 +1,29 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace Files.App.Dialogs +{ + public sealed partial class BulkRenameDialog : ContentDialog, IDialog + { + private FrameworkElement RootAppElement + => (FrameworkElement)MainWindow.Instance.Content; + + public BulkRenameDialogViewModel ViewModel + { + get => (BulkRenameDialogViewModel)DataContext; + set => DataContext = value; + } + + public BulkRenameDialog() + { + InitializeComponent(); + } + + public new async Task ShowAsync() + { + return (DialogResult)await base.ShowAsync(); + } + } +} diff --git a/src/Files.App/Dialogs/CloneRepoDialog.xaml b/src/Files.App/Dialogs/CloneRepoDialog.xaml new file mode 100644 index 000000000000..e2ac9888b7f9 --- /dev/null +++ b/src/Files.App/Dialogs/CloneRepoDialog.xaml @@ -0,0 +1,30 @@ + + + + + + + + + + diff --git a/src/Files.App/Dialogs/CloneRepoDialog.xaml.cs b/src/Files.App/Dialogs/CloneRepoDialog.xaml.cs new file mode 100644 index 000000000000..e3d7d92f563f --- /dev/null +++ b/src/Files.App/Dialogs/CloneRepoDialog.xaml.cs @@ -0,0 +1,30 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace Files.App.Dialogs +{ + public sealed partial class CloneRepoDialog : ContentDialog, IDialog + { + private FrameworkElement RootAppElement + => (FrameworkElement)MainWindow.Instance.Content; + + public CloneRepoDialogViewModel ViewModel + { + get => (CloneRepoDialogViewModel)DataContext; + set => DataContext = value; + } + + public CloneRepoDialog() + { + InitializeComponent(); + } + + public new async Task ShowAsync() + { + return (DialogResult)await base.ShowAsync(); + } + } +} diff --git a/src/Files.App/Dialogs/CreateArchiveDialog.xaml b/src/Files.App/Dialogs/CreateArchiveDialog.xaml index 2e32f4ecee9c..14afb6b30cee 100644 --- a/src/Files.App/Dialogs/CreateArchiveDialog.xaml +++ b/src/Files.App/Dialogs/CreateArchiveDialog.xaml @@ -1,4 +1,4 @@ - + - + + - - - - - - + + + + + + - + - - - - - + + + + + - + - - - - - - - - - - - + + + + + + + + + + + + + + - - - + + + - - - + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - - - - - - + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App/Dialogs/CreateArchiveDialog.xaml.cs b/src/Files.App/Dialogs/CreateArchiveDialog.xaml.cs index 6715c9d22d14..9ceef7d27625 100644 --- a/src/Files.App/Dialogs/CreateArchiveDialog.xaml.cs +++ b/src/Files.App/Dialogs/CreateArchiveDialog.xaml.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -52,6 +52,24 @@ public ArchiveSplittingSizes SplittingSize set => ViewModel.SplittingSize = ViewModel.SplittingSizes.First(size => size.Key == value); } + public ArchiveDictionarySizes DictionarySize + { + get => ViewModel.DictionarySize.Key; + set => ViewModel.DictionarySize = ViewModel.DictionarySizes.First(size => size.Key == value); + } + + public ArchiveWordSizes WordSize + { + get => ViewModel.WordSize.Key; + set => ViewModel.WordSize = ViewModel.WordSizes.First(size => size.Key == value); + } + + public int CPUThreads + { + get => ViewModel.CPUThreads; + set => ViewModel.CPUThreads = value; + } + private DialogViewModel ViewModel { get; } = new(); public CreateArchiveDialog() @@ -99,8 +117,13 @@ private void ViewModel_PropertyChanged(object? _, PropertyChangedEventArgs e) PasswordBox.Focus(FocusState.Programmatic); } - private sealed class DialogViewModel : ObservableObject + private sealed partial class DialogViewModel : ObservableObject { + private readonly IGeneralSettingsService GeneralSettingsService = Ioc.Default.GetRequiredService(); + + private readonly long _totalMemory; + private readonly long _availableMemory; + public bool IsNameValid => FilesystemHelpers.IsValidForFilename(fileName); public bool ShowNameWarning => !string.IsNullOrEmpty(fileName) && !IsNameValid; @@ -119,31 +142,80 @@ public string FileName } } - private FileFormatItem fileFormat; public FileFormatItem FileFormat { - get => fileFormat; + get => FileFormats.First(format => format.Key == GeneralSettingsService.ArchiveFormatsOption); set { - if (SetProperty(ref fileFormat, value)) + if (value.Key != GeneralSettingsService.ArchiveFormatsOption) + { + GeneralSettingsService.ArchiveFormatsOption = value.Key; OnPropertyChanged(nameof(CanSplit)); + OnPropertyChanged(nameof(EstimatedMemoryText)); + } } } - private CompressionLevelItem compressionLevel; public CompressionLevelItem CompressionLevel { - get => compressionLevel; - set => SetProperty(ref compressionLevel, value); + get => CompressionLevels.First(level => level.Key == GeneralSettingsService.ArchiveCompressionLevelsOption); + set + { + if (value.Key != GeneralSettingsService.ArchiveCompressionLevelsOption) + { + GeneralSettingsService.ArchiveCompressionLevelsOption = value.Key; + OnPropertyChanged(nameof(EstimatedMemoryText)); + } + } } public bool CanSplit => FileFormat.Key is ArchiveFormats.SevenZip; - private SplittingSizeItem splittingSize; public SplittingSizeItem SplittingSize { - get => splittingSize; - set => SetProperty(ref splittingSize, value); + get => SplittingSizes.First(size => size.Key == GeneralSettingsService.ArchiveSplittingSizesOption); + set + { + if (value.Key != GeneralSettingsService.ArchiveSplittingSizesOption) + GeneralSettingsService.ArchiveSplittingSizesOption = value.Key; + } + } + + public DictionarySizeItem DictionarySize + { + get => DictionarySizes.First(size => size.Key == GeneralSettingsService.ArchiveDictionarySizesOption); + set + { + if (value.Key != GeneralSettingsService.ArchiveDictionarySizesOption) + { + GeneralSettingsService.ArchiveDictionarySizesOption = value.Key; + OnPropertyChanged(nameof(EstimatedMemoryText)); + } + } + } + + public WordSizeItem WordSize + { + get => WordSizes.First(size => size.Key == GeneralSettingsService.ArchiveWordSizesOption); + set + { + if (value.Key != GeneralSettingsService.ArchiveWordSizesOption) + { + GeneralSettingsService.ArchiveWordSizesOption = value.Key; + OnPropertyChanged(nameof(EstimatedMemoryText)); + } + } + } + + private int cpuThreads = Environment.ProcessorCount; + public int CPUThreads + { + get => cpuThreads; + set + { + if (SetProperty(ref cpuThreads, value)) + OnPropertyChanged(nameof(EstimatedMemoryText)); + } } private bool useEncryption = false; @@ -176,35 +248,136 @@ public string Password public ImmutableList CompressionLevels { get; } = [ - new CompressionLevelItem(ArchiveCompressionLevels.Ultra, "CompressionLevelUltra".GetLocalizedResource()), - new CompressionLevelItem(ArchiveCompressionLevels.High, "CompressionLevelHigh".GetLocalizedResource()), - new CompressionLevelItem(ArchiveCompressionLevels.Normal, "CompressionLevelNormal".GetLocalizedResource()), - new CompressionLevelItem(ArchiveCompressionLevels.Low, "CompressionLevelLow".GetLocalizedResource()), - new CompressionLevelItem(ArchiveCompressionLevels.Fast, "CompressionLevelFast".GetLocalizedResource()), - new CompressionLevelItem(ArchiveCompressionLevels.None, "CompressionLevelNone".GetLocalizedResource()), + new CompressionLevelItem(ArchiveCompressionLevels.Ultra, Strings.CompressionLevelUltra.GetLocalizedResource()), + new CompressionLevelItem(ArchiveCompressionLevels.High, Strings.CompressionLevelHigh.GetLocalizedResource()), + new CompressionLevelItem(ArchiveCompressionLevels.Normal, Strings.CompressionLevelNormal.GetLocalizedResource()), + new CompressionLevelItem(ArchiveCompressionLevels.Low, Strings.CompressionLevelLow.GetLocalizedResource()), + new CompressionLevelItem(ArchiveCompressionLevels.Fast, Strings.CompressionLevelFast.GetLocalizedResource()), + new CompressionLevelItem(ArchiveCompressionLevels.None, Strings.CompressionLevelNone.GetLocalizedResource()), ]; public ImmutableList SplittingSizes { get; } = [ - new(ArchiveSplittingSizes.None, "Do not split".GetLocalizedResource()), + new(ArchiveSplittingSizes.None, Strings.DoNotSplit.GetLocalizedResource()), new(ArchiveSplittingSizes.Mo10, ToSizeText(10)), new(ArchiveSplittingSizes.Mo100, ToSizeText(100)), - new(ArchiveSplittingSizes.Cd650, ToSizeText(650), "CD".GetLocalizedResource()), - new(ArchiveSplittingSizes.Cd700, ToSizeText(700), "CD".GetLocalizedResource()), + new(ArchiveSplittingSizes.Cd650, ToSizeText(650), Strings.CD.GetLocalizedResource()), + new(ArchiveSplittingSizes.Cd700, ToSizeText(700), Strings.CD.GetLocalizedResource()), new(ArchiveSplittingSizes.Mo1024, ToSizeText(1024)), new(ArchiveSplittingSizes.Mo2048, ToSizeText(2048)), - new(ArchiveSplittingSizes.Fat4092, ToSizeText(4092), "FAT".GetLocalizedResource()), - new(ArchiveSplittingSizes.Dvd4480, ToSizeText(4480), "DVD".GetLocalizedResource()), + new(ArchiveSplittingSizes.Fat4092, ToSizeText(4092), Strings.FAT.GetLocalizedResource()), + new(ArchiveSplittingSizes.Dvd4480, ToSizeText(4480), Strings.DVD.GetLocalizedResource()), new(ArchiveSplittingSizes.Mo5120, ToSizeText(5120)), - new(ArchiveSplittingSizes.Dvd8128, ToSizeText(8128), "DVD".GetLocalizedResource()), - new(ArchiveSplittingSizes.Bd23040, ToSizeText(23040), "Bluray".GetLocalizedResource()), + new(ArchiveSplittingSizes.Dvd8128, ToSizeText(8128), Strings.DVD.GetLocalizedResource()), + new(ArchiveSplittingSizes.Bd23040, ToSizeText(23040), Strings.Bluray.GetLocalizedResource()), + ]; + + public ImmutableList DictionarySizes { get; } = + [ + new(ArchiveDictionarySizes.Auto, Strings.Auto.GetLocalizedResource()), + new(ArchiveDictionarySizes.Kb64, "64 KB"), + new(ArchiveDictionarySizes.Kb256, "256 KB"), + new(ArchiveDictionarySizes.Mb1, "1 MB"), + new(ArchiveDictionarySizes.Mb2, "2 MB"), + new(ArchiveDictionarySizes.Mb4, "4 MB"), + new(ArchiveDictionarySizes.Mb8, "8 MB"), + new(ArchiveDictionarySizes.Mb16, "16 MB"), + new(ArchiveDictionarySizes.Mb32, "32 MB"), + new(ArchiveDictionarySizes.Mb64, "64 MB"), + new(ArchiveDictionarySizes.Mb128, "128 MB"), + new(ArchiveDictionarySizes.Mb256, "256 MB"), + new(ArchiveDictionarySizes.Mb512, "512 MB"), + new(ArchiveDictionarySizes.Mb1024, "1024 MB"), + ]; + + public ImmutableList WordSizes { get; } = + [ + new(ArchiveWordSizes.Auto, Strings.Auto.GetLocalizedResource()), + new(ArchiveWordSizes.Fb8, "8"), + new(ArchiveWordSizes.Fb16, "16"), + new(ArchiveWordSizes.Fb32, "32"), + new(ArchiveWordSizes.Fb64, "64"), + new(ArchiveWordSizes.Fb128, "128"), + new(ArchiveWordSizes.Fb256, "256"), + new(ArchiveWordSizes.Fb273, "273"), ]; public DialogViewModel() { - fileFormat = FileFormats.First(format => format.Key is ArchiveFormats.Zip); - compressionLevel = CompressionLevels.First(level => level.Key is ArchiveCompressionLevels.Normal); - splittingSize = SplittingSizes.First(size => size.Key is ArchiveSplittingSizes.None); + var gcMemoryInfo = GC.GetGCMemoryInfo(); + _totalMemory = gcMemoryInfo.TotalAvailableMemoryBytes; + _availableMemory = _totalMemory - gcMemoryInfo.MemoryLoadBytes; + } + + public string EstimatedMemoryText + { + get + { + long estimated = EstimateCompressionMemory(); + return string.Format( + Strings.EstimatedMemoryUsage.GetLocalizedResource(), + ((double)estimated).ToSizeString()); + } + } + + public string AvailableMemoryText + { + get + { + return string.Format( + Strings.AvailableMemory.GetLocalizedResource(), + ((double)_availableMemory).ToSizeString()); + } + } + + private long EstimateCompressionMemory() + { + long dictBytes = GetEffectiveDictionaryBytes(); + int threads = Math.Max(1, cpuThreads); + + // LZMA2 compression memory estimation: + // Per encoder instance: ~dictSize * 11.5 + 6 MB + // LZMA2 uses ceil(threads / 2) encoder instances + int numEncoders = (threads + 1) / 2; + long perEncoder = (long)(dictBytes * 11.5) + 6 * 1024 * 1024; + return perEncoder * numEncoders; + } + + private long GetEffectiveDictionaryBytes() + { + var dictSizeKey = DictionarySize.Key; + + if (dictSizeKey is ArchiveDictionarySizes.Auto) + { + // Default dictionary sizes for LZMA2 based on compression level + return CompressionLevel.Key switch + { + ArchiveCompressionLevels.Ultra => 64L * 1024 * 1024, + ArchiveCompressionLevels.High => 32L * 1024 * 1024, + ArchiveCompressionLevels.Normal => 16L * 1024 * 1024, + ArchiveCompressionLevels.Low => 1L * 1024 * 1024, + ArchiveCompressionLevels.Fast => 64L * 1024, + _ => 0L + }; + } + + return dictSizeKey switch + { + ArchiveDictionarySizes.Kb64 => 64L * 1024, + ArchiveDictionarySizes.Kb256 => 256L * 1024, + ArchiveDictionarySizes.Mb1 => 1L * 1024 * 1024, + ArchiveDictionarySizes.Mb2 => 2L * 1024 * 1024, + ArchiveDictionarySizes.Mb4 => 4L * 1024 * 1024, + ArchiveDictionarySizes.Mb8 => 8L * 1024 * 1024, + ArchiveDictionarySizes.Mb16 => 16L * 1024 * 1024, + ArchiveDictionarySizes.Mb32 => 32L * 1024 * 1024, + ArchiveDictionarySizes.Mb64 => 64L * 1024 * 1024, + ArchiveDictionarySizes.Mb128 => 128L * 1024 * 1024, + ArchiveDictionarySizes.Mb256 => 256L * 1024 * 1024, + ArchiveDictionarySizes.Mb512 => 512L * 1024 * 1024, + ArchiveDictionarySizes.Mb1024 => 1024L * 1024 * 1024, + _ => 16L * 1024 * 1024 + }; } private static string ToSizeText(ulong megaBytes) => ByteSize.FromMebiBytes(megaBytes).ShortString; @@ -212,6 +385,10 @@ public DialogViewModel() public record FileFormatItem(ArchiveFormats Key, string Label); public record CompressionLevelItem(ArchiveCompressionLevels Key, string Label); + + public record DictionarySizeItem(ArchiveDictionarySizes Key, string Label); + + public record WordSizeItem(ArchiveWordSizes Key, string Label); } } diff --git a/src/Files.App/Dialogs/CreateShortcutDialog.xaml b/src/Files.App/Dialogs/CreateShortcutDialog.xaml index 554f53e5a24b..2df4638729e6 100644 --- a/src/Files.App/Dialogs/CreateShortcutDialog.xaml +++ b/src/Files.App/Dialogs/CreateShortcutDialog.xaml @@ -1,4 +1,4 @@ - + + + @@ -46,12 +48,12 @@ + Text="{x:Bind ViewModel.ShortcutTarget, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"> + + + + + + + + diff --git a/src/Files.App/Dialogs/CreateShortcutDialog.xaml.cs b/src/Files.App/Dialogs/CreateShortcutDialog.xaml.cs index 007337e109bd..086627286d85 100644 --- a/src/Files.App/Dialogs/CreateShortcutDialog.xaml.cs +++ b/src/Files.App/Dialogs/CreateShortcutDialog.xaml.cs @@ -1,8 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.App.ViewModels.Dialogs; -using Files.App.ViewModels.Dialogs; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Data; @@ -27,7 +25,7 @@ public CreateShortcutDialog() InvalidPathWarning.SetBinding(TeachingTip.TargetProperty, new Binding() { - Source = DestinationItemPath + Source = ShortcutTarget }); } diff --git a/src/Files.App/Dialogs/CredentialDialog.xaml b/src/Files.App/Dialogs/CredentialDialog.xaml index 8e0d78521bb7..0bc09d4e24c2 100644 --- a/src/Files.App/Dialogs/CredentialDialog.xaml +++ b/src/Files.App/Dialogs/CredentialDialog.xaml @@ -1,9 +1,9 @@ - + + - + ItemsSource="{x:Bind ViewModel.PreviousExtractionLocations, Mode=OneWay}" + Text="{x:Bind ViewModel.DestinationFolderPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" + TextChanged="DestinationFolderPath_TextChanged" /> - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Files.App/Dialogs/ReleaseNotesDialog.xaml.cs b/src/Files.App/Dialogs/ReleaseNotesDialog.xaml.cs deleted file mode 100644 index d5aae7b549ea..000000000000 --- a/src/Files.App/Dialogs/ReleaseNotesDialog.xaml.cs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Windows.System; - -namespace Files.App.Dialogs -{ - public sealed partial class ReleaseNotesDialog : ContentDialog, IDialog - { - private FrameworkElement RootAppElement - => (FrameworkElement)MainWindow.Instance.Content; - - public ReleaseNotesDialogViewModel ViewModel - { - get => (ReleaseNotesDialogViewModel)DataContext; - set => DataContext = value; - } - - public ReleaseNotesDialog() - { - InitializeComponent(); - - MainWindow.Instance.SizeChanged += Current_SizeChanged; - UpdateDialogLayout(); - } - - private void UpdateDialogLayout() - { - ContainerGrid.MaxHeight = MainWindow.Instance.Bounds.Height - 70; - } - - private void Current_SizeChanged(object sender, WindowSizeChangedEventArgs e) - { - UpdateDialogLayout(); - } - - private void ContentDialog_Closing(ContentDialog sender, ContentDialogClosingEventArgs args) - { - MainWindow.Instance.SizeChanged -= Current_SizeChanged; - } - - public new async Task ShowAsync() - { - return (DialogResult)await SetContentDialogRoot(this).TryShowAsync(); - } - - private void CloseDialogButton_Click(object sender, RoutedEventArgs e) - { - Hide(); - } - - // WINUI3 - private ContentDialog SetContentDialogRoot(ContentDialog contentDialog) - { - if (Windows.Foundation.Metadata.ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) - contentDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; - - return contentDialog; - } - - private async void ReleaseNotesMarkdownTextBlock_LinkClicked(object sender, CommunityToolkit.WinUI.UI.Controls.LinkClickedEventArgs e) - { - if (Uri.TryCreate(e.Link, UriKind.Absolute, out Uri? link)) - await Launcher.LaunchUriAsync(link); - } - } -} diff --git a/src/Files.App/Dialogs/ReorderSidebarItemsDialog.xaml b/src/Files.App/Dialogs/ReorderSidebarItemsDialog.xaml index 3b6f65af8384..5550d40c5ae1 100644 --- a/src/Files.App/Dialogs/ReorderSidebarItemsDialog.xaml +++ b/src/Files.App/Dialogs/ReorderSidebarItemsDialog.xaml @@ -1,4 +1,4 @@ - + + - + @@ -138,7 +138,7 @@ Content="{helpers:ResourceString Name=DevTools}" Tag="DevToolsPage"> - + ((NavigationViewItem)item).IsSelected) as NavigationViewItem; + var targetSection = MainSettingsNavigationView.MenuItems.FirstOrDefault( + item => Enum.Parse(((NavigationViewItem)item).Tag.ToString() ?? defaultTag) == navParams.PageKind + ); + if (oldSelection is not null) + oldSelection.IsSelected = false; + + MainSettingsNavigationView.SelectedItem = targetSection; + } + private void Current_SizeChanged(object sender, WindowSizeChangedEventArgs e) { UpdateDialogLayout(); @@ -40,25 +50,32 @@ private void UpdateDialogLayout() { ContainerGrid.Height = MainWindow.Instance.Bounds.Height <= 760 ? MainWindow.Instance.Bounds.Height - 70 : 690; ContainerGrid.Width = MainWindow.Instance.Bounds.Width <= 1100 ? MainWindow.Instance.Bounds.Width : 1100; - MainSettingsNavigationView.PaneDisplayMode = MainWindow.Instance.Bounds.Width < 700 ? NavigationViewPaneDisplayMode.LeftCompact : NavigationViewPaneDisplayMode.Left; + + var paneDisplayMode = MainWindow.Instance.Bounds.Width < 700 + ? NavigationViewPaneDisplayMode.LeftCompact + : NavigationViewPaneDisplayMode.Left; + + if (MainSettingsNavigationView.PaneDisplayMode != paneDisplayMode) + MainSettingsNavigationView.PaneDisplayMode = paneDisplayMode; } private void MainSettingsNavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args) { + SettingsContentScrollViewer.ChangeView(null, 0, null, true); var selectedItem = (NavigationViewItem)args.SelectedItem; _ = Enum.Parse(selectedItem.Tag.ToString()) switch { - SettingsPageKind.GeneralPage => SettingsContentFrame.Navigate(typeof(GeneralPage)), - SettingsPageKind.AppearancePage => SettingsContentFrame.Navigate(typeof(AppearancePage)), - SettingsPageKind.LayoutPage => SettingsContentFrame.Navigate(typeof(LayoutPage)), - SettingsPageKind.FoldersPage => SettingsContentFrame.Navigate(typeof(FoldersPage)), - SettingsPageKind.ActionsPage => SettingsContentFrame.Navigate(typeof(ActionsPage)), - SettingsPageKind.TagsPage => SettingsContentFrame.Navigate(typeof(TagsPage)), - SettingsPageKind.DevToolsPage => SettingsContentFrame.Navigate(typeof(DevToolsPage)), - SettingsPageKind.AdvancedPage => SettingsContentFrame.Navigate(typeof(AdvancedPage)), - SettingsPageKind.AboutPage => SettingsContentFrame.Navigate(typeof(AboutPage)), - _ => SettingsContentFrame.Navigate(typeof(AppearancePage)) + SettingsPageKind.GeneralPage => SettingsContentFrame.Navigate(typeof(GeneralPage), null, new SuppressNavigationTransitionInfo()), + SettingsPageKind.AppearancePage => SettingsContentFrame.Navigate(typeof(AppearancePage), null, new SuppressNavigationTransitionInfo()), + SettingsPageKind.LayoutPage => SettingsContentFrame.Navigate(typeof(LayoutPage), null, new SuppressNavigationTransitionInfo()), + SettingsPageKind.FoldersPage => SettingsContentFrame.Navigate(typeof(FoldersPage), null, new SuppressNavigationTransitionInfo()), + SettingsPageKind.ActionsPage => SettingsContentFrame.Navigate(typeof(ActionsPage), null, new SuppressNavigationTransitionInfo()), + SettingsPageKind.TagsPage => SettingsContentFrame.Navigate(typeof(TagsPage), null, new SuppressNavigationTransitionInfo()), + SettingsPageKind.DevToolsPage => SettingsContentFrame.Navigate(typeof(DevToolsPage), null, new SuppressNavigationTransitionInfo()), + SettingsPageKind.AdvancedPage => SettingsContentFrame.Navigate(typeof(AdvancedPage), null, new SuppressNavigationTransitionInfo()), + SettingsPageKind.AboutPage => SettingsContentFrame.Navigate(typeof(AboutPage), null, new SuppressNavigationTransitionInfo()), + _ => SettingsContentFrame.Navigate(typeof(AppearancePage), null, new SuppressNavigationTransitionInfo()) }; } diff --git a/src/Files.App/Extensions/ActionManager.cs b/src/Files.App/Extensions/ActionManager.cs new file mode 100644 index 000000000000..8928718ef6c9 --- /dev/null +++ b/src/Files.App/Extensions/ActionManager.cs @@ -0,0 +1,56 @@ +using System.Runtime.InteropServices; +using Windows.AI.Actions; +using Windows.AI.Actions.Hosting; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.System.Com; +using WinRT; + +namespace Files.App.Extensions +{ + internal class ActionManager + { + internal static ActionManager Instance => _instance ??= new(); + + private static ActionManager? _instance; + + // NOTE: This Guid is subject to change in the future + private static readonly Guid IActionRuntimeIID = Guid.Parse("206EFA2C-C909-508A-B4B0-9482BE96DB9C"); + + // Public API usage (ActionCatalog) + private const string ActionRuntimeClsidStr = "C36FEF7E-35F3-4192-9F2C-AF1FD425FB85"; + + internal ActionEntityFactory EntityFactory => ActionRuntime.EntityFactory; + + internal ActionRuntime? ActionRuntime; + internal ActionCatalog ActionCatalog => ActionRuntime.ActionCatalog; + + private ActionManager() + { + ActionRuntime = CreateActionRuntime(); + } + + public static unsafe Windows.AI.Actions.ActionRuntime? CreateActionRuntime() + { + IntPtr abiPtr = default; + try + { + Guid classId = Guid.Parse(ActionRuntimeClsidStr); + Guid iid = IActionRuntimeIID; + + HRESULT hresult = PInvoke.CoCreateInstance(&classId, null, CLSCTX.CLSCTX_LOCAL_SERVER, &iid, (void**)&abiPtr); + Marshal.ThrowExceptionForHR((int)hresult); + + return MarshalInterface.FromAbi(abiPtr); + } + catch + { + return null; + } + finally + { + MarshalInspectable.DisposeAbi(abiPtr); + } + } + } +} \ No newline at end of file diff --git a/src/Files.App/Extensions/EnumExtensions.cs b/src/Files.App/Extensions/EnumExtensions.cs index 76665680f529..ab3759d85da3 100644 --- a/src/Files.App/Extensions/EnumExtensions.cs +++ b/src/Files.App/Extensions/EnumExtensions.cs @@ -1,7 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using System; using System.Reflection; namespace Files.App.Extensions @@ -15,7 +14,7 @@ public static TEnum GetEnum(string text) where TEnum : struct throw new InvalidOperationException("Generic parameter 'TEnum' must be an enum."); } - return (TEnum)Enum.Parse(typeof(TEnum), text); + return Enum.Parse(text); } } } diff --git a/src/Files.App/Extensions/Fractions.cs b/src/Files.App/Extensions/Fractions.cs index c02706ad7165..084049002242 100644 --- a/src/Files.App/Extensions/Fractions.cs +++ b/src/Files.App/Extensions/Fractions.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Extensions { diff --git a/src/Files.App/Extensions/GroupOptionExtensions.cs b/src/Files.App/Extensions/GroupOptionExtensions.cs index 20bb4990bc5c..53c2f731d134 100644 --- a/src/Files.App/Extensions/GroupOptionExtensions.cs +++ b/src/Files.App/Extensions/GroupOptionExtensions.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Extensions { diff --git a/src/Files.App/Extensions/ImageFromBytes.cs b/src/Files.App/Extensions/ImageFromBytes.cs index f679de6ec2a0..1e076dbe77c0 100644 --- a/src/Files.App/Extensions/ImageFromBytes.cs +++ b/src/Files.App/Extensions/ImageFromBytes.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; diff --git a/src/Files.App/Extensions/ImageSourceExtensions.cs b/src/Files.App/Extensions/ImageSourceExtensions.cs index 84841ff5db81..313e1d8dd0c1 100644 --- a/src/Files.App/Extensions/ImageSourceExtensions.cs +++ b/src/Files.App/Extensions/ImageSourceExtensions.cs @@ -1,11 +1,8 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using System; using System.IO; using System.Runtime.InteropServices.WindowsRuntime; -using System.Threading; -using System.Threading.Tasks; using Windows.Storage; using Windows.Storage.Streams; @@ -20,7 +17,7 @@ internal static async Task ToByteArrayAsync(this IInputStream stream) return null; } - using var readStream = stream.AsStreamForRead(); + await using var readStream = stream.AsStreamForRead(); return await readStream.ToByteArrayAsync(); } diff --git a/src/Files.App/Extensions/KeyboardAcceleratorExtensions.cs b/src/Files.App/Extensions/KeyboardAcceleratorExtensions.cs index 18314181f7e4..3447af2ae4cd 100644 --- a/src/Files.App/Extensions/KeyboardAcceleratorExtensions.cs +++ b/src/Files.App/Extensions/KeyboardAcceleratorExtensions.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Input; using Windows.System; diff --git a/src/Files.App/Extensions/LocalizationExtensions.cs b/src/Files.App/Extensions/LocalizationExtensions.cs index 9974d2d513f6..ce7862811e6c 100644 --- a/src/Files.App/Extensions/LocalizationExtensions.cs +++ b/src/Files.App/Extensions/LocalizationExtensions.cs @@ -1,7 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.App.Services; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Extensions { diff --git a/src/Files.App/Extensions/MessageFormatExtensions.cs b/src/Files.App/Extensions/MessageFormatExtensions.cs index 69d0c87daa7a..70e77e59b5c4 100644 --- a/src/Files.App/Extensions/MessageFormatExtensions.cs +++ b/src/Files.App/Extensions/MessageFormatExtensions.cs @@ -1,10 +1,10 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Jeffijoe.MessageFormat; +using Microsoft.Extensions.Logging; using Microsoft.Windows.ApplicationModel.Resources; using System.Globalization; -using Microsoft.Extensions.Logging; namespace Files.App.Extensions { @@ -37,7 +37,7 @@ public static class MessageFormatExtensions if (style is not null && style == string.Empty) { // Format the number '{0, number}' - formatted = string.Format($"{{0:#,##0}}", value); + formatted = $"{value:#,##0}"; return true; } diff --git a/src/Files.App/Extensions/ScrollViewerMiddleClickExtensions.cs b/src/Files.App/Extensions/ScrollViewerMiddleClickExtensions.cs new file mode 100644 index 000000000000..ca4059c0cae8 --- /dev/null +++ b/src/Files.App/Extensions/ScrollViewerMiddleClickExtensions.cs @@ -0,0 +1,412 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using CommunityToolkit.WinUI; +using Files.App.Controls; +using Files.App.Utils; +using Microsoft.UI.Input; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Input; +using System; +using System.Runtime.CompilerServices; +using Windows.Foundation; +using Windows.Storage; +using Windows.System; +using DispatcherQueueTimer = Microsoft.UI.Dispatching.DispatcherQueueTimer; + +namespace Files.App.Extensions +{ + /// + /// Provides middle-click auto-scroll for list controls backed by a . + /// + public sealed class ScrollViewerMiddleClickExtensions : DependencyObject + { + private static readonly ConditionalWeakTable Controllers = new(); + + public static bool GetEnableMiddleClickScrolling(DependencyObject obj) + { + return (bool)obj.GetValue(EnableMiddleClickScrollingProperty); + } + + public static void SetEnableMiddleClickScrolling(DependencyObject obj, bool value) + { + obj.SetValue(EnableMiddleClickScrollingProperty, value); + } + + public static readonly DependencyProperty EnableMiddleClickScrollingProperty = + DependencyProperty.RegisterAttached( + "EnableMiddleClickScrolling", + typeof(bool), + typeof(ScrollViewerMiddleClickExtensions), + new PropertyMetadata(false, OnEnableMiddleClickScrollingChanged)); + + private static void OnEnableMiddleClickScrollingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is not FrameworkElement element) + return; + + var controller = Controllers.GetValue(element, key => new MiddleClickScrollController(key)); + + if ((bool)e.NewValue) + controller.Enable(); + else + controller.Disable(); + } + + private sealed class MiddleClickScrollController + { + private const double DeadZone = 12; + private const double SpeedFactor = 0.12; + private const double MaxSpeedPerTick = 32; + private static readonly InputSystemCursor ScrollCursorAll = InputSystemCursor.Create(InputSystemCursorShape.SizeAll); + private static readonly InputSystemCursor ScrollCursorVertical = InputSystemCursor.Create(InputSystemCursorShape.SizeNorthSouth); + private static readonly InputSystemCursor ScrollCursorHorizontal = InputSystemCursor.Create(InputSystemCursorShape.SizeWestEast); + private static readonly InputSystemCursor DefaultCursor = InputSystemCursor.Create(InputSystemCursorShape.Arrow); + + private readonly FrameworkElement _element; + private readonly DispatcherQueueTimer _scrollTimer; + private readonly PointerEventHandler _rootPointerMovedHandler; + private readonly PointerEventHandler _rootPointerPressedHandler; + private readonly PointerEventHandler _rootPointerReleasedHandler; + private readonly KeyEventHandler _rootKeyDownHandler; + + private UIElement? _rootElement; + private ScrollViewer? _scrollViewer; + + private bool _isEnabled; + private bool _isAutoScrollActive; + private ulong _activationPressTimestamp; + private bool _ignoreActivationMiddleRelease; + private bool _holdScrollDetected; + private UIElement? _lastCursorTarget; + + private Point _anchorPosition; + private Point _currentPosition; + + public MiddleClickScrollController(FrameworkElement element) + { + _element = element; + + _scrollTimer = _element.DispatcherQueue.CreateTimer(); + _scrollTimer.Interval = TimeSpan.FromMilliseconds(16); + _scrollTimer.Tick += ScrollTimer_Tick; + + _rootPointerMovedHandler = new PointerEventHandler(RootElement_PointerMoved); + _rootPointerPressedHandler = new PointerEventHandler(RootElement_PointerPressed); + _rootPointerReleasedHandler = new PointerEventHandler(RootElement_PointerReleased); + _rootKeyDownHandler = new KeyEventHandler(RootElement_KeyDown); + } + + public void Enable() + { + if (_isEnabled) + return; + + _isEnabled = true; + _element.Loaded += Element_Loaded; + _element.Unloaded += Element_Unloaded; + _element.PointerPressed += Element_PointerPressed; + + if (_element.IsLoaded) + AttachToVisualTree(); + } + + public void Disable() + { + if (!_isEnabled) + return; + + _isEnabled = false; + StopAutoScroll(); + DetachFromVisualTree(); + + _element.Loaded -= Element_Loaded; + _element.Unloaded -= Element_Unloaded; + _element.PointerPressed -= Element_PointerPressed; + } + + private void Element_Loaded(object sender, RoutedEventArgs e) + { + AttachToVisualTree(); + } + + private void Element_Unloaded(object sender, RoutedEventArgs e) + { + StopAutoScroll(); + DetachFromVisualTree(); + } + + private void AttachToVisualTree() + { + _scrollViewer = _element.FindDescendant(x => x.Name == "ScrollViewer") + ?? _element.FindDescendant(); + + var root = MainWindow.Instance?.Content as UIElement; + if (root is null || ReferenceEquals(_rootElement, root)) + return; + + if (_rootElement is not null) + DetachRootHandlers(); + + _rootElement = root; + + // Use AddHandler with handledEventsToo: true so we receive pointer events + // even when child controls (list items, scroll bars, etc.) mark them as Handled. + _rootElement.AddHandler(UIElement.PointerMovedEvent, _rootPointerMovedHandler, true); + _rootElement.AddHandler(UIElement.PointerPressedEvent, _rootPointerPressedHandler, true); + _rootElement.AddHandler(UIElement.PointerReleasedEvent, _rootPointerReleasedHandler, true); + _rootElement.AddHandler(UIElement.KeyDownEvent, _rootKeyDownHandler, true); + } + + private void DetachFromVisualTree() + { + _scrollViewer = null; + + if (_rootElement is null) + return; + + DetachRootHandlers(); + _rootElement = null; + } + + private void DetachRootHandlers() + { + _rootElement?.RemoveHandler(UIElement.PointerMovedEvent, _rootPointerMovedHandler); + _rootElement?.RemoveHandler(UIElement.PointerPressedEvent, _rootPointerPressedHandler); + _rootElement?.RemoveHandler(UIElement.PointerReleasedEvent, _rootPointerReleasedHandler); + _rootElement?.RemoveHandler(UIElement.KeyDownEvent, _rootKeyDownHandler); + } + + private void Element_PointerPressed(object sender, PointerRoutedEventArgs e) + { + if (!_isEnabled) + return; + + if (_isAutoScrollActive) + { + StopAutoScroll(); + e.Handled = true; + return; + } + + var point = e.GetCurrentPoint(_rootElement ?? _element); + if (!point.Properties.IsMiddleButtonPressed) + return; + + // Keep middle-click-to-open-folder-in-new-tab behavior unchanged. + if (e.OriginalSource is FrameworkElement { DataContext: ListedItem item } && item.PrimaryItemAttribute == StorageItemTypes.Folder) + return; + + _scrollViewer ??= _element.FindDescendant(x => x.Name == "ScrollViewer") + ?? _element.FindDescendant(); + if (_scrollViewer is null || !CanScroll(_scrollViewer)) + return; + + _anchorPosition = point.Position; + _currentPosition = _anchorPosition; + _isAutoScrollActive = true; + _activationPressTimestamp = point.Timestamp; + _ignoreActivationMiddleRelease = true; + _holdScrollDetected = false; + _scrollTimer.Start(); + ApplyCursor(GetAutoScrollCursor(_scrollViewer), e.OriginalSource as UIElement); + e.Handled = true; + } + + private void RootElement_PointerMoved(object sender, PointerRoutedEventArgs e) + { + if (!_isAutoScrollActive) + return; + + var point = e.GetCurrentPoint(_rootElement ?? _element); + _currentPosition = point.Position; + + if (point.Properties.IsMiddleButtonPressed) + { + var deltaY = _currentPosition.Y - _anchorPosition.Y; + var deltaX = _currentPosition.X - _anchorPosition.X; + if (Math.Abs(deltaY) > DeadZone || Math.Abs(deltaX) > DeadZone) + _holdScrollDetected = true; + } + + ApplyCursor(GetAutoScrollCursor(_scrollViewer), e.OriginalSource as UIElement); + } + + private void RootElement_PointerPressed(object sender, PointerRoutedEventArgs e) + { + if (!_isAutoScrollActive) + return; + + var point = e.GetCurrentPoint(_rootElement ?? _element); + + // Ignore the same click that activated auto-scroll. + if (point.Timestamp == _activationPressTimestamp) + { + return; + } + + if (point.Properties.PointerUpdateKind == PointerUpdateKind.MiddleButtonPressed) + { + StopAutoScroll(); + e.Handled = true; + return; + } + + // Any other pointer press exits auto-scroll mode. + StopAutoScroll(); + e.Handled = true; + } + + private void RootElement_PointerReleased(object sender, PointerRoutedEventArgs e) + { + if (!_isAutoScrollActive) + return; + + var point = e.GetCurrentPoint(_rootElement ?? _element); + if (point.Properties.PointerUpdateKind != PointerUpdateKind.MiddleButtonReleased) + return; + + // Ignore the release from the original activation click. + if (_ignoreActivationMiddleRelease) + { + _ignoreActivationMiddleRelease = false; + + // Hold-to-scroll: stop when the user releases the middle button after dragging. + if (_holdScrollDetected) + { + StopAutoScroll(); + e.Handled = true; + } + + return; + } + + StopAutoScroll(); + e.Handled = true; + } + + private void RootElement_KeyDown(object sender, KeyRoutedEventArgs e) + { + if (_isAutoScrollActive && e.Key == VirtualKey.Escape) + { + StopAutoScroll(); + e.Handled = true; + } + } + + private void ScrollTimer_Tick(DispatcherQueueTimer sender, object args) + { + if (!_isAutoScrollActive) + return; + + _scrollViewer ??= _element.FindDescendant(x => x.Name == "ScrollViewer") + ?? _element.FindDescendant(); + if (_scrollViewer is null) + return; + + var deltaY = _currentPosition.Y - _anchorPosition.Y; + var deltaX = _currentPosition.X - _anchorPosition.X; + + double? newHorizontalOffset = null; + double? newVerticalOffset = null; + + if (_scrollViewer.ScrollableHeight > 0) + { + var velocityY = CalculateVelocity(deltaY); + if (Math.Abs(velocityY) > 0) + { + var targetY = Math.Clamp(_scrollViewer.VerticalOffset + velocityY, 0, _scrollViewer.ScrollableHeight); + if (!targetY.Equals(_scrollViewer.VerticalOffset)) + newVerticalOffset = targetY; + } + } + + if (_scrollViewer.ScrollableWidth > 0) + { + var velocityX = CalculateVelocity(deltaX); + if (Math.Abs(velocityX) > 0) + { + var targetX = Math.Clamp(_scrollViewer.HorizontalOffset + velocityX, 0, _scrollViewer.ScrollableWidth); + if (!targetX.Equals(_scrollViewer.HorizontalOffset)) + newHorizontalOffset = targetX; + } + } + + if (newHorizontalOffset is not null || newVerticalOffset is not null) + _scrollViewer.ChangeView(newHorizontalOffset, newVerticalOffset, null, true); + } + + private void StopAutoScroll() + { + var wasAutoScrollActive = _isAutoScrollActive; + _isAutoScrollActive = false; + _activationPressTimestamp = 0; + _ignoreActivationMiddleRelease = false; + _holdScrollDetected = false; + if (_scrollTimer.IsRunning) + _scrollTimer.Stop(); + + if (wasAutoScrollActive) + ResetCursor(); + } + + private void ApplyCursor(InputCursor cursor, UIElement? cursorTarget = null) + { + _rootElement ??= MainWindow.Instance?.Content as UIElement; + + _rootElement?.ChangeCursor(cursor); + _element.ChangeCursor(cursor); + _scrollViewer?.ChangeCursor(cursor); + + if (!ReferenceEquals(_lastCursorTarget, cursorTarget)) + { + _lastCursorTarget?.ChangeCursor(DefaultCursor); + _lastCursorTarget = cursorTarget; + } + + _lastCursorTarget?.ChangeCursor(cursor); + } + + private void ResetCursor() + { + _rootElement ??= MainWindow.Instance?.Content as UIElement; + + _rootElement?.ChangeCursor(DefaultCursor); + _element.ChangeCursor(DefaultCursor); + _scrollViewer?.ChangeCursor(DefaultCursor); + _lastCursorTarget?.ChangeCursor(DefaultCursor); + _lastCursorTarget = null; + } + + private static bool CanScroll(ScrollViewer scrollViewer) + { + return scrollViewer.ScrollableHeight > 0 || scrollViewer.ScrollableWidth > 0; + } + + private static InputSystemCursor GetAutoScrollCursor(ScrollViewer? sv) + { + if (sv is null) + return ScrollCursorAll; + + bool canV = sv.ScrollableHeight > 0; + bool canH = sv.ScrollableWidth > 0; + + if (canV && canH) + return ScrollCursorAll; + if (canH) + return ScrollCursorHorizontal; + return ScrollCursorVertical; + } + + private static double CalculateVelocity(double delta) + { + if (Math.Abs(delta) <= DeadZone) + return 0; + + var adjustedDelta = delta - (Math.Sign(delta) * DeadZone); + return Math.Clamp(adjustedDelta * SpeedFactor, -MaxSpeedPerTick, MaxSpeedPerTick); + } + } + } +} diff --git a/src/Files.App/Extensions/ShellNewEntryExtensions.cs b/src/Files.App/Extensions/ShellNewEntryExtensions.cs index 3688bac70833..00cedd074d9b 100644 --- a/src/Files.App/Extensions/ShellNewEntryExtensions.cs +++ b/src/Files.App/Extensions/ShellNewEntryExtensions.cs @@ -1,15 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.App.Utils; -using Files.App.Helpers; -using Files.App.Utils.Shell; -using Files.Shared; -using Files.Shared.Extensions; -using System; -using System.Collections.Generic; using System.IO; -using System.Threading.Tasks; using Windows.Storage; namespace Files.App.Extensions diff --git a/src/Files.App/Extensions/StringExtensions.cs b/src/Files.App/Extensions/StringExtensions.cs index 2093ed4dd58b..c1409a2446f5 100644 --- a/src/Files.App/Extensions/StringExtensions.cs +++ b/src/Files.App/Extensions/StringExtensions.cs @@ -1,7 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using ByteSizeLib; using Microsoft.Windows.ApplicationModel.Resources; using System.Collections.Concurrent; using System.IO; @@ -11,6 +10,8 @@ namespace Files.App.Extensions { public static class StringExtensions { + private static IFoldersSettingsService FoldersSettingsService { get; } = Ioc.Default.GetRequiredService(); + /// /// Returns true if starts with the path . /// The comparison is case-insensitive, handles / and \ slashes as folder separators and @@ -60,13 +61,13 @@ public static string WithEnding(this string str, string ending) private static readonly Dictionary abbreviations = new() { - { "KiB", "KiloByteSymbol".GetLocalizedResource() }, - { "MiB", "MegaByteSymbol".GetLocalizedResource() }, - { "GiB", "GigaByteSymbol".GetLocalizedResource() }, - { "TiB", "TeraByteSymbol".GetLocalizedResource() }, - { "PiB", "PetaByteSymbol".GetLocalizedResource() }, - { "B", "ByteSymbol".GetLocalizedResource() }, - { "b", "ByteSymbol".GetLocalizedResource() } + { ByteSize.KiloByteSymbol, Strings.KiloByteSymbol.GetLocalizedResource() }, + { ByteSize.MegaByteSymbol, Strings.MegaByteSymbol.GetLocalizedResource() }, + { ByteSize.GigaByteSymbol, Strings.GigaByteSymbol.GetLocalizedResource() }, + { ByteSize.TeraByteSymbol, Strings.TeraByteSymbol.GetLocalizedResource() }, + { ByteSize.PetaByteSymbol, Strings.PetaByteSymbol.GetLocalizedResource() }, + { ByteSize.BitSymbol, Strings.ByteSymbol.GetLocalizedResource() }, + { ByteSize.ByteSymbol, Strings.ByteSymbol.GetLocalizedResource() } }; public static string ConvertSizeAbbreviation(this string value) @@ -82,11 +83,15 @@ public static string ConvertSizeAbbreviation(this string value) public static string ToSizeString(this double size) => ByteSize.FromBytes(size).ToSizeString(); public static string ToSizeString(this long size) => ByteSize.FromBytes(size).ToSizeString(); public static string ToSizeString(this ulong size) => ByteSize.FromBytes(size).ToSizeString(); - public static string ToSizeString(this ByteSize size) => size.ToBinaryString().ConvertSizeAbbreviation(); + public static string ToSizeString(this decimal size) => ByteSize.FromBytes((double)size).ToSizeString(); + public static string ToSizeString(this ByteSize size) => FoldersSettingsService.SizeUnitFormat is SizeUnitTypes.BinaryUnits + ? size.ToBinaryString().ConvertSizeAbbreviation() + : size.ToString().ConvertSizeAbbreviation(); + public static string ToLongSizeString(this long size) => ByteSize.FromBytes(size).ToLongSizeString(); public static string ToLongSizeString(this ulong size) => ByteSize.FromBytes(size).ToLongSizeString(); - public static string ToLongSizeString(this ByteSize size) => $"{size.ToSizeString()} ({size.Bytes:#,##0} {"ItemSizeBytes".GetLocalizedResource()})"; + public static string ToLongSizeString(this ByteSize size) => $"{size.ToSizeString()} ({size.Bytes:#,##0} {Strings.ItemSizeBytes.GetLocalizedResource()})"; //public static string GetLocalizedResource(this string s) => s.GetLocalized("Resources"); diff --git a/src/Files.App/Extensions/Win32Extensions.cs b/src/Files.App/Extensions/Win32Extensions.cs index b5ef2e49b6c7..7d27ee02c20f 100644 --- a/src/Files.App/Extensions/Win32Extensions.cs +++ b/src/Files.App/Extensions/Win32Extensions.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Extensions { diff --git a/src/Files.App/Extensions/Win32FindDataExtensions.cs b/src/Files.App/Extensions/Win32FindDataExtensions.cs index 73c82b1d9f4f..3971145beac6 100644 --- a/src/Files.App/Extensions/Win32FindDataExtensions.cs +++ b/src/Files.App/Extensions/Win32FindDataExtensions.cs @@ -1,7 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using static Files.App.Helpers.Win32Helper; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Extensions { diff --git a/src/Files.App/Files.App.csproj b/src/Files.App/Files.App.csproj index 6a6155985e4a..a0dfe9e80e8c 100644 --- a/src/Files.App/Files.App.csproj +++ b/src/Files.App/Files.App.csproj @@ -1,14 +1,16 @@ - + - net8.0-windows10.0.22621.0 + true + Properties\PublishProfiles\win-$(Platform).pubxml + $(WindowsTargetFramework) WinExe Files en-US Scale|DXFeatureLevel - Language=en-US;af;ar;be-BY;bg;ca;cs-CZ;da;de-DE;el;en-GB;es-ES;es-419;fa-IR;fi-FI;fil-PH;fr-FR;he-IL;hi-IN;hr-HR;hu-HU;id-ID;it-IT;ja-JP;ka;km-KH;ko-KR;ku-Arab;lt-LT;lv-LV;ms-MY;nb-NO;nl-NL;or-IN;pl-PL;pt-BR;pt-PT;ro-RO;ru-RU;sk-SK;sq-AL;sr-Cyrl;sv-SE;ta;th-TH;tr-TR;uk-UA;vi;zh-Hans;zh-Hant - 10.0.19041.0 + Language=en-US;af;ar;be-BY;bg;ca;cs-CZ;da;de-DE;el;en-GB;es-ES;es-419;fa-IR;fi-FI;fil-PH;fr-FR;he-IL;hi-IN;hr-HR;hu-HU;hy-AM;id-ID;it-IT;ja-JP;ka;km-KH;ko-KR;lt-LT;lv-LV;ms-MY;nb-NO;nl-NL;pl-PL;pt-BR;pt-PT;ro-RO;ru-RU;sk-SK;sq-AL;sr-Cyrl;sv-SE;ta;th-TH;tr-TR;uk-UA;vi;zh-Hans;zh-Hant + $(MinimalWindowsVersion) False SHA256 False @@ -21,23 +23,23 @@ app.manifest x86;x64;arm64 win-x86;win-x64;win-arm64 + false + false true true true - Debug;Release;Stable;Preview;Store + false + Debug;Release Files.App.Server;Microsoft.UI.Content.ContentExternalOutputLink;Microsoft.UI.Content.IContentExternalOutputLink bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\ False True True + Assets\AppTiles\Dev\Logo.ico - - TRACE;DEBUG;NETFX_CORE;DISABLE_XAML_GENERATED_MAIN - - - TRACE;RELEASE;NETFX_CORE;DISABLE_XAML_GENERATED_MAIN - true + + $(DefineConstants);DISABLE_XAML_GENERATED_MAIN @@ -54,48 +56,62 @@ PreserveNewest - + PreserveNewest - + + PreserveNewest - + PreserveNewest - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + PreserveNewest @@ -105,7 +121,9 @@ + + @@ -119,19 +137,19 @@ - - - - - - - - - - - - - - + + + + + + + + + + + diff --git a/src/Files.App/GlobalUsings.cs b/src/Files.App/GlobalUsings.cs index 475f545b00cf..20f7c681ec3e 100644 --- a/src/Files.App/GlobalUsings.cs +++ b/src/Files.App/GlobalUsings.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. // System global using global::System; @@ -29,20 +29,16 @@ global using global::Files.App.Utils.FileTags; global using global::Files.App.Utils.Git; global using global::Files.App.Utils.Library; -global using global::Files.App.Utils.RecentItem; -global using global::Files.App.Utils.RecycleBin; global using global::Files.App.Utils.Serialization; global using global::Files.App.Utils.Shell; global using global::Files.App.Utils.StatusCenter; global using global::Files.App.Utils.Storage; global using global::Files.App.Utils.Taskbar; -global using global::Files.App.Data.Attributes; global using global::Files.App.Data.Behaviors; global using global::Files.App.Data.Commands; global using global::Files.App.Data.Contexts; global using global::Files.App.Data.Contracts; global using global::Files.App.Data.EventArguments; -global using global::Files.App.Data.Exceptions; global using global::Files.App.Data.Factories; global using global::Files.App.Data.Items; global using global::Files.App.Data.Models; @@ -66,19 +62,23 @@ global using global::Files.App.ViewModels.Dialogs; global using global::Files.App.ViewModels.Dialogs.AddItemDialog; global using global::Files.App.ViewModels.Dialogs.FileSystemDialog; -global using global::Files.App.ViewModels.UserControls.Widgets; global using global::Files.App.Utils.CommandLine; // Files.Core.Storage global using global::Files.Core.Storage; -global using global::Files.Core.Storage.Contracts; -global using global::Files.Core.Storage.Storables; global using global::Files.Core.Storage.Enums; global using global::Files.Core.Storage.EventArguments; global using global::Files.Core.Storage.Extensions; -global using global::Files.Core.Storage.StorageEnumeration; +global using global::OwlCore.Storage; + +// Files.App.Storage + +global using global::Files.App.Storage; +global using global::Files.App.Storage.Storables; +global using global::Files.App.Storage.Watchers; // Files.Shared global using global::Files.Shared; +global using global::Files.Shared.Attributes; global using global::Files.Shared.Extensions; diff --git a/src/Files.App/Helpers/Application/AppLanguageHelper.cs b/src/Files.App/Helpers/Application/AppLanguageHelper.cs index 4a70d84fec66..673e69673c5d 100644 --- a/src/Files.App/Helpers/Application/AppLanguageHelper.cs +++ b/src/Files.App/Helpers/Application/AppLanguageHelper.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Globalization; using Windows.Globalization; @@ -27,6 +27,21 @@ public static class AppLanguageHelper /// public static AppLanguageItem PreferredLanguage { get; private set; } + /// + /// Gets the preferred language. + /// + public static bool IsPreferredLanguageRtl + { + get + { + if (PreferredLanguage.Code is null) + return false; + + var culture = new CultureInfo(PreferredLanguage.Code); + return culture.TextInfo.IsRightToLeft; + } + } + /// /// Initializes the class. /// diff --git a/src/Files.App/Helpers/Application/AppLifecycleHelper.cs b/src/Files.App/Helpers/Application/AppLifecycleHelper.cs index 4cfced5b98df..367ec926aa6e 100644 --- a/src/Files.App/Helpers/Application/AppLifecycleHelper.cs +++ b/src/Files.App/Helpers/Application/AppLifecycleHelper.cs @@ -1,15 +1,14 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using CommunityToolkit.WinUI.Helpers; using Files.App.Helpers.Application; using Files.App.Services.SizeProvider; -using Files.App.Storage.Storables; using Files.App.Utils.Logger; using Files.App.ViewModels.Settings; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using Microsoft.Win32; using Sentry; using Sentry.Protocol; using System.IO; @@ -26,19 +25,56 @@ namespace Files.App.Helpers /// public static class AppLifecycleHelper { + private readonly static string AppInformationKey = @$"Software\Files Community\{Package.Current.Id.Name}\v1\AppInformation"; + + /// + /// Gets the value that indicates whether the app is updated. + /// + public static bool IsAppUpdated { get; } + + /// + /// Gets the value that indicates whether the app is running for the first time. + /// + public static bool IsFirstRun { get; } + + /// + /// Gets the value that indicates the total launch count of the app. + /// + public static long TotalLaunchCount { get; } + + /// + /// Gets the value that indicates if the release notes tab was automatically opened. + /// + private static bool ViewedReleaseNotes { get; set; } = false; + + static AppLifecycleHelper() + { + using var infoKey = Registry.CurrentUser.CreateSubKey(AppInformationKey); + var version = infoKey.GetValue("LastLaunchVersion"); + var launchCount = infoKey.GetValue("TotalLaunchCount"); + if (version is null) + { + IsAppUpdated = true; + IsFirstRun = true; + } + else + { + IsAppUpdated = version.ToString() != AppVersion.ToString(); + } + + TotalLaunchCount = long.TryParse(launchCount?.ToString(), out var v) ? v + 1 : 1; + infoKey.SetValue("LastLaunchVersion", AppVersion.ToString()); + infoKey.SetValue("TotalLaunchCount", TotalLaunchCount); + } + /// /// Gets the value that provides application environment or branch name. /// - public static AppEnvironment AppEnvironment { get; } = -#if STORE - AppEnvironment.Store; -#elif PREVIEW - AppEnvironment.Preview; -#elif STABLE - AppEnvironment.Stable; -#else - AppEnvironment.Dev; -#endif + public static AppEnvironment AppEnvironment => + Enum.TryParse("cd_app_env_placeholder", true, out AppEnvironment appEnvironment) + ? appEnvironment + : AppEnvironment.Dev; + /// /// Gets application package version. @@ -53,7 +89,7 @@ public static class AppLifecycleHelper SystemIO.Path.Combine(Package.Current.InstalledLocation.Path, AppEnvironment switch { AppEnvironment.Dev => Constants.AssetPaths.DevLogo, - AppEnvironment.Preview => Constants.AssetPaths.PreviewLogo, + AppEnvironment.SideloadPreview or AppEnvironment.StorePreview => Constants.AssetPaths.PreviewLogo, _ => Constants.AssetPaths.StableLogo }); @@ -69,22 +105,34 @@ public static async Task InitializeAppComponentsAsync() // Start off a list of tasks we need to run before we can continue startup await Task.WhenAll( - OptionalTaskAsync(CloudDrivesManager.UpdateDrivesAsync(), generalSettingsService.ShowCloudDrivesSection), - App.LibraryManager.UpdateLibrariesAsync(), - OptionalTaskAsync(WSLDistroManager.UpdateDrivesAsync(), generalSettingsService.ShowWslSection), - OptionalTaskAsync(App.FileTagsManager.UpdateFileTagsAsync(), generalSettingsService.ShowFileTagsSection), App.QuickAccessManager.InitializeAsync() ); - await Task.WhenAll( - jumpListService.InitializeAsync(), - addItemService.InitializeAsync(), - ContextMenu.WarmUpQueryContextMenuAsync() - ); + // Start non-critical tasks without waiting for them to complete + _ = Task.Run(async () => + { + await Task.WhenAll( + OptionalTaskAsync(CloudDrivesManager.UpdateDrivesAsync(), generalSettingsService.ShowCloudDrivesSection), + App.LibraryManager.UpdateLibrariesAsync(), + OptionalTaskAsync(WSLDistroManager.UpdateDrivesAsync(), generalSettingsService.ShowWslSection), + OptionalTaskAsync(App.FileTagsManager.UpdateFileTagsAsync(), generalSettingsService.ShowFileTagsSection), + jumpListService.InitializeAsync() + ); + + //Start the tasks separately to reduce resource contention + await Task.WhenAll( + addItemService.InitializeAsync(), + ContextMenu.WarmUpQueryContextMenuAsync() + ); + }); FileTagsHelper.UpdateTagsDb(); - await CheckAppUpdate(); + _ = Task.Run(async () => + { + // The follwing method invokes UI thread, so we run it in a separate task + await CheckAppUpdate(); + }); static Task OptionalTaskAsync(Task task, bool condition) { @@ -93,6 +141,8 @@ static Task OptionalTaskAsync(Task task, bool condition) return Task.CompletedTask; } + + generalSettingsService.PropertyChanged += GeneralSettingsService_PropertyChanged; } /// @@ -102,10 +152,26 @@ public static async Task CheckAppUpdate() { var updateService = Ioc.Default.GetRequiredService(); + await updateService.CheckForReleaseNotesAsync(); + + // Check for release notes before checking for new updates + if (AppEnvironment != AppEnvironment.Dev && + IsAppUpdated && + updateService.AreReleaseNotesAvailable && + !ViewedReleaseNotes) + { + await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(async () => + { + await Ioc.Default.GetRequiredService().OpenReleaseNotes.ExecuteAsync(); + ViewedReleaseNotes = true; + }); + } + await updateService.CheckForUpdatesAsync(); await updateService.DownloadMandatoryUpdatesAsync(); - await updateService.CheckAndUpdateFilesLauncherAsync(); - await updateService.CheckLatestReleaseNotesAsync(); + + if (IsAppUpdated) + await updateService.CheckAndUpdateFilesLauncherAsync(); } /// @@ -117,14 +183,11 @@ public static void ConfigureSentry() { options.Dsn = Constants.AutomatedWorkflowInjectionKeys.SentrySecret; options.AutoSessionTracking = true; - options.Release = $"{SystemInformation.Instance.ApplicationVersion.Major}.{SystemInformation.Instance.ApplicationVersion.Minor}.{SystemInformation.Instance.ApplicationVersion.Build}"; + var packageVersion = Package.Current.Id.Version; + options.Release = $"{packageVersion.Major}.{packageVersion.Minor}.{packageVersion.Build}"; options.TracesSampleRate = 0.80; options.ProfilesSampleRate = 0.40; - options.Environment = AppEnvironment == AppEnvironment.Preview ? "preview" : "production"; - options.ExperimentalMetrics = new ExperimentalMetricsOptions - { - EnableCodeLocations = true - }; + options.Environment = AppEnvironment == AppEnvironment.StorePreview || AppEnvironment == AppEnvironment.SideloadPreview ? "preview" : "production"; options.DisableWinUiUnhandledExceptionIntegration(); }); @@ -135,9 +198,13 @@ public static void ConfigureSentry() /// public static IHost ConfigureHost() { - return Host.CreateDefaultBuilder() + var builder = Host.CreateDefaultBuilder() + .UseContentRoot(Package.Current.InstalledLocation.Path) .UseEnvironment(AppLifecycleHelper.AppEnvironment.ToString()) .ConfigureLogging(builder => builder + .ClearProviders() + .AddConsole() + .AddDebug() .AddProvider(new FileLoggerProvider(Path.Combine(ApplicationData.Current.LocalFolder.Path, "debug.log"))) .AddProvider(new SentryLoggerProvider()) .SetMinimumLevel(LogLevel.Information)) @@ -164,7 +231,10 @@ public static IHost ConfigureHost() .AddSingleton() .AddSingleton() // Services + .AddSingleton() .AddSingleton() + .AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() @@ -175,16 +245,9 @@ public static IHost ConfigureHost() .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton() -#if STABLE || PREVIEW - .AddSingleton() -#elif STORE - .AddSingleton() -#else - .AddSingleton() -#endif .AddSingleton() .AddSingleton() .AddSingleton() @@ -192,10 +255,12 @@ public static IHost ConfigureHost() .AddSingleton() .AddSingleton() .AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() @@ -203,8 +268,8 @@ public static IHost ConfigureHost() .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton() .AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton() .AddTransient() @@ -213,14 +278,24 @@ public static IHost ConfigureHost() .AddSingleton() .AddSingleton() .AddSingleton() + .AddSingleton() // Utilities .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton() .AddSingleton() .AddSingleton() - ).Build(); + ); + + // Conditional DI + if (AppEnvironment is AppEnvironment.SideloadPreview or AppEnvironment.SideloadStable) + builder.ConfigureServices(s => s.AddSingleton()); + else if (AppEnvironment is AppEnvironment.StorePreview or AppEnvironment.StoreStable) + builder.ConfigureServices(s => s.AddSingleton()); + else + builder.ConfigureServices(s => s.AddSingleton()); + + return builder.Build(); } /// @@ -238,13 +313,7 @@ public static void SaveSessionTabs() } else { - var defaultArg = new TabBarItemParameter() - { - InitialPageType = typeof(ShellPanesPage), - NavigationParameter = "Home" - }; - - return defaultArg.Serialize(); + return ""; } }) .ToList(); @@ -339,7 +408,7 @@ public static void HandleAppUnhandledException(Exception? ex, bool showToastNoti // Try to re-launch and start over MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(async () => { - await Launcher.LaunchUriAsync(new Uri("files-uwp:")); + await Launcher.LaunchUriAsync(new Uri("files-dev:")); }) .Wait(100); } @@ -347,19 +416,20 @@ public static void HandleAppUnhandledException(Exception? ex, bool showToastNoti } /// - /// Checks if the taskbar is set to auto-hide. + /// Updates the visibility of the system tray icon /// - public static bool IsAutoHideTaskbarEnabled() + private static void GeneralSettingsService_PropertyChanged(object? sender, PropertyChangedEventArgs e) { - const string registryKey = @"Software\Microsoft\Windows\CurrentVersion\Explorer\StuckRects3"; - const string valueName = "Settings"; - - using var key = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(registryKey); - - var value = key?.GetValue(valueName) as byte[]; + if (sender is not IGeneralSettingsService generalSettingsService) + return; - // The least significant bit of the 9th byte controls the auto-hide setting - return value != null && ((value[8] & 0x01) == 1); + if (e.PropertyName == nameof(IGeneralSettingsService.ShowSystemTrayIcon)) + { + if (generalSettingsService.ShowSystemTrayIcon) + App.SystemTrayIcon?.Show(); + else + App.SystemTrayIcon?.Hide(); + } } } } diff --git a/src/Files.App/Helpers/Application/AppToastNotificationHelper.cs b/src/Files.App/Helpers/Application/AppToastNotificationHelper.cs index d3901c64f40b..a73201c093bd 100644 --- a/src/Files.App/Helpers/Application/AppToastNotificationHelper.cs +++ b/src/Files.App/Helpers/Application/AppToastNotificationHelper.cs @@ -1,10 +1,5 @@ using Microsoft.Windows.AppNotifications; using Microsoft.Windows.AppNotifications.Builder; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Files.App.Helpers.Application { @@ -13,10 +8,10 @@ internal static class AppToastNotificationHelper public static void ShowUnhandledExceptionToast() { var toastContent = new AppNotificationBuilder() - .AddText("ExceptionNotificationHeader".GetLocalizedResource()) - .AddText("ExceptionNotificationBody".GetLocalizedResource()) + .AddText(Strings.ExceptionNotificationHeader.GetLocalizedResource()) + .AddText(Strings.ExceptionNotificationBody.GetLocalizedResource()) .SetAppLogoOverride(new Uri("ms-appx:///Assets/error.png")) - .AddButton(new AppNotificationButton("ExceptionNotificationReportButton".GetLocalizedResource()) + .AddButton(new AppNotificationButton(Strings.ExceptionNotificationReportButton.GetLocalizedResource()) .SetInvokeUri(new Uri(Constants.ExternalUrl.BugReportUrl))) .BuildNotification(); AppNotificationManager.Default.Show(toastContent); @@ -25,8 +20,8 @@ public static void ShowUnhandledExceptionToast() public static void ShowBackgroundRunningToast() { var toastContent = new AppNotificationBuilder() - .AddText("BackgroundRunningNotificationHeader".GetLocalizedResource()) - .AddText("BackgroundRunningNotificationBody".GetLocalizedResource()) + .AddText(Strings.BackgroundRunningNotificationHeader.GetLocalizedResource()) + .AddText(Strings.BackgroundRunningNotificationBody.GetLocalizedResource()) .BuildNotification(); AppNotificationManager.Default.Show(toastContent); } @@ -34,8 +29,8 @@ public static void ShowBackgroundRunningToast() public static void ShowDriveEjectToast() { var toastContent = new AppNotificationBuilder() - .AddText("EjectNotificationHeader".GetLocalizedResource()) - .AddText("EjectNotificationBody".GetLocalizedResource()) + .AddText(Strings.EjectNotificationHeader.GetLocalizedResource()) + .AddText(Strings.EjectNotificationBody.GetLocalizedResource()) .SetAttributionText("SettingsAboutAppName".GetLocalizedResource()) .BuildNotification(); AppNotificationManager.Default.Show(toastContent); diff --git a/src/Files.App/Helpers/BitmapHelper.cs b/src/Files.App/Helpers/BitmapHelper.cs index 7089dd5cbe7e..a89301d1ad92 100644 --- a/src/Files.App/Helpers/BitmapHelper.cs +++ b/src/Files.App/Helpers/BitmapHelper.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media.Imaging; @@ -94,9 +94,9 @@ public static async Task RotateAsync(string filePath, BitmapRotation rotation) { var errorDialog = new ContentDialog() { - Title = "FailedToRotateImage".GetLocalizedResource(), + Title = Strings.FailedToRotateImage.GetLocalizedResource(), Content = ex.Message, - PrimaryButtonText = "OK".GetLocalizedResource(), + PrimaryButtonText = Strings.OK.GetLocalizedResource(), }; if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) diff --git a/src/Files.App/Helpers/CollectionDebugView.cs b/src/Files.App/Helpers/CollectionDebugView.cs index 8ce4f0a68138..11a933c85ac9 100644 --- a/src/Files.App/Helpers/CollectionDebugView.cs +++ b/src/Files.App/Helpers/CollectionDebugView.cs @@ -1,9 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System; -using System.Collections.Generic; -using System.Diagnostics; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Helpers { diff --git a/src/Files.App/Helpers/ColorHelpers.cs b/src/Files.App/Helpers/ColorHelpers.cs index 722f4de8c712..a4bd89df29c9 100644 --- a/src/Files.App/Helpers/ColorHelpers.cs +++ b/src/Files.App/Helpers/ColorHelpers.cs @@ -1,8 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using CommunityToolkit.WinUI.Helpers; -using System; using System.Globalization; using Windows.UI; @@ -79,26 +77,9 @@ public static string RandomColor() return color.ToHex(); } - private static string ToHex(this System.Drawing.Color color) + private static string ToHex(this Color color) { return $"#{color.A:X2}{color.R:X2}{color.G:X2}{color.B:X2}"; } - - public static Windows.UI.Color ToWindowsColor(this System.Drawing.Color color) - { - string hex = color.ToHex(); - return FromHex(hex); - } - - public static System.Drawing.Color FromWindowsColor(this Windows.UI.Color color) - { - string hex = color.ToHex(); - - return System.Drawing.Color.FromArgb( - Convert.ToByte(hex.Substring(1, 2), 16), - Convert.ToByte(hex.Substring(3, 2), 16), - Convert.ToByte(hex.Substring(5, 2), 16), - Convert.ToByte(hex.Substring(7, 2), 16)); - } } } diff --git a/src/Files.App/Helpers/CredentialsHelpers.cs b/src/Files.App/Helpers/CredentialsHelpers.cs index 334dd752200c..10d3b3bc1412 100644 --- a/src/Files.App/Helpers/CredentialsHelpers.cs +++ b/src/Files.App/Helpers/CredentialsHelpers.cs @@ -1,5 +1,4 @@ -using System.Runtime.InteropServices; -using Windows.Security.Credentials; +using Windows.Security.Credentials; namespace Files.App.Helpers { diff --git a/src/Files.App/Helpers/Dialog/DialogDisplayHelper.cs b/src/Files.App/Helpers/Dialog/DialogDisplayHelper.cs index 48a8f7e91bfa..db195728ea5c 100644 --- a/src/Files.App/Helpers/Dialog/DialogDisplayHelper.cs +++ b/src/Files.App/Helpers/Dialog/DialogDisplayHelper.cs @@ -1,11 +1,8 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.Dialogs; -using Files.App.ViewModels.Dialogs; using Microsoft.UI.Xaml.Controls; -using System; -using System.Threading.Tasks; namespace Files.App.Helpers { diff --git a/src/Files.App/Helpers/Dialog/DynamicDialogFactory.cs b/src/Files.App/Helpers/Dialog/DynamicDialogFactory.cs index 4c3b6af8c7ec..e13d792a8f20 100644 --- a/src/Files.App/Helpers/Dialog/DynamicDialogFactory.cs +++ b/src/Files.App/Helpers/Dialog/DynamicDialogFactory.cs @@ -1,15 +1,12 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.Dialogs; -using Files.App.ViewModels.Dialogs; using Microsoft.UI; -using Microsoft.UI.Text; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Data; using Microsoft.UI.Xaml.Media; -using Windows.ApplicationModel.DataTransfer; using Windows.System; namespace Files.App.Helpers @@ -22,11 +19,11 @@ public static DynamicDialog GetFor_PropertySaveErrorDialog() { DynamicDialog dialog = new DynamicDialog(new DynamicDialogViewModel() { - TitleText = "PropertySaveErrorDialog/Title".GetLocalizedResource(), - SubtitleText = "PropertySaveErrorMessage/Text".GetLocalizedResource(), // We can use subtitle here as our content - PrimaryButtonText = "Retry".GetLocalizedResource(), - SecondaryButtonText = "PropertySaveErrorDialog/SecondaryButtonText".GetLocalizedResource(), - CloseButtonText = "Cancel".GetLocalizedResource(), + TitleText = Strings.PropertySaveErrorDialog_Title.GetLocalizedResource(), + SubtitleText = Strings.PropertySaveErrorMessage_Text.GetLocalizedResource(), // We can use subtitle here as our content + PrimaryButtonText = Strings.Retry.GetLocalizedResource(), + SecondaryButtonText = Strings.PropertySaveErrorDialog_SecondaryButtonText.GetLocalizedResource(), + CloseButtonText = Strings.Cancel.GetLocalizedResource(), DynamicButtons = DynamicDialogButtons.Primary | DynamicDialogButtons.Secondary | DynamicDialogButtons.Cancel }); return dialog; @@ -36,9 +33,9 @@ public static DynamicDialog GetFor_ConsentDialog() { DynamicDialog dialog = new DynamicDialog(new DynamicDialogViewModel() { - TitleText = "WelcomeDialog/Title".GetLocalizedResource(), - SubtitleText = "WelcomeDialogTextBlock/Text".GetLocalizedResource(), // We can use subtitle here as our content - PrimaryButtonText = "WelcomeDialog/PrimaryButtonText".GetLocalizedResource(), + TitleText = Strings.WelcomeDialog_Title.GetLocalizedResource(), + SubtitleText = Strings.WelcomeDialogTextBlock_Text.GetLocalizedResource(), // We can use subtitle here as our content + PrimaryButtonText = Strings.WelcomeDialog_PrimaryButtonText.GetLocalizedResource(), PrimaryButtonAction = async (vm, e) => await Launcher.LaunchUriAsync(new Uri("ms-settings:privacy-broadfilesystemaccess")), DynamicButtons = DynamicDialogButtons.Primary }); @@ -49,28 +46,28 @@ public static DynamicDialog GetFor_ShortcutNotFound(string targetPath) { DynamicDialog dialog = new(new DynamicDialogViewModel { - TitleText = "ShortcutCannotBeOpened".GetLocalizedResource(), - SubtitleText = string.Format("DeleteShortcutDescription".GetLocalizedResource(), targetPath), - PrimaryButtonText = "Delete".GetLocalizedResource(), - SecondaryButtonText = "No".GetLocalizedResource(), + TitleText = Strings.ShortcutCannotBeOpened.GetLocalizedResource(), + SubtitleText = string.Format(Strings.DeleteShortcutDescription.GetLocalizedResource(), targetPath), + PrimaryButtonText = Strings.Delete.GetLocalizedResource(), + SecondaryButtonText = Strings.No.GetLocalizedResource(), DynamicButtons = DynamicDialogButtons.Primary | DynamicDialogButtons.Secondary }); return dialog; } - public static DynamicDialog GetFor_RenameDialog() + public static DynamicDialog GetFor_CreateItemDialog(string itemType, string? itemName) { DynamicDialog? dialog = null; TextBox inputText = new() { - PlaceholderText = "EnterAnItemName".GetLocalizedResource() + PlaceholderText = Strings.EnterAnItemName.GetLocalizedResource() }; TeachingTip warning = new() { - Title = "InvalidFilename/Text".GetLocalizedResource(), + Title = Strings.InvalidFilename_Text.GetLocalizedResource(), PreferredPlacement = TeachingTipPlacementMode.Bottom, - DataContext = new RenameDialogViewModel(), + DataContext = new CreateItemDialogViewModel(), }; warning.SetBinding(TeachingTip.TargetProperty, new Binding() @@ -88,7 +85,7 @@ public static DynamicDialog GetFor_RenameDialog() inputText.TextChanged += (textBox, args) => { var isInputValid = FilesystemHelpers.IsValidForFilename(inputText.Text); - ((RenameDialogViewModel)warning.DataContext).IsNameInvalid = !string.IsNullOrEmpty(inputText.Text) && !isInputValid; + ((CreateItemDialogViewModel)warning.DataContext).IsNameInvalid = !string.IsNullOrEmpty(inputText.Text) && !isInputValid; dialog!.ViewModel.DynamicButtonsEnabled = isInputValid ? DynamicDialogButtons.Primary | DynamicDialogButtons.Cancel : DynamicDialogButtons.Cancel; @@ -98,14 +95,24 @@ public static DynamicDialog GetFor_RenameDialog() inputText.Loaded += (s, e) => { - // dispatching to the ui thread fixes an issue where the primary dialog button would steal focus - _ = inputText.DispatcherQueue.EnqueueOrInvokeAsync(() => inputText.Focus(FocusState.Programmatic)); + // Dispatching to the UI thread fixes an issue where the primary dialog button would steal focus + _ = inputText.DispatcherQueue.EnqueueOrInvokeAsync(() => + { + // Prefill text box with default name #17845 + if (itemType.Equals("Folder", StringComparison.OrdinalIgnoreCase)) + inputText.Text = Strings.NewFolder.GetLocalizedResource(); + else if (itemName is not null) + inputText.Text = string.Format(Strings.CreateNewFile.GetLocalizedResource(), itemName); + + inputText.Focus(FocusState.Programmatic); + inputText.SelectAll(); + }); }; dialog = new DynamicDialog(new DynamicDialogViewModel() { - TitleText = "EnterAnItemName".GetLocalizedResource(), - SubtitleText = null, + TitleText = string.Format(Strings.CreateNewItemTitle.GetLocalizedResource(), itemType), + SubtitleText = Strings.EnterAnItemName.GetLocalizedResource(), DisplayControl = new Grid() { MinWidth = 300d, @@ -118,8 +125,8 @@ public static DynamicDialog GetFor_RenameDialog() { vm.HideDialog(); // Rename successful }, - PrimaryButtonText = "RenameDialog/PrimaryButtonText".GetLocalizedResource(), - CloseButtonText = "Cancel".GetLocalizedResource(), + PrimaryButtonText = Strings.Create.GetLocalizedResource(), + CloseButtonText = Strings.Cancel.GetLocalizedResource(), DynamicButtonsEnabled = DynamicDialogButtons.Cancel, DynamicButtons = DynamicDialogButtons.Primary | DynamicDialogButtons.Cancel }); @@ -136,9 +143,9 @@ public static DynamicDialog GetFor_FileInUseDialog(List lockingPro { DynamicDialog dialog = new DynamicDialog(new DynamicDialogViewModel() { - TitleText = "FileInUseDialog/Title".GetLocalizedResource(), - SubtitleText = lockingProcess.IsEmpty() ? "FileInUseDialog/Text".GetLocalizedResource() : - string.Format("FileInUseByDialog/Text".GetLocalizedResource(), string.Join(", ", lockingProcess.Select(x => $"{x.AppName ?? x.Name} (PID: {x.Pid})"))), + TitleText = Strings.FileInUseDialog_Title.GetLocalizedResource(), + SubtitleText = lockingProcess.IsEmpty() ? Strings.FileInUseDialog_Text.GetLocalizedResource() : + string.Format(Strings.FileInUseByDialog_Text.GetLocalizedResource(), string.Join(", ", lockingProcess.Select(x => $"{x.AppName ?? x.Name} (PID: {x.Pid})"))), PrimaryButtonText = "OK", DynamicButtons = DynamicDialogButtons.Primary }); @@ -152,17 +159,17 @@ public static DynamicDialog GetFor_CredentialEntryDialog(string path) TextBox inputUsername = new() { - PlaceholderText = "CredentialDialogUserName/PlaceholderText".GetLocalizedResource() + PlaceholderText = Strings.CredentialDialogUserName_PlaceholderText.GetLocalizedResource() }; PasswordBox inputPassword = new() { - PlaceholderText = "Password".GetLocalizedResource() + PlaceholderText = Strings.Password.GetLocalizedResource() }; CheckBox saveCreds = new() { - Content = "NetworkAuthenticationSaveCheckbox".GetLocalizedResource() + Content = Strings.NetworkAuthenticationSaveCheckbox.GetLocalizedResource() }; inputUsername.TextChanged += (textBox, args) => @@ -191,10 +198,10 @@ public static DynamicDialog GetFor_CredentialEntryDialog(string path) dialog = new DynamicDialog(new DynamicDialogViewModel() { - TitleText = "NetworkAuthenticationDialogTitle".GetLocalizedResource(), - PrimaryButtonText = "OK".GetLocalizedResource(), - CloseButtonText = "Cancel".GetLocalizedResource(), - SubtitleText = string.Format("NetworkAuthenticationDialogMessage".GetLocalizedResource(), path.Substring(2)), + TitleText = Strings.NetworkAuthenticationDialogTitle.GetLocalizedResource(), + PrimaryButtonText = Strings.OK.GetLocalizedResource(), + CloseButtonText = Strings.Cancel.GetLocalizedResource(), + SubtitleText = string.Format(Strings.NetworkAuthenticationDialogMessage.GetLocalizedResource(), path.Substring(2)), DisplayControl = new Grid() { MinWidth = 250d, @@ -231,9 +238,9 @@ public static DynamicDialog GetFor_GitCheckoutConflicts(string checkoutBranchNam { ItemsSource = new string[] { - string.Format("BringChanges".GetLocalizedResource(), checkoutBranchName), - string.Format("StashChanges".GetLocalizedResource(), headBranchName), - "DiscardChanges".GetLocalizedResource() + string.Format(Strings.BringChanges.GetLocalizedResource(), checkoutBranchName), + string.Format(Strings.StashChanges.GetLocalizedResource(), headBranchName), + Strings.DiscardChanges.GetLocalizedResource() }, SelectionMode = ListViewSelectionMode.Single }; @@ -246,10 +253,10 @@ public static DynamicDialog GetFor_GitCheckoutConflicts(string checkoutBranchNam dialog = new DynamicDialog(new DynamicDialogViewModel() { - TitleText = "SwitchBranch".GetLocalizedResource(), - PrimaryButtonText = "Switch".GetLocalizedResource(), - CloseButtonText = "Cancel".GetLocalizedResource(), - SubtitleText = "UncommittedChanges".GetLocalizedResource(), + TitleText = Strings.SwitchBranch.GetLocalizedResource(), + PrimaryButtonText = Strings.Switch.GetLocalizedResource(), + CloseButtonText = Strings.Cancel.GetLocalizedResource(), + SubtitleText = Strings.UncommittedChanges.GetLocalizedResource(), DisplayControl = new Grid() { MinWidth = 250d, @@ -269,13 +276,60 @@ public static DynamicDialog GetFor_GitCheckoutConflicts(string checkoutBranchNam return dialog; } + public static DynamicDialog GetFor_GitMergeConflicts(string checkoutBranchName, string headBranchName) + { + DynamicDialog dialog = null!; + + var optionsListView = new ListView() + { + ItemsSource = new string[] + { + string.Format(Strings.AbortMergeAndSwitch.GetLocalizedResource(), checkoutBranchName), + string.Format(Strings.StayAndResolveConflicts.GetLocalizedResource(), headBranchName) + }, + SelectionMode = ListViewSelectionMode.Single + }; + optionsListView.SelectedIndex = 0; + + optionsListView.SelectionChanged += (listView, args) => + { + dialog.ViewModel.AdditionalData = optionsListView.SelectedIndex == 0 + ? GitCheckoutOptions.AbortMerge + : GitCheckoutOptions.None; + }; + + dialog = new DynamicDialog(new DynamicDialogViewModel() + { + TitleText = Strings.SwitchBranch.GetLocalizedResource(), + PrimaryButtonText = Strings.Confirm.GetLocalizedResource(), + CloseButtonText = Strings.Cancel.GetLocalizedResource(), + SubtitleText = Strings.MergeInProgress.GetLocalizedResource(), + DisplayControl = new Grid() + { + MinWidth = 250d, + Children = + { + optionsListView + } + }, + AdditionalData = GitCheckoutOptions.AbortMerge, + CloseButtonAction = (vm, e) => + { + dialog.ViewModel.AdditionalData = GitCheckoutOptions.None; + vm.HideDialog(); + } + }); + + return dialog; + } + public static DynamicDialog GetFor_GitHubConnectionError() { DynamicDialog dialog = new DynamicDialog(new DynamicDialogViewModel() { TitleText = "Error".GetLocalizedResource(), - SubtitleText = "CannotReachGitHubError".GetLocalizedResource(), - PrimaryButtonText = "Close".GetLocalizedResource(), + SubtitleText = Strings.CannotReachGitHubError.GetLocalizedResource(), + PrimaryButtonText = Strings.Close.GetLocalizedResource(), DynamicButtons = DynamicDialogButtons.Primary }); return dialog; @@ -286,8 +340,8 @@ public static DynamicDialog GetFor_GitCannotInitializeqRepositoryHere() return new DynamicDialog(new DynamicDialogViewModel() { TitleText = "Error".GetLocalizedResource(), - SubtitleText = "CannotInitializeGitRepo".GetLocalizedResource(), - PrimaryButtonText = "Close".GetLocalizedResource(), + SubtitleText = Strings.CannotInitializeGitRepo.GetLocalizedResource(), + PrimaryButtonText = Strings.Close.GetLocalizedResource(), DynamicButtons = DynamicDialogButtons.Primary }); } @@ -297,10 +351,10 @@ public static DynamicDialog GetFor_DeleteGitBranchConfirmation(string branchName DynamicDialog dialog = null!; dialog = new DynamicDialog(new DynamicDialogViewModel() { - TitleText = "GitDeleteBranch".GetLocalizedResource(), - SubtitleText = string.Format("GitDeleteBranchSubtitle".GetLocalizedResource(), branchName), - PrimaryButtonText = "OK".GetLocalizedResource(), - CloseButtonText = "Cancel".GetLocalizedResource(), + TitleText = Strings.GitDeleteBranch.GetLocalizedResource(), + SubtitleText = string.Format(Strings.GitDeleteBranchSubtitle.GetLocalizedResource(), branchName), + PrimaryButtonText = Strings.OK.GetLocalizedResource(), + CloseButtonText = Strings.Cancel.GetLocalizedResource(), AdditionalData = true, CloseButtonAction = (vm, e) => { @@ -317,10 +371,10 @@ public static DynamicDialog GetFor_RenameRequiresHigherPermissions(string path) DynamicDialog dialog = null!; dialog = new DynamicDialog(new DynamicDialogViewModel() { - TitleText = "ItemRenameFailed".GetLocalizedResource(), - SubtitleText = string.Format("HigherPermissionsRequired".GetLocalizedResource(), path), - PrimaryButtonText = "OK".GetLocalizedResource(), - SecondaryButtonText = "EditPermissions".GetLocalizedResource(), + TitleText = Strings.ItemRenameFailed.GetLocalizedResource(), + SubtitleText = string.Format(Strings.HigherPermissionsRequired.GetLocalizedResource(), path), + PrimaryButtonText = Strings.OK.GetLocalizedResource(), + SecondaryButtonText = Strings.EditPermissions.GetLocalizedResource(), SecondaryButtonAction = (vm, e) => { var context = Ioc.Default.GetRequiredService(); @@ -333,5 +387,112 @@ public static DynamicDialog GetFor_RenameRequiresHigherPermissions(string path) return dialog; } + + public static DynamicDialog GetFor_CreateAlternateDataStreamDialog() + { + DynamicDialog? dialog = null; + TextBox inputText = new() + { + PlaceholderText = Strings.EnterDataStreamName.GetLocalizedResource() + }; + + TeachingTip warning = new() + { + Title = Strings.InvalidFilename_Text.GetLocalizedResource(), + PreferredPlacement = TeachingTipPlacementMode.Bottom, + DataContext = new CreateItemDialogViewModel(), + }; + + warning.SetBinding(TeachingTip.TargetProperty, new Binding() + { + Source = inputText + }); + warning.SetBinding(TeachingTip.IsOpenProperty, new Binding() + { + Mode = BindingMode.OneWay, + Path = new PropertyPath("IsNameInvalid") + }); + + inputText.Resources.Add("InvalidNameWarningTip", warning); + + inputText.TextChanged += (textBox, args) => + { + var isInputValid = FilesystemHelpers.IsValidForFilename(inputText.Text); + ((CreateItemDialogViewModel)warning.DataContext).IsNameInvalid = !string.IsNullOrEmpty(inputText.Text) && !isInputValid; + dialog!.ViewModel.DynamicButtonsEnabled = isInputValid + ? DynamicDialogButtons.Primary | DynamicDialogButtons.Cancel + : DynamicDialogButtons.Cancel; + if (isInputValid) + dialog.ViewModel.AdditionalData = inputText.Text; + }; + + inputText.Loaded += (s, e) => + { + // dispatching to the ui thread fixes an issue where the primary dialog button would steal focus + _ = inputText.DispatcherQueue.EnqueueOrInvokeAsync(() => inputText.Focus(FocusState.Programmatic)); + }; + + dialog = new DynamicDialog(new DynamicDialogViewModel() + { + TitleText = string.Format(Strings.CreateAlternateDataStream.GetLocalizedResource()), + SubtitleText = null, + DisplayControl = new Grid() + { + MinWidth = 300d, + Children = + { + inputText + } + }, + PrimaryButtonAction = (vm, e) => + { + vm.HideDialog(); + }, + PrimaryButtonText = Strings.Create.GetLocalizedResource(), + CloseButtonText = Strings.Cancel.GetLocalizedResource(), + DynamicButtonsEnabled = DynamicDialogButtons.Cancel, + DynamicButtons = DynamicDialogButtons.Primary | DynamicDialogButtons.Cancel + }); + + dialog.Closing += (s, e) => + { + warning.IsOpen = false; + }; + + return dialog; + } + + public static async Task ShowFor_IDEErrorDialog(string friendlyName) + { + var commands = Ioc.Default.GetRequiredService(); + var dialog = new DynamicDialog(new DynamicDialogViewModel() + { + TitleText = Strings.IDENotLocatedTitle.GetLocalizedResource(), + SubtitleText = string.Format(Strings.IDENotLocatedContent.GetLocalizedResource(), friendlyName), + PrimaryButtonText = Strings.OpenSettings.GetLocalizedResource(), + SecondaryButtonText = Strings.Close.GetLocalizedResource(), + DynamicButtons = DynamicDialogButtons.Primary | DynamicDialogButtons.Secondary, + }); + + await dialog.TryShowAsync(); + + if (dialog.DynamicResult is DynamicDialogResult.Primary) + await commands.OpenSettings.ExecuteAsync( + new SettingsNavigationParams() { PageKind = SettingsPageKind.DevToolsPage } + ); + } + + public static async Task ShowFor_CannotCloneRepo(string exception) + { + var dialog = new DynamicDialog(new DynamicDialogViewModel() + { + TitleText = Strings.CannotCloneRepoTitle.GetLocalizedResource(), + SubtitleText = exception, + PrimaryButtonText = Strings.OK.GetLocalizedResource(), + DynamicButtons = DynamicDialogButtons.Primary + }); + + await dialog.TryShowAsync(); + } } } diff --git a/src/Files.App/Helpers/EnumConversionHelpers.cs b/src/Files.App/Helpers/EnumConversionHelpers.cs index 3677db536cda..ba5c070d22ee 100644 --- a/src/Files.App/Helpers/EnumConversionHelpers.cs +++ b/src/Files.App/Helpers/EnumConversionHelpers.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.Storage; diff --git a/src/Files.App/Helpers/Environment/ElevationHelpers.cs b/src/Files.App/Helpers/Environment/ElevationHelpers.cs deleted file mode 100644 index 2770e09eb7ae..000000000000 --- a/src/Files.App/Helpers/Environment/ElevationHelpers.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System.Security.Principal; - -namespace Files.App.Helpers -{ - public static class ElevationHelpers - { - public static bool IsElevationRequired(string filePath) - { - if (string.IsNullOrEmpty(filePath)) - return false; - - return Win32PInvoke.IsElevationRequired(filePath); - } - - public static bool IsAppRunAsAdmin() - { - using WindowsIdentity identity = WindowsIdentity.GetCurrent(); - return new WindowsPrincipal(identity).IsInRole(new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null)); - } - } -} diff --git a/src/Files.App/Helpers/Environment/PackageHelper.cs b/src/Files.App/Helpers/Environment/PackageHelper.cs index b91995087970..f9c54130ce64 100644 --- a/src/Files.App/Helpers/Environment/PackageHelper.cs +++ b/src/Files.App/Helpers/Environment/PackageHelper.cs @@ -1,9 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using System; -using System.Diagnostics; -using System.Threading.Tasks; using Windows.System; namespace Files.App.Helpers diff --git a/src/Files.App/Helpers/Environment/SoftwareHelpers.cs b/src/Files.App/Helpers/Environment/SoftwareHelpers.cs index cb7ea97fc915..a87fedd14260 100644 --- a/src/Files.App/Helpers/Environment/SoftwareHelpers.cs +++ b/src/Files.App/Helpers/Environment/SoftwareHelpers.cs @@ -1,35 +1,30 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.Win32; -using System.IO; +using System.Security; namespace Files.App.Helpers { internal static class SoftwareHelpers { private const string UninstallRegistryKey = @"Software\Microsoft\Windows\CurrentVersion\Uninstall"; - private const string VsRegistryKey = @"SOFTWARE\Microsoft\VisualStudio"; - - private const string VsCodeName = "Microsoft Visual Studio Code"; + private const string VsCodeName = "Microsoft Visual Studio Code"; public static bool IsVSCodeInstalled() { - return - ContainsName(Registry.CurrentUser.OpenSubKey(UninstallRegistryKey), VsCodeName) || - ContainsName(Registry.LocalMachine.OpenSubKey(UninstallRegistryKey), VsCodeName); - } - - public static bool IsVSInstalled() - { - var key = Registry.LocalMachine.OpenSubKey(VsRegistryKey); - if (key is null) + try + { + return + ContainsName(Registry.CurrentUser.OpenSubKey(UninstallRegistryKey), VsCodeName) || + ContainsName(Registry.LocalMachine.OpenSubKey(UninstallRegistryKey), VsCodeName); + } + catch (SecurityException) + { + // Handle edge case where OpenSubKey results in SecurityException return false; - - key.Close(); - - return true; + } } private static bool ContainsName(RegistryKey? key, string find) @@ -37,20 +32,28 @@ private static bool ContainsName(RegistryKey? key, string find) if (key is null) return false; - foreach (var subKey in key.GetSubKeyNames().Select(key.OpenSubKey)) + try { - var displayName = subKey?.GetValue("DisplayName") as string; - if (!string.IsNullOrWhiteSpace(displayName) && displayName.StartsWith(find)) + foreach (var subKey in key.GetSubKeyNames().Select(key.OpenSubKey)) { - key.Close(); + var displayName = subKey?.GetValue("DisplayName") as string; + if (!string.IsNullOrWhiteSpace(displayName) && displayName.StartsWith(find)) + { + key.Close(); - return true; + return true; + } } - } - key.Close(); + key.Close(); - return false; + return false; + } + catch (SecurityException) + { + // Handle edge case where OpenSubKey results in SecurityException + return false; + } } } } diff --git a/src/Files.App/Helpers/IntervalSampler.cs b/src/Files.App/Helpers/IntervalSampler.cs index 8877215b30a6..e35518f13d80 100644 --- a/src/Files.App/Helpers/IntervalSampler.cs +++ b/src/Files.App/Helpers/IntervalSampler.cs @@ -1,38 +1,34 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Helpers { internal sealed class IntervalSampler { - private DateTime recordPoint; - private TimeSpan sampleInterval; + private readonly TimeSpan sampleInterval; + private DateTime nextRecordPoint; - public IntervalSampler(int millisecondsInterval) + public IntervalSampler(int millisecondsInterval) : this(TimeSpan.FromMilliseconds(millisecondsInterval)) { - sampleInterval = TimeSpan.FromMilliseconds(millisecondsInterval); - recordPoint = DateTime.Now; } public IntervalSampler(TimeSpan interval) { sampleInterval = interval; - recordPoint = DateTime.Now; + Reset(); } public void Reset() { - recordPoint = DateTime.Now; + nextRecordPoint = DateTime.UtcNow + sampleInterval; } public bool CheckNow() { - var now = DateTime.Now; - if (now - sampleInterval >= recordPoint) + var utcNow = DateTime.UtcNow; + if (utcNow >= nextRecordPoint) { - recordPoint = now; + nextRecordPoint = utcNow + sampleInterval; return true; } return false; diff --git a/src/Files.App/Helpers/Layout/AdaptiveLayoutHelpers.cs b/src/Files.App/Helpers/Layout/AdaptiveLayoutHelpers.cs index 0256cbfd0b92..83c5ebe14eaa 100644 --- a/src/Files.App/Helpers/Layout/AdaptiveLayoutHelpers.cs +++ b/src/Files.App/Helpers/Layout/AdaptiveLayoutHelpers.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.ViewModels.Previews; using Files.Shared.Helpers; @@ -43,7 +43,7 @@ private static Layouts GetAdaptiveLayout(IList filesAndFolders) private static Layouts GetPathLayout() { - var desktopIni = ContentPageContext.ShellPage?.ShellViewModel.DesktopIni; + var desktopIni = ContentPageContext.ShellPage?.ShellViewModel?.DesktopIni; if (desktopIni is null) return Layouts.None; @@ -93,7 +93,7 @@ static bool IsImage(ListedItem item) static bool IsMedia(ListedItem item) => !string.IsNullOrEmpty(item.FileExtension) - && (FileExtensionHelpers.IsAudioFile(item.FileExtension) + && (FileExtensionHelpers.IsAudioFile(item.FileExtension) || FileExtensionHelpers.IsVideoFile(item.FileExtension)); } diff --git a/src/Files.App/Helpers/Layout/LayoutHelpers.cs b/src/Files.App/Helpers/Layout/LayoutHelpers.cs new file mode 100644 index 000000000000..eb067e2d57df --- /dev/null +++ b/src/Files.App/Helpers/Layout/LayoutHelpers.cs @@ -0,0 +1,48 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Helpers +{ + static class LayoutHelpers + { + public static void UpdateOpenTabsPreferences() + { + // Services + var multitaskingContext = Ioc.Default.GetRequiredService(); + var layoutSettingsService = Ioc.Default.GetRequiredService(); + + // Get all tab instances and active path + var tabs = multitaskingContext.Control?.GetAllTabInstances(); + var activePath = (multitaskingContext.CurrentTabItem?.TabItemContent as ShellPanesPage)?.ActivePane?.TabBarItemParameter?.NavigationParameter as string; + + // Return if required data is missing + if (tabs is null || activePath is null) + return; + + for (int i = 0; i < tabs.Count; i++) + { + var isNotCurrentTab = i != multitaskingContext.CurrentTabIndex; + var shPage = tabs[i] as ShellPanesPage; + + if (shPage is not null) + { + foreach (var pane in shPage.GetPanes()) + { + var path = pane.ShellViewModel?.CurrentFolder?.ItemPath; + + // Skip panes without a valid path + if (path is null) + continue; + + // Check if we need to update preferences for this pane + if ((isNotCurrentTab || !ReferenceEquals(pane, shPage.ActivePane)) && + (layoutSettingsService.SyncFolderPreferencesAcrossDirectories || + path.Equals(activePath, StringComparison.OrdinalIgnoreCase))) + if (pane.SlimContentPage is BaseLayoutPage page) + page.FolderSettings?.ReloadGroupAndSortPreferences(path); + } + } + } + } + } +} diff --git a/src/Files.App/Helpers/Layout/LayoutPreferencesDatabase.cs b/src/Files.App/Helpers/Layout/LayoutPreferencesDatabase.cs index cde2cd28ffab..e03b0a7e0db2 100644 --- a/src/Files.App/Helpers/Layout/LayoutPreferencesDatabase.cs +++ b/src/Files.App/Helpers/Layout/LayoutPreferencesDatabase.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.Win32; using System.Runtime.CompilerServices; @@ -12,7 +12,7 @@ namespace Files.App.Helpers { public sealed class LayoutPreferencesDatabase { - private readonly static string LayoutSettingsKey = @$"Software\Files Community\{Package.Current.Id.FullName}\v1\LayoutPreferences"; + private readonly static string LayoutSettingsKey = @$"Software\Files Community\{Package.Current.Id.Name}\v1\LayoutPreferences"; public LayoutPreferencesItem? GetPreferences(string filePath, ulong? frn) { diff --git a/src/Files.App/Helpers/Layout/LayoutPreferencesDatabaseItem.cs b/src/Files.App/Helpers/Layout/LayoutPreferencesDatabaseItem.cs index a853aba0304e..c327526c3587 100644 --- a/src/Files.App/Helpers/Layout/LayoutPreferencesDatabaseItem.cs +++ b/src/Files.App/Helpers/Layout/LayoutPreferencesDatabaseItem.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Helpers { diff --git a/src/Files.App/Helpers/Layout/LayoutPreferencesDatabaseManager.cs b/src/Files.App/Helpers/Layout/LayoutPreferencesDatabaseManager.cs index e28905627fa6..14ae02bd94ed 100644 --- a/src/Files.App/Helpers/Layout/LayoutPreferencesDatabaseManager.cs +++ b/src/Files.App/Helpers/Layout/LayoutPreferencesDatabaseManager.cs @@ -1,7 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System.Diagnostics.CodeAnalysis; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Helpers { diff --git a/src/Files.App/Helpers/Layout/LayoutPreferencesItem.cs b/src/Files.App/Helpers/Layout/LayoutPreferencesItem.cs index 68577a863d17..870b14b4fe3f 100644 --- a/src/Files.App/Helpers/Layout/LayoutPreferencesItem.cs +++ b/src/Files.App/Helpers/Layout/LayoutPreferencesItem.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Helpers { diff --git a/src/Files.App/Helpers/Layout/LayoutPreferencesManager.cs b/src/Files.App/Helpers/Layout/LayoutPreferencesManager.cs index d658e0d340e3..866cedd4f4bb 100644 --- a/src/Files.App/Helpers/Layout/LayoutPreferencesManager.cs +++ b/src/Files.App/Helpers/Layout/LayoutPreferencesManager.cs @@ -1,9 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.App.Data.Enums; -using System.Text.Json; -using Windows.Storage; using Windows.Win32; namespace Files.App.Helpers @@ -11,7 +8,7 @@ namespace Files.App.Helpers /// /// Represents manager for layout preferences settings. /// - public sealed class LayoutPreferencesManager : ObservableObject + public sealed partial class LayoutPreferencesManager : ObservableObject { // Dependency injections @@ -205,43 +202,6 @@ public LayoutPreferencesManager(FolderLayoutModes modeOverride) : this() // Methods - /// - /// This will round the current icon size to get the best result from the File Explorer thumbnail system. - /// - /// Details View: - /// Always uses the Large icon size (32). - /// - /// List View: - /// Always uses the Large icon size (32). - /// - /// Columns View: - /// Always uses the Large icon size (32). - /// - /// Tiles View: - /// Always uses 96, 128, or 256 depending on the layout size. - /// - /// Grid View: - /// Always uses 96, 128, or 256 depending on the layout size. - /// - public uint GetRoundedIconSize() - { - return LayoutMode switch - { - FolderLayoutModes.DetailsView - => Constants.ShellIconSizes.Large, - FolderLayoutModes.ListView - => Constants.ShellIconSizes.Large, - FolderLayoutModes.ColumnView - => Constants.ShellIconSizes.Large, - _ when LayoutMode == FolderLayoutModes.GridView && UserSettingsService.LayoutSettingsService.GridViewSize <= GridViewSizeKind.Small || - LayoutMode == FolderLayoutModes.TilesView - => 96, - _ when LayoutMode == FolderLayoutModes.GridView && UserSettingsService.LayoutSettingsService.GridViewSize <= GridViewSizeKind.Large - => 128, - _ => 256, - }; - } - public Type GetLayoutType(string path, bool changeLayoutMode = true) { var preferencesItem = GetLayoutPreferencesForPath(path); @@ -258,13 +218,38 @@ public Type GetLayoutType(string path, bool changeLayoutMode = true) { FolderLayoutModes.DetailsView => typeof(DetailsLayoutPage), FolderLayoutModes.ListView => typeof(GridLayoutPage), - FolderLayoutModes.TilesView => typeof(GridLayoutPage), + FolderLayoutModes.CardsView => typeof(GridLayoutPage), FolderLayoutModes.GridView => typeof(GridLayoutPage), FolderLayoutModes.ColumnView => typeof(ColumnsLayoutPage), _ => typeof(DetailsLayoutPage) }; } + public void ReloadGroupAndSortPreferences(string? path) + { + if (string.IsNullOrWhiteSpace(path)) + return; + + var preferencesItem = GetLayoutPreferencesForPath(path); + if (preferencesItem is null) + return; + + DirectorySortOption = preferencesItem.DirectorySortOption; + DirectorySortDirection = preferencesItem.DirectorySortDirection; + DirectoryGroupOption = preferencesItem.DirectoryGroupOption; + DirectoryGroupByDateUnit = preferencesItem.DirectoryGroupByDateUnit; + DirectoryGroupDirection = preferencesItem.DirectoryGroupDirection; + SortDirectoriesAlongsideFiles = preferencesItem.SortDirectoriesAlongsideFiles; + SortFilesFirst = preferencesItem.SortFilesFirst; + } + + public bool IsPathUsingDefaultLayout(string? path) + { + return UserSettingsService.LayoutSettingsService.SyncFolderPreferencesAcrossDirectories || + string.IsNullOrEmpty(path) || + GetLayoutPreferencesFromDatabase(path, Win32Helper.GetFolderFRN(path)) is null; + } + public void ToggleLayoutModeColumnView(bool manuallySet) { IsAdaptiveLayoutEnabled &= !manuallySet; @@ -285,14 +270,14 @@ public void ToggleLayoutModeGridView(bool manuallySet) LayoutModeChangeRequested?.Invoke(this, new LayoutModeEventArgs(FolderLayoutModes.GridView)); } - public void ToggleLayoutModeTiles(bool manuallySet) + public void ToggleLayoutModeCards(bool manuallySet) { IsAdaptiveLayoutEnabled &= !manuallySet; - // Tiles View - LayoutMode = FolderLayoutModes.TilesView; + // Cards View + LayoutMode = FolderLayoutModes.CardsView; - LayoutModeChangeRequested?.Invoke(this, new LayoutModeEventArgs(FolderLayoutModes.TilesView)); + LayoutModeChangeRequested?.Invoke(this, new LayoutModeEventArgs(FolderLayoutModes.CardsView)); } public void ToggleLayoutModeList(bool manuallySet) @@ -501,6 +486,34 @@ public static void SetLayoutPreferencesForPath(string path, LayoutPreferencesIte private static LayoutPreferencesItem? GetLayoutPreferencesForPath(string path) { + // Guard against null + if (path is null) + return null; + + //Recycle Bin does not support Column View due to navigation conflicts with hierarchical display + //Fall back to Details View when Column View is configured + if (path.StartsWith(Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.Ordinal)) + { + var trimmedPath = path.TrimPath() ?? string.Empty; + + var recycleBinPreference = SafetyExtensions.IgnoreExceptions(() => + { + var folderFRN = Win32Helper.GetFolderFRN(trimmedPath); + + return GetLayoutPreferencesFromDatabase(trimmedPath, folderFRN) + ?? GetLayoutPreferencesFromAds(trimmedPath, folderFRN); + }, App.Logger); + + if (recycleBinPreference is not null && recycleBinPreference.LayoutMode != FolderLayoutModes.ColumnView) + return recycleBinPreference; + + var defaultPref = new LayoutPreferencesItem(); + if (defaultPref.LayoutMode == FolderLayoutModes.ColumnView) + defaultPref.LayoutMode = FolderLayoutModes.DetailsView; + + return defaultPref; + } + if (!UserSettingsService.LayoutSettingsService.SyncFolderPreferencesAcrossDirectories) { path = path.TrimPath() ?? string.Empty; @@ -594,7 +607,7 @@ private static bool SetLayoutPreferencesToDatabase(string path, ulong? frn, Layo } dbInstance.SetPreferences(path, frn, preferencesItem); - }); + }); } private bool SetProperty(Func prop, Action update, string propertyName) @@ -611,4 +624,4 @@ private bool SetProperty(Func prop, Actio return true; } } -} +} \ No newline at end of file diff --git a/src/Files.App/Helpers/Layout/LayoutSizeKindHelper.cs b/src/Files.App/Helpers/Layout/LayoutSizeKindHelper.cs index fa25561d4737..6dec2955adff 100644 --- a/src/Files.App/Helpers/Layout/LayoutSizeKindHelper.cs +++ b/src/Files.App/Helpers/Layout/LayoutSizeKindHelper.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Helpers { @@ -7,6 +7,50 @@ public static class LayoutSizeKindHelper { private static ILayoutSettingsService LayoutSettingsService { get; } = Ioc.Default.GetRequiredService(); + /// + /// Gets the desired icon size for the requested layout + /// + /// + /// + public static uint GetIconSize(FolderLayoutModes folderLayoutMode) + { + return folderLayoutMode switch + { + // Details + FolderLayoutModes.DetailsView when LayoutSettingsService.DetailsViewSize == DetailsViewSizeKind.Compact => Constants.ShellIconSizes.Small, + FolderLayoutModes.DetailsView when LayoutSettingsService.DetailsViewSize == DetailsViewSizeKind.Small => Constants.ShellIconSizes.Small, + FolderLayoutModes.DetailsView when LayoutSettingsService.DetailsViewSize == DetailsViewSizeKind.Medium => 20, + FolderLayoutModes.DetailsView when LayoutSettingsService.DetailsViewSize == DetailsViewSizeKind.Large => 24, + FolderLayoutModes.DetailsView when LayoutSettingsService.DetailsViewSize == DetailsViewSizeKind.ExtraLarge => Constants.ShellIconSizes.Large, + + // List + FolderLayoutModes.ListView when LayoutSettingsService.ListViewSize == ListViewSizeKind.Compact => Constants.ShellIconSizes.Small, + FolderLayoutModes.ListView when LayoutSettingsService.ListViewSize == ListViewSizeKind.Small => Constants.ShellIconSizes.Small, + FolderLayoutModes.ListView when LayoutSettingsService.ListViewSize == ListViewSizeKind.Medium => 20, + FolderLayoutModes.ListView when LayoutSettingsService.ListViewSize == ListViewSizeKind.Large => 24, + FolderLayoutModes.ListView when LayoutSettingsService.ListViewSize == ListViewSizeKind.ExtraLarge => Constants.ShellIconSizes.Large, + + // Columns + FolderLayoutModes.ColumnView when LayoutSettingsService.ColumnsViewSize == ColumnsViewSizeKind.Compact => Constants.ShellIconSizes.Small, + FolderLayoutModes.ColumnView when LayoutSettingsService.ColumnsViewSize == ColumnsViewSizeKind.Small => Constants.ShellIconSizes.Small, + FolderLayoutModes.ColumnView when LayoutSettingsService.ColumnsViewSize == ColumnsViewSizeKind.Medium => 20, + FolderLayoutModes.ColumnView when LayoutSettingsService.ColumnsViewSize == ColumnsViewSizeKind.Large => 24, + FolderLayoutModes.ColumnView when LayoutSettingsService.ColumnsViewSize == ColumnsViewSizeKind.ExtraLarge => Constants.ShellIconSizes.Large, + + // Card + FolderLayoutModes.CardsView when LayoutSettingsService.CardsViewSize == CardsViewSizeKind.Small => 64, + FolderLayoutModes.CardsView when LayoutSettingsService.CardsViewSize == CardsViewSizeKind.Medium => 64, + FolderLayoutModes.CardsView when LayoutSettingsService.CardsViewSize == CardsViewSizeKind.Large => 80, + FolderLayoutModes.CardsView when LayoutSettingsService.CardsViewSize == CardsViewSizeKind.ExtraLarge => 96, + + // Grid + FolderLayoutModes.GridView when LayoutSettingsService.GridViewSize <= GridViewSizeKind.Small => 96, + FolderLayoutModes.GridView when LayoutSettingsService.GridViewSize <= GridViewSizeKind.Large => 128, + + _ => 256, + }; + } + /// /// Gets the desired height for items in the Details View /// @@ -116,15 +160,5 @@ public static int GetColumnsViewRowHeight(ColumnsViewSizeKind columnsViewSizeKin return 32; } } - - /// - /// Gets the desired width for items in the Tiles View - /// - /// - /// - public static int GetTilesViewItemWidth(TilesViewSizeKind tilesViewSizeKind) - { - return 260; - } } } \ No newline at end of file diff --git a/src/Files.App/Helpers/LocationHelpers.cs b/src/Files.App/Helpers/LocationHelpers.cs index 2f712e77cd8d..fc4757a9bab3 100644 --- a/src/Files.App/Helpers/LocationHelpers.cs +++ b/src/Files.App/Helpers/LocationHelpers.cs @@ -1,5 +1,5 @@ -// Copyright(c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright(c) Files Community +// Licensed under the MIT License. using Windows.Devices.Geolocation; using Windows.Services.Maps; diff --git a/src/Files.App/Helpers/LogPathHelper.cs b/src/Files.App/Helpers/LogPathHelper.cs new file mode 100644 index 000000000000..3adb028c4737 --- /dev/null +++ b/src/Files.App/Helpers/LogPathHelper.cs @@ -0,0 +1,34 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace Files.App.Helpers +{ + public static class LogPathHelper + { + public static string GetPathIdentifier(string? path) + { + if (string.IsNullOrEmpty(path)) + return "[Empty]"; + + try + { + var hashBytes = MD5.HashData(Encoding.UTF8.GetBytes(path)); + + //4 bytes, still low collision + var shortHash = Convert.ToHexStringLower(hashBytes, 0, 4); + + var extension = Path.GetExtension(path); + + return $"[hash:{shortHash}{extension}]"; + } + catch + { + return "[?]"; + } + } + } +} diff --git a/src/Files.App/Helpers/MenuFlyout/ContextFlyoutModelToElementHelper.cs b/src/Files.App/Helpers/MenuFlyout/ContextFlyoutModelToElementHelper.cs index a2a260047fee..95a815b76ede 100644 --- a/src/Files.App/Helpers/MenuFlyout/ContextFlyoutModelToElementHelper.cs +++ b/src/Files.App/Helpers/MenuFlyout/ContextFlyoutModelToElementHelper.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.UserControls.Menus; using Microsoft.UI.Xaml; @@ -229,8 +229,8 @@ private static ICommandBarElement GetCommandBarButton(ContextMenuFlyoutItemViewM { Source = item.BitmapIcon, }; - else if (item.OpacityIcon.IsValid) - content = item.OpacityIcon.ToOpacityIcon(); + else if (item.ThemedIconModel.IsValid) + content = item.ThemedIconModel.ToThemedIcon(); else if (item.ShowLoadingIndicator) content = new ProgressRing() { @@ -251,6 +251,7 @@ private static ICommandBarElement GetCommandBarButton(ContextMenuFlyoutItemViewM LabelPosition = item.IsPrimary || item.CollapseLabel ? CommandBarLabelPosition.Collapsed : CommandBarLabelPosition.Default, IsEnabled = item.IsEnabled, Visibility = item.IsHidden ? Visibility.Collapsed : Visibility.Visible, + AccessKey = item.AccessKey, }; if (element is AppBarToggleButton toggleButton) @@ -281,6 +282,7 @@ private static ICommandBarElement GetCommandBarButton(ContextMenuFlyoutItemViewM Content = content, IsEnabled = item.IsEnabled, Visibility = item.IsHidden ? Visibility.Collapsed : Visibility.Visible, + AccessKey = item.AccessKey, }; if (element is AppBarButton button) @@ -295,6 +297,8 @@ private static ICommandBarElement GetCommandBarButton(ContextMenuFlyoutItemViewM { button.KeyboardAccelerators.Add(item.KeyboardAccelerator); button.KeyboardAcceleratorTextOverride = item.KeyboardAcceleratorTextOverride; + // Fixes #16193: VirtualKey does not support OEM keys (e.g "�") + button.KeyboardAcceleratorPlacementMode = Microsoft.UI.Xaml.Input.KeyboardAcceleratorPlacementMode.Hidden; } } } diff --git a/src/Files.App/Helpers/MenuFlyout/MenuFlyoutHelper.cs b/src/Files.App/Helpers/MenuFlyout/MenuFlyoutHelper.cs index 8db44068d672..925bdc971a32 100644 --- a/src/Files.App/Helpers/MenuFlyout/MenuFlyoutHelper.cs +++ b/src/Files.App/Helpers/MenuFlyout/MenuFlyoutHelper.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -56,7 +56,7 @@ public MenuFlyoutFactoryItemViewModel(Func factoryFunc) public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.RegisterAttached("ItemsSource", typeof(IEnumerable), typeof(MenuFlyoutHelper), new PropertyMetadata(null, ItemsSourceChanged)); - private static void ItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) => SetupItemsAsync(d as MenuFlyout); + private static void ItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) => _ = SetupItemsAsync(d as MenuFlyout); public static bool GetIsVisible(DependencyObject d) => (bool)d.GetValue(IsVisibleProperty); diff --git a/src/Files.App/Helpers/NaturalStringComparer.cs b/src/Files.App/Helpers/NaturalStringComparer.cs index 2e22c266417c..6108d604c0b4 100644 --- a/src/Files.App/Helpers/NaturalStringComparer.cs +++ b/src/Files.App/Helpers/NaturalStringComparer.cs @@ -1,37 +1,176 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Helpers { + // Credit: https://github.com/GihanSoft/NaturalStringComparer public sealed class NaturalStringComparer { public static IComparer GetForProcessor() { - return Win32Helper.IsRunningOnArm ? new StringComparerArm64() : new StringComparerDefault(); + return new NaturalComparer(StringComparison.CurrentCultureIgnoreCase); } - private sealed class StringComparerArm64 : IComparer + /// + /// Provides functionality to compare and sort strings in a natural (human-readable) order. + /// + /// + /// This class implements string comparison that respects the natural numeric order in strings, + /// such as "file10" being ordered after "file2". + /// It is designed to handle cases where alphanumeric sorting is required. + /// + private sealed class NaturalComparer : IComparer, IComparer, IComparer> { - public int Compare(object a, object b) + private readonly StringComparison stringComparison; + + public NaturalComparer(StringComparison stringComparison = StringComparison.Ordinal) { - return StringComparer.CurrentCulture.Compare(a, b); + this.stringComparison = stringComparison; } - } - private sealed class StringComparerDefault : IComparer - { - public int Compare(object a, object b) - { - return Win32PInvoke.CompareStringEx( - Win32PInvoke.LOCALE_NAME_USER_DEFAULT, - Win32PInvoke.SORT_DIGITSASNUMBERS, // Add other flags if required. - a?.ToString(), - a?.ToString().Length ?? 0, - b?.ToString(), - b?.ToString().Length ?? 0, - IntPtr.Zero, - IntPtr.Zero, - 0) - 2; + public int Compare(object? x, object? y) + { + if (x == y) return 0; + if (x == null) return -1; + if (y == null) return 1; + + return x switch + { + string x1 when y is string y1 => Compare(x1.AsSpan(), y1.AsSpan(), stringComparison), + IComparable comparable => comparable.CompareTo(y), + _ => StringComparer.FromComparison(stringComparison).Compare(x, y) + }; + } + + public int Compare(string? x, string? y) + { + if (ReferenceEquals(x, y)) return 0; + if (x is null) return -1; + if (y is null) return 1; + + return Compare(x.AsSpan(), y.AsSpan(), stringComparison); + } + + public int Compare(ReadOnlySpan x, ReadOnlySpan y) + { + return Compare(x, y, stringComparison); + } + + public int Compare(ReadOnlyMemory x, ReadOnlyMemory y) + { + return Compare(x.Span, y.Span, stringComparison); + } + + public static int Compare(ReadOnlySpan x, ReadOnlySpan y, StringComparison stringComparison) + { + // Handle file extensions specially + int xExtPos = GetExtensionPosition(x); + int yExtPos = GetExtensionPosition(y); + + // If both have extensions, compare the names first + if (xExtPos >= 0 && yExtPos >= 0) + { + var xName = x.Slice(0, xExtPos); + var yName = y.Slice(0, yExtPos); + + int nameCompare = CompareWithoutExtension(xName, yName, stringComparison); + if (nameCompare != 0) + return nameCompare; + + // If names match, compare extensions + return x.Slice(xExtPos).CompareTo(y.Slice(yExtPos), stringComparison); + } + + // Original comparison logic for non-extension cases + return CompareWithoutExtension(x, y, stringComparison); + } + + private static int CompareWithoutExtension(ReadOnlySpan x, ReadOnlySpan y, StringComparison stringComparison) + { + var length = Math.Min(x.Length, y.Length); + + for (var i = 0; i < length; i++) + { + while (i < x.Length && i < y.Length && IsIgnorableSeparator(x, i) && IsIgnorableSeparator(y, i)) + i++; + + if (i >= x.Length || i >= y.Length) break; + + if (char.IsDigit(x[i]) && char.IsDigit(y[i])) + { + var xOut = GetNumber(x.Slice(i), out var xNumAsSpan); + var yOut = GetNumber(y.Slice(i), out var yNumAsSpan); + + var compareResult = CompareNumValues(xNumAsSpan, yNumAsSpan); + + if (compareResult != 0) return compareResult; + + i = -1; + length = Math.Min(xOut.Length, yOut.Length); + + x = xOut; + y = yOut; + continue; + } + + var charCompareResult = x.Slice(i, 1).CompareTo(y.Slice(i, 1), stringComparison); + if (charCompareResult != 0) return charCompareResult; + } + + return x.Length.CompareTo(y.Length); + } + + private static int GetExtensionPosition(ReadOnlySpan text) + { + // Find the last period that's not at the beginning + for (int i = text.Length - 1; i > 0; i--) + { + if (text[i] == '.') + return i; + } + return -1; + } + + private static bool IsIgnorableSeparator(ReadOnlySpan span, int index) + { + if (span[index] != '-' && span[index] != '_') return false; + + // Check bounds before accessing span[index + 1] or span[index - 1] + if (index == 0) return span.Length > 1 && char.IsLetterOrDigit(span[index + 1]); + if (index == span.Length - 1) return span.Length > 1 && char.IsLetterOrDigit(span[index - 1]); + + return char.IsLetterOrDigit(span[index - 1]) && char.IsLetterOrDigit(span[index + 1]); + } + + + private static ReadOnlySpan GetNumber(ReadOnlySpan span, out ReadOnlySpan number) + { + var i = 0; + while (i < span.Length && char.IsDigit(span[i])) + { + i++; + } + + number = span.Slice(0, i); + return span.Slice(i); + } + + private static int CompareNumValues(ReadOnlySpan numValue1, ReadOnlySpan numValue2) + { + var num1AsSpan = numValue1.TrimStart('0'); + var num2AsSpan = numValue2.TrimStart('0'); + + if (num1AsSpan.Length < num2AsSpan.Length) return -1; + + if (num1AsSpan.Length > num2AsSpan.Length) return 1; + + var compareResult = num1AsSpan.CompareTo(num2AsSpan, StringComparison.Ordinal); + + if (compareResult != 0) return Math.Sign(compareResult); + + if (numValue2.Length == numValue1.Length) return compareResult; + + return numValue2.Length < numValue1.Length ? -1 : 1; // "033" < "33" == true } } } diff --git a/src/Files.App/Helpers/Navigation/MultitaskingTabsHelpers.cs b/src/Files.App/Helpers/Navigation/MultitaskingTabsHelpers.cs index 03bd783b4e95..f24bc2d5a97f 100644 --- a/src/Files.App/Helpers/Navigation/MultitaskingTabsHelpers.cs +++ b/src/Files.App/Helpers/Navigation/MultitaskingTabsHelpers.cs @@ -1,10 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.App.UserControls.TabBar; -using Files.App.ViewModels; -using System.Linq; -using System.Threading.Tasks; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Helpers { @@ -41,6 +36,15 @@ public static void CloseOtherTabs(TabBarItem clickedTab, ITabBar multitaskingCon } } + public static void CloseAllTabs(ITabBar multitaskingControl) + { + if (multitaskingControl is not null) + { + var tabs = MainPageViewModel.AppInstances; + tabs.ToList().ForEach(tab => multitaskingControl.CloseTab(tab)); + } + } + public static Task MoveTabToNewWindow(TabBarItem tab, ITabBar multitaskingControl) { int index = MainPageViewModel.AppInstances.IndexOf(tab); diff --git a/src/Files.App/Helpers/Navigation/NavigationHelpers.cs b/src/Files.App/Helpers/Navigation/NavigationHelpers.cs index 4bea0a45bbd3..59ab7fb90b86 100644 --- a/src/Files.App/Helpers/Navigation/NavigationHelpers.cs +++ b/src/Files.App/Helpers/Navigation/NavigationHelpers.cs @@ -1,9 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.Shared.Helpers; using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Media.Imaging; +using System.IO; using Windows.Storage; using Windows.Storage.Search; using Windows.System; @@ -12,13 +14,33 @@ namespace Files.App.Helpers { public static class NavigationHelpers { + private static readonly IGeneralSettingsService GeneralSettingsService = Ioc.Default.GetRequiredService(); + + private static readonly IWindowsRecentItemsService WindowsRecentItemsService = Ioc.Default.GetRequiredService(); private static MainPageViewModel MainPageViewModel { get; } = Ioc.Default.GetRequiredService(); private static DrivesViewModel DrivesViewModel { get; } = Ioc.Default.GetRequiredService(); private static INetworkService NetworkService { get; } = Ioc.Default.GetRequiredService(); - public static Task OpenPathInNewTab(string? path, bool focusNewTab) + /// + /// Opens the path in a new tab. + /// + /// The path to open in a new tab. + /// Indicates whether to switch to the new tab. + /// + public static Task OpenPathInNewTab(string? path, bool switchToNewTab) + { + return AddNewTabByPathAsync(typeof(ShellPanesPage), path, switchToNewTab); + } + + /// + /// Opens the path in a new tab and automatically switches to the newly + /// created tab if configured in settings. + /// + /// The path to open in a new tab. + /// + public static Task OpenPathInNewTab(string? path) { - return AddNewTabByPathAsync(typeof(ShellPanesPage), path, focusNewTab); + return AddNewTabByPathAsync(typeof(ShellPanesPage), path, GeneralSettingsService.AlwaysSwitchToNewlyOpenedTab); } public static Task AddNewTabAsync() @@ -26,14 +48,14 @@ public static Task AddNewTabAsync() return AddNewTabByPathAsync(typeof(ShellPanesPage), "Home", true); } - public static async Task AddNewTabByPathAsync(Type type, string? path, bool focusNewTab, int atIndex = -1) + public static async Task AddNewTabByPathAsync(Type type, string? path, bool switchToNewTab, int atIndex = -1) { if (string.IsNullOrEmpty(path)) { path = "Home"; } // Support drives launched through jump list by stripping away the question mark at the end. - else if (path.EndsWith("\\?")) + else if (path.EndsWith("\\?", StringComparison.Ordinal)) { path = path.Remove(path.Length - 1); } @@ -59,7 +81,7 @@ public static async Task AddNewTabByPathAsync(Type type, string? path, bool focu MainPageViewModel.AppInstances.Insert(index, tabItem); - if (focusNewTab) + if (switchToNewTab) App.AppModel.TabStripSelectedIndex = index; } @@ -125,11 +147,103 @@ private static async Task UpdateTabInfoAsync(TabBarItem tabItem, object navigati var a1 = navigationParameter is PaneNavigationArguments pna1 ? pna1 : new PaneNavigationArguments() { LeftPaneNavPathParam = navigationParameter as string }; var a2 = navigationArg is PaneNavigationArguments pna2 ? pna2 : new PaneNavigationArguments() { LeftPaneNavPathParam = navigationArg as string }; - if (a1 == a2) - (tabItem.Header, tabItem.IconSource, tabItem.ToolTipText) = result; + if (a1.LeftPaneNavPathParam == a2.LeftPaneNavPathParam && a1.RightPaneNavPathParam == a2.RightPaneNavPathParam) + { + tabItem.Description = result.Item1; + tabItem.IconSource = result.Item2; + tabItem.ToolTipText = result.Item3; + RefreshTabPathHints(); + } } } + internal static void RefreshTabPathHints() + { + foreach (var group in MainPageViewModel.AppInstances + .Where(t => !string.IsNullOrEmpty(t.Description)) + .GroupBy(t => t.Description!, StringComparer.OrdinalIgnoreCase)) + { + var tabs = group.ToArray(); + + foreach (var t in tabs) + t.Header = t.Description; + + if (tabs.Length < 2 || tabs[0].Description!.Contains(" | ")) + continue; + + var hints = tabs.ToDictionary(t => t, t => AncestorHints(t.ToolTipText)); + + foreach (var tab in tabs) + { + for (var d = 0; d < hints[tab].Length; d++) + { + if (tabs.All(t => t == tab || d >= hints[t].Length || hints[t][d] != hints[tab][d])) + { + tab.Header = $"{hints[tab][d]}\\{tab.Description}"; + break; + } + } + } + } + } + + private static string[] AncestorHints(string? path) + { + var result = new List(); + + try + { + var root = (PathNormalization.GetPathRoot(path) ?? "").TrimEnd('\\', '/'); + var prefix = root.Length >= 2 && root[1] == ':' + ? $"{char.ToUpperInvariant(root[0])}:\\..." + : root.Length > 0 ? $"{root}\\..." : "..."; + + var dir = path?.TrimEnd('\\', '/'); + while ((dir = Path.GetDirectoryName(dir)) is not null + && Path.GetFileName(dir) is { Length: > 0 } seg) + { + result.Add($"{prefix}\\{seg}"); + } + } + catch (ArgumentException) { } + + return result.ToArray(); + } + + public static async Task GetIconForPathAsync(string path) + { + ImageSource? imageSource; + if (string.IsNullOrEmpty(path) || path == "Home") + imageSource = new BitmapImage(new Uri(Constants.FluentIconsPaths.HomeIcon)); + else if (path == "ReleaseNotes") + imageSource = new BitmapImage(new Uri(AppLifecycleHelper.AppIconPath)); + // TODO add settings page + //else if (path == "Settings") + // imageSource = new BitmapImage(new Uri(AppLifecycleHelper.AppIconPath)); + else if (WSLDistroManager.TryGetDistro(path, out WslDistroItem? wslDistro) && path.Equals(wslDistro.Path)) + imageSource = new BitmapImage(wslDistro.Icon); + else + { + var normalizedPath = PathNormalization.NormalizePath(path); + var matchingCloudDrive = CloudDrivesManager.Drives.FirstOrDefault(x => normalizedPath.Equals(PathNormalization.NormalizePath(x.Path), StringComparison.OrdinalIgnoreCase)); + imageSource = matchingCloudDrive?.Icon; + + if (imageSource is null) + { + var result = await FileThumbnailHelper.GetIconAsync( + path, + Constants.ShellIconSizes.Small, + true, + IconOptions.ReturnIconOnly | IconOptions.UseCurrentScale); + + if (result is not null) + imageSource = await result.ToBitmapAsync(); + } + } + + return imageSource; + } + public static async Task<(string tabLocationHeader, IconSource tabIcon, string toolTipText)> GetSelectedTabInfoAsync(string currentPath) { string? tabLocationHeader; @@ -138,19 +252,30 @@ private static async Task UpdateTabInfoAsync(TabBarItem tabItem, object navigati if (string.IsNullOrEmpty(currentPath) || currentPath == "Home") { - tabLocationHeader = "Home".GetLocalizedResource(); + tabLocationHeader = Strings.Home.GetLocalizedResource(); iconSource.ImageSource = new BitmapImage(new Uri(Constants.FluentIconsPaths.HomeIcon)); } + else if (currentPath == "ReleaseNotes") + { + tabLocationHeader = Strings.ReleaseNotes.GetLocalizedResource(); + iconSource.ImageSource = new BitmapImage(new Uri(AppLifecycleHelper.AppIconPath)); + } + // TODO add settings page + //else if (currentPath == "Settings") + //{ + // tabLocationHeader = Strings.Settings.GetLocalizedResource(); + // iconSource.ImageSource = new BitmapImage(new Uri(AppLifecycleHelper.AppIconPath)); + //} else if (currentPath.Equals(Constants.UserEnvironmentPaths.DesktopPath, StringComparison.OrdinalIgnoreCase)) - tabLocationHeader = "Desktop".GetLocalizedResource(); + tabLocationHeader = Strings.Desktop.GetLocalizedResource(); else if (currentPath.Equals(Constants.UserEnvironmentPaths.DownloadsPath, StringComparison.OrdinalIgnoreCase)) - tabLocationHeader = "Downloads".GetLocalizedResource(); + tabLocationHeader = Strings.Downloads.GetLocalizedResource(); else if (currentPath.Equals(Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.OrdinalIgnoreCase)) - tabLocationHeader = "RecycleBin".GetLocalizedResource(); + tabLocationHeader = Strings.RecycleBin.GetLocalizedResource(); else if (currentPath.Equals(Constants.UserEnvironmentPaths.MyComputerPath, StringComparison.OrdinalIgnoreCase)) - tabLocationHeader = "ThisPC".GetLocalizedResource(); + tabLocationHeader = Strings.ThisPC.GetLocalizedResource(); else if (currentPath.Equals(Constants.UserEnvironmentPaths.NetworkFolderPath, StringComparison.OrdinalIgnoreCase)) - tabLocationHeader = "Network".GetLocalizedResource(); + tabLocationHeader = Strings.Network.GetLocalizedResource(); else if (App.LibraryManager.TryGetLibrary(currentPath, out LibraryLocationItem library)) { var libName = System.IO.Path.GetFileNameWithoutExtension(library.Path).GetLocalizedResource(); @@ -220,17 +345,10 @@ await SafetyExtensions.IgnoreExceptions(async () => windowTitle = $"{leftTabInfo.tabLocationHeader} | {rightTabInfo.tabLocationHeader}"; } else - { (windowTitle, _, _) = await GetSelectedTabInfoAsync(paneArgs.LeftPaneNavPathParam); - } } else if (navigationArg is string pathArgs) - { (windowTitle, _, _) = await GetSelectedTabInfoAsync(pathArgs); - } - - if (MainPageViewModel.AppInstances.Count > 1) - windowTitle = $"{windowTitle} ({MainPageViewModel.AppInstances.Count})"; if (navigationArg == MainPageViewModel.SelectedTabItem?.NavigationParameter?.NavigationParameter) MainWindow.Instance.AppWindow.Title = $"{windowTitle} - Files"; @@ -254,14 +372,14 @@ public static Task OpenPathInNewWindowAsync(string? path) if (string.IsNullOrWhiteSpace(path)) return Task.FromResult(false); - var folderUri = new Uri($"files-uwp:?folder={Uri.EscapeDataString(path)}"); + var folderUri = new Uri($"files-dev:?folder={Uri.EscapeDataString(path)}"); return Launcher.LaunchUriAsync(folderUri).AsTask(); } public static Task OpenTabInNewWindowAsync(string tabArgs) { - var folderUri = new Uri($"files-uwp:?tab={Uri.EscapeDataString(tabArgs)}"); + var folderUri = new Uri($"files-dev:?tab={Uri.EscapeDataString(tabArgs)}"); return Launcher.LaunchUriAsync(folderUri).AsTask(); } @@ -270,19 +388,19 @@ public static void OpenInSecondaryPane(IShellPage associatedInstance, ListedItem if (associatedInstance is null || listedItem is null) return; - associatedInstance.PaneHolder?.OpenSecondaryPane((listedItem as ShortcutItem)?.TargetPath ?? listedItem.ItemPath); + associatedInstance.PaneHolder?.OpenSecondaryPane((listedItem as IShortcutItem)?.TargetPath ?? listedItem.ItemPath); } public static Task LaunchNewWindowAsync() { - var filesUWPUri = new Uri("files-uwp:?window="); - return Launcher.LaunchUriAsync(filesUWPUri).AsTask(); + return Launcher.LaunchUriAsync(new Uri("files-dev:?window=")).AsTask(); } public static async Task OpenSelectedItemsAsync(IShellPage associatedInstance, bool openViaApplicationPicker = false) { // Don't open files and folders inside recycle bin - if (associatedInstance.ShellViewModel.WorkingDirectory.StartsWith(Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.Ordinal) || + if (associatedInstance.ShellViewModel is null || + associatedInstance.ShellViewModel.WorkingDirectory.StartsWith(Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.Ordinal) || associatedInstance.SlimContentPage?.SelectedItems is null) { return; @@ -319,7 +437,8 @@ public static async Task OpenSelectedItemsAsync(IShellPage associatedInstance, b public static async Task OpenItemsWithExecutableAsync(IShellPage associatedInstance, IEnumerable items, string executablePath) { // Don't open files and folders inside recycle bin - if (associatedInstance.ShellViewModel.WorkingDirectory.StartsWith(Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.Ordinal) || + if (associatedInstance.ShellViewModel is null || + associatedInstance.ShellViewModel.WorkingDirectory.StartsWith(Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.Ordinal) || associatedInstance.SlimContentPage is null) return; @@ -339,16 +458,10 @@ public static async Task OpenItemsWithExecutableAsync(IShellPage associatedInsta /// Open folders in a new tab regardless of the "OpenFoldersInNewTab" option public static async Task OpenPath(string path, IShellPage associatedInstance, FilesystemItemType? itemType = null, bool openSilent = false, bool openViaApplicationPicker = false, IEnumerable? selectItems = null, string? args = default, bool forceOpenInNewTab = false) { - string previousDir = associatedInstance.ShellViewModel.WorkingDirectory; - bool isHiddenItem = Win32Helper.HasFileAttribute(path, System.IO.FileAttributes.Hidden); - bool isDirectory = Win32Helper.HasFileAttribute(path, System.IO.FileAttributes.Directory); - bool isReparsePoint = Win32Helper.HasFileAttribute(path, System.IO.FileAttributes.ReparsePoint); - bool isShortcut = FileExtensionHelpers.IsShortcutOrUrlFile(path); - bool isScreenSaver = FileExtensionHelpers.IsScreenSaverFile(path); - bool isTag = path.StartsWith("tag:"); - FilesystemResult opened = (FilesystemResult)false; + if (associatedInstance.ShellViewModel is null) + return false; - if (isTag) + if (path.StartsWith("tag:")) { if (!forceOpenInNewTab) { @@ -369,50 +482,54 @@ public static async Task OpenPath(string path, IShellPage associatedInstan return true; } + string previousDir = associatedInstance.ShellViewModel.WorkingDirectory; + + var fileAttributes = Win32Helper.GetFileAttributes(path); + bool isDirectory = fileAttributes.HasFlag(System.IO.FileAttributes.Directory); + var shortcutInfo = new ShellLinkItem(); - if (itemType is null || isShortcut || isHiddenItem || isReparsePoint) - { - if (isShortcut) - { - var shInfo = await FileOperationsHelpers.ParseLinkAsync(path); - if (shInfo is null) - return false; + if (!isDirectory && FileExtensionHelpers.IsShortcutOrUrlFile(path)) + { + var shInfo = await FileOperationsHelpers.ParseLinkAsync(path); - itemType = shInfo.IsFolder ? FilesystemItemType.Directory : FilesystemItemType.File; + if (shInfo is null) + return false; - shortcutInfo = shInfo; + itemType = shInfo.IsFolder ? FilesystemItemType.Directory : FilesystemItemType.File; - if (shortcutInfo.InvalidTarget) - { - if (await DialogDisplayHelper.ShowDialogAsync(DynamicDialogFactory.GetFor_ShortcutNotFound(shortcutInfo.TargetPath)) != DynamicDialogResult.Primary) - return false; + shortcutInfo = shInfo; - // Delete shortcut - var shortcutItem = StorageHelpers.FromPathAndType(path, FilesystemItemType.File); - await associatedInstance.FilesystemHelpers.DeleteItemAsync(shortcutItem, DeleteConfirmationPolicies.Never, false, true); - } - } - else if (isReparsePoint) + if (shortcutInfo.InvalidTarget) { - if (!isDirectory && - Win32Helper.GetWin32FindDataForPath(path, out var findData) && - findData.dwReserved0 == Win32PInvoke.IO_REPARSE_TAG_SYMLINK) - { - shortcutInfo.TargetPath = Win32Helper.ParseSymLink(path); - } - itemType ??= isDirectory ? FilesystemItemType.Directory : FilesystemItemType.File; - } - else if (isHiddenItem) - { - itemType = Win32Helper.HasFileAttribute(path, System.IO.FileAttributes.Directory) ? FilesystemItemType.Directory : FilesystemItemType.File; + if (await DialogDisplayHelper.ShowDialogAsync(DynamicDialogFactory.GetFor_ShortcutNotFound(shortcutInfo.TargetPath)) != DynamicDialogResult.Primary) + return false; + + // Delete shortcut + var shortcutItem = StorageHelpers.FromPathAndType(path, FilesystemItemType.File); + await associatedInstance.FilesystemHelpers.DeleteItemAsync(shortcutItem, DeleteConfirmationPolicies.Never, false, true); } - else + } + else if (fileAttributes.HasFlag(System.IO.FileAttributes.ReparsePoint)) + { + if (!isDirectory && + Win32Helper.GetWin32FindDataForPath(path, out var findData) && + findData.dwReserved0 == Win32PInvoke.IO_REPARSE_TAG_SYMLINK) { - itemType = await StorageHelpers.GetTypeFromPath(path); + shortcutInfo.TargetPath = Win32Helper.ParseSymLink(path); } + itemType ??= isDirectory ? FilesystemItemType.Directory : FilesystemItemType.File; + } + else if (fileAttributes.HasFlag(System.IO.FileAttributes.Hidden)) + { + itemType = isDirectory ? FilesystemItemType.Directory : FilesystemItemType.File; + } + else if (itemType is null) + { + itemType = await StorageHelpers.GetTypeFromPath(path); } + FilesystemResult opened = (FilesystemResult)false; switch (itemType) { case FilesystemItemType.Library: @@ -425,16 +542,16 @@ public static async Task OpenPath(string path, IShellPage associatedInstan case FilesystemItemType.File: // Starts the screensaver in full-screen mode - if (isScreenSaver) + if (FileExtensionHelpers.IsScreenSaverFile(path)) args += "/s"; opened = await OpenFile(path, associatedInstance, shortcutInfo, openViaApplicationPicker, args); break; - }; + } if (opened.ErrorCode == FileSystemStatusCode.NotFound && !openSilent) { - await DialogDisplayHelper.ShowDialogAsync("FileNotFoundDialog/Title".GetLocalizedResource(), "FileNotFoundDialog/Text".GetLocalizedResource()); + await DialogDisplayHelper.ShowDialogAsync(Strings.FileNotFoundDialog_Title.GetLocalizedResource(), Strings.FileNotFoundDialog_Text.GetLocalizedResource()); associatedInstance.ToolbarViewModel.CanRefresh = false; associatedInstance.ShellViewModel?.RefreshItems(previousDir); } @@ -490,13 +607,16 @@ private static async Task OpenDirectory(string path, IShellPag } else { - opened = await associatedInstance.ShellViewModel.GetFolderWithPathFromPathAsync(path) - .OnSuccess((childFolder) => - { - // Add location to Recent Items List - if (childFolder.Item is SystemStorageFolder) - App.RecentItemsManager.AddToRecentItems(childFolder.Path); - }); + if (associatedInstance.ShellViewModel is not null) + { + opened = await associatedInstance.ShellViewModel.GetFolderWithPathFromPathAsync(path) + .OnSuccess((childFolder) => + { + // Add location to Recent Items List + if (childFolder.Item is SystemStorageFolder) + WindowsRecentItemsService.Add(childFolder.Path); + }); + } if (!opened) opened = (FilesystemResult)FolderHelpers.CheckFolderAccessWithWin32(path); @@ -522,12 +642,12 @@ private static async Task OpenFile(string path, IShellPage ass } else { - if (!FileExtensionHelpers.IsWebLinkFile(path)) + if (!FileExtensionHelpers.IsWebLinkFile(path) && associatedInstance.ShellViewModel is not null) { StorageFileWithPath childFile = await associatedInstance.ShellViewModel.GetFileWithPathFromPathAsync(shortcutInfo.TargetPath); // Add location to Recent Items List if (childFile?.Item is SystemStorageFile) - App.RecentItemsManager.AddToRecentItems(childFile.Path); + WindowsRecentItemsService.Add(childFile.Path); } await Win32Helper.InvokeWin32ComponentAsync(shortcutInfo.TargetPath, associatedInstance, $"{args} {shortcutInfo.Arguments}", shortcutInfo.RunAsAdmin, shortcutInfo.WorkingDirectory); } @@ -539,105 +659,113 @@ private static async Task OpenFile(string path, IShellPage ass } else { - opened = await associatedInstance.ShellViewModel.GetFileWithPathFromPathAsync(path) - .OnSuccess(async childFile => - { - // Add location to Recent Items List - if (childFile.Item is SystemStorageFile) - App.RecentItemsManager.AddToRecentItems(childFile.Path); + if (associatedInstance.ShellViewModel is not null) + { + var shellViewModel = associatedInstance.ShellViewModel; - if (openViaApplicationPicker) - { - LauncherOptions options = InitializeWithWindow(new LauncherOptions - { - DisplayApplicationPicker = true - }); - if (!await Launcher.LaunchFileAsync(childFile.Item, options)) - await ContextMenu.InvokeVerb("openas", path); - } - else + opened = await shellViewModel.GetFileWithPathFromPathAsync(path) + .OnSuccess(async childFile => { - //try using launcher first - bool launchSuccess = false; + // Add location to Recent Items List + if (childFile.Item is SystemStorageFile) + WindowsRecentItemsService.Add(childFile.Path); - BaseStorageFileQueryResult? fileQueryResult = null; - - //Get folder to create a file query (to pass to apps like Photos, Movies & TV..., needed to scroll through the folder like what Windows Explorer does) - BaseStorageFolder currentFolder = await associatedInstance.ShellViewModel.GetFolderFromPathAsync(PathNormalization.GetParentDir(path)); - - if (currentFolder is not null) + if (openViaApplicationPicker) { - QueryOptions queryOptions = new(CommonFileQuery.DefaultQuery, null); - - //We can have many sort entries - SortEntry sortEntry = new() + LauncherOptions options = InitializeWithWindow(new LauncherOptions { - AscendingOrder = associatedInstance.InstanceViewModel.FolderSettings.DirectorySortDirection == SortDirection.Ascending - }; - - //Basically we tell to the launched app to follow how we sorted the files in the directory. - var sortOption = associatedInstance.InstanceViewModel.FolderSettings.DirectorySortOption; + DisplayApplicationPicker = true + }); + if (!await Launcher.LaunchFileAsync(childFile.Item, options)) + await ContextMenu.InvokeVerb("openas", path); + } + else + { + var fileExtension = Path.GetExtension(path); - switch (sortOption) + // Use NeighboringFilesQuery to launch photos + // The query options no longer work with the Windows 11 Photo App but they still work for Windows 10 + if (FileExtensionHelpers.IsImageFile(fileExtension)) { - case SortOption.Name: - sortEntry.PropertyName = "System.ItemNameDisplay"; - queryOptions.SortOrder.Clear(); - queryOptions.SortOrder.Add(sortEntry); - break; - - case SortOption.DateModified: - sortEntry.PropertyName = "System.DateModified"; - queryOptions.SortOrder.Clear(); - queryOptions.SortOrder.Add(sortEntry); - break; - - case SortOption.DateCreated: - sortEntry.PropertyName = "System.DateCreated"; - queryOptions.SortOrder.Clear(); - queryOptions.SortOrder.Add(sortEntry); - break; - - //Unfortunately this is unsupported | Remarks: https://learn.microsoft.com/uwp/api/windows.storage.search.queryoptions.sortorder?view=winrt-19041 - //case Enums.SortOption.Size: - - //sortEntry.PropertyName = "System.TotalFileSize"; - //queryOptions.SortOrder.Clear(); - //queryOptions.SortOrder.Add(sortEntry); - //break; - - //Unfortunately this is unsupported | Remarks: https://learn.microsoft.com/uwp/api/windows.storage.search.queryoptions.sortorder?view=winrt-19041 - //case Enums.SortOption.FileType: - - //sortEntry.PropertyName = "System.FileExtension"; - //queryOptions.SortOrder.Clear(); - //queryOptions.SortOrder.Add(sortEntry); - //break; - - //Handle unsupported - default: - //keep the default one in SortOrder IList - break; + //try using launcher first + bool launchSuccess = false; + + // The Windows 11 Photos app ignores NeighboringFilesQuery when launched as default app. + // Use the app URI only when this extension is associated with Microsoft Photos. + if (FileAssociationHelpers.IsMicrosoftPhotosDefaultAssociation(fileExtension)) + { + string uri = $"ms-photos:viewer?fileName={Uri.EscapeDataString(path)}"; + launchSuccess = await Launcher.LaunchUriAsync(new Uri(uri)); + } + + BaseStorageFileQueryResult? fileQueryResult = null; + //Get folder to create a file query (to pass to apps like Photos, Movies & TV..., needed to scroll through the folder like what Windows Explorer does) + BaseStorageFolder currentFolder = await shellViewModel.GetFolderFromPathAsync(PathNormalization.GetParentDir(path)); + if (!launchSuccess && currentFolder is not null) + { + QueryOptions queryOptions = new(CommonFileQuery.DefaultQuery, null); + //We can have many sort entries + SortEntry sortEntry = new() + { + AscendingOrder = associatedInstance.InstanceViewModel.FolderSettings.DirectorySortDirection == SortDirection.Ascending + }; + //Basically we tell to the launched app to follow how we sorted the files in the directory. + var sortOption = associatedInstance.InstanceViewModel.FolderSettings.DirectorySortOption; + switch (sortOption) + { + case SortOption.Name: + sortEntry.PropertyName = "System.ItemNameDisplay"; + queryOptions.SortOrder.Clear(); + queryOptions.SortOrder.Add(sortEntry); + break; + case SortOption.DateModified: + sortEntry.PropertyName = "System.DateModified"; + queryOptions.SortOrder.Clear(); + queryOptions.SortOrder.Add(sortEntry); + break; + case SortOption.DateCreated: + sortEntry.PropertyName = "System.DateCreated"; + queryOptions.SortOrder.Clear(); + queryOptions.SortOrder.Add(sortEntry); + break; + //Unfortunately this is unsupported | Remarks: https://learn.microsoft.com/uwp/api/windows.storage.search.queryoptions.sortorder?view=winrt-19041 + //case Enums.SortOption.Size: + //sortEntry.PropertyName = "System.TotalFileSize"; + //queryOptions.SortOrder.Clear(); + //queryOptions.SortOrder.Add(sortEntry); + //break; + //Unfortunately this is unsupported | Remarks: https://learn.microsoft.com/uwp/api/windows.storage.search.queryoptions.sortorder?view=winrt-19041 + //case Enums.SortOption.FileType: + //sortEntry.PropertyName = "System.FileExtension"; + //queryOptions.SortOrder.Clear(); + //queryOptions.SortOrder.Add(sortEntry); + //break; + //Handle unsupported + default: + //keep the default one in SortOrder IList + break; + } + var options = InitializeWithWindow(new LauncherOptions()); + if (currentFolder.AreQueryOptionsSupported(queryOptions)) + { + fileQueryResult = currentFolder.CreateFileQueryWithOptions(queryOptions); + options.NeighboringFilesQuery = fileQueryResult.ToStorageFileQueryResult(); + } + // Now launch file with options. + var storageItem = (StorageFile)await FilesystemTasks.Wrap(() => childFile.Item.ToStorageFileAsync().AsTask()); + if (storageItem is not null) + launchSuccess = await Launcher.LaunchFileAsync(storageItem, options); + } + if (!launchSuccess) + await Win32Helper.InvokeWin32ComponentAsync(path, associatedInstance, args); } - - var options = InitializeWithWindow(new LauncherOptions()); - if (currentFolder.AreQueryOptionsSupported(queryOptions)) + else { - fileQueryResult = currentFolder.CreateFileQueryWithOptions(queryOptions); - options.NeighboringFilesQuery = fileQueryResult.ToStorageFileQueryResult(); + await Win32Helper.InvokeWin32ComponentAsync(path, associatedInstance, args); } - - // Now launch file with options. - var storageItem = (StorageFile)await FilesystemTasks.Wrap(() => childFile.Item.ToStorageFileAsync().AsTask()); - - if (storageItem is not null) - launchSuccess = await Launcher.LaunchFileAsync(storageItem, options); } - - if (!launchSuccess) - await Win32Helper.InvokeWin32ComponentAsync(path, associatedInstance, args); - } - }); + }); + } } return opened; } diff --git a/src/Files.App/Helpers/Navigation/NavigationInteractionTracker.cs b/src/Files.App/Helpers/Navigation/NavigationInteractionTracker.cs index ab2eaa4a65c6..5c6886a794cf 100644 --- a/src/Files.App/Helpers/Navigation/NavigationInteractionTracker.cs +++ b/src/Files.App/Helpers/Navigation/NavigationInteractionTracker.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Composition; using Microsoft.UI.Composition.Interactions; @@ -12,7 +12,7 @@ namespace Files.App.Helpers { - internal sealed class NavigationInteractionTracker : IDisposable + internal sealed partial class NavigationInteractionTracker : IDisposable { public bool CanNavigateForward { @@ -175,7 +175,7 @@ public void Dispose() GC.SuppressFinalize(this); } - private sealed class InteractionTrackerOwner : IInteractionTrackerOwner + private sealed partial class InteractionTrackerOwner : IInteractionTrackerOwner { private NavigationInteractionTracker _parent; private bool _shouldBounceBack; @@ -221,7 +221,7 @@ public void IdleStateEntered(InteractionTracker sender, InteractionTrackerIdleSt } } else - { + { _parent._tracker.TryUpdatePositionWithAnimation(_returnAnimation); } _shouldBounceBack = false; diff --git a/src/Files.App/Helpers/PathNormalization.cs b/src/Files.App/Helpers/PathNormalization.cs index c91c1ab6d1e3..de584ea07db0 100644 --- a/src/Files.App/Helpers/PathNormalization.cs +++ b/src/Files.App/Helpers/PathNormalization.cs @@ -1,8 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.Extensions.Logging; -using System; using System.IO; namespace Files.App.Helpers @@ -17,7 +16,7 @@ public static string GetPathRoot(string path) string rootPath = string.Empty; try { - var pathAsUri = new Uri(path.Replace("\\", "/", StringComparison.Ordinal)); + var pathAsUri = new Uri(path.Replace('\\', '/')); rootPath = pathAsUri.GetLeftPart(UriPartial.Authority); if (pathAsUri.IsFile && !string.IsNullOrEmpty(rootPath)) rootPath = new Uri(rootPath).LocalPath; @@ -54,7 +53,7 @@ public static string NormalizePath(string path) } catch (Exception ex) when (ex is UriFormatException || ex is ArgumentException) { - App.Logger.LogWarning(ex, path); + App.Logger.LogDebug(ex, path); return path; } } @@ -69,7 +68,7 @@ public static string GetParentDir(string path) if (string.IsNullOrEmpty(path)) return string.Empty; - var index = path.Contains('/', StringComparison.Ordinal) ? path.LastIndexOf("/", StringComparison.Ordinal) : path.LastIndexOf("\\", StringComparison.Ordinal); + var index = path.Contains('/', StringComparison.Ordinal) ? path.LastIndexOf('/') : path.LastIndexOf('\\'); return path.Substring(0, index != -1 ? index : path.Length); } @@ -78,7 +77,16 @@ public static string Combine(string folder, string name) if (string.IsNullOrEmpty(folder)) return name; - return folder.Contains('/', StringComparison.Ordinal) ? Path.Combine(folder, name).Replace("\\", "/", StringComparison.Ordinal) : Path.Combine(folder, name); + // Handle case where name is a rooted path (e.g., "E:\") + if (Path.IsPathRooted(name)) + { + var root = Path.GetPathRoot(name); + if (!string.IsNullOrEmpty(root) && name.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) == root.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)) + // Just use the drive letter + name = root.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar, ':'); + } + + return folder.Contains('/', StringComparison.Ordinal) ? Path.Combine(folder, name).Replace('\\', '/') : Path.Combine(folder, name); } } } \ No newline at end of file diff --git a/src/Files.App/Helpers/RegexHelpers.cs b/src/Files.App/Helpers/RegexHelpers.cs index 788652921478..309dfb6f7785 100644 --- a/src/Files.App/Helpers/RegexHelpers.cs +++ b/src/Files.App/Helpers/RegexHelpers.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Text.RegularExpressions; @@ -9,22 +9,22 @@ public static partial class RegexHelpers { [GeneratedRegex(@"\w:\w")] public static partial Regex AlternateStream(); - + [GeneratedRegex("(?<=^[^\"]*(?:\"[^\"]*\"[^\"]*)*) (?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)")] public static partial Regex SpaceSplit(); - + [GeneratedRegex(@"^\\\\\?\\[^\\]*\\?")] public static partial Regex WindowsPath(); - + [GeneratedRegex(@"^[A-Z]:\\\$Recycle\.Bin\\", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)] public static partial Regex RecycleBinPath(); - + [GeneratedRegex(@"^(?!/)(?!.*//)[^\000-\037\177 ~^:?*[]+(?!.*\.\.)(?!.*@\{)(?!.*\\)(? itemsToShare) { var errorDialog = new ContentDialog() { - Title = "FaildToShareItems".GetLocalizedResource(), + Title = Strings.FaildToShareItems.GetLocalizedResource(), Content = ex.Message, - PrimaryButtonText = "OK".GetLocalizedResource(), + PrimaryButtonText = Strings.OK.GetLocalizedResource(), }; if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) @@ -59,12 +54,12 @@ async void Manager_DataRequested(DataTransferManager sender, DataRequestedEventA foreach (ListedItem item in itemsToShare) { - if (item is ShortcutItem shItem) + if (item is IShortcutItem shItem) { if (shItem.IsLinkItem && !string.IsNullOrEmpty(shItem.TargetPath)) { - dataRequest.Data.Properties.Title = string.Format("ShareDialogTitle".GetLocalizedResource(), item.Name); - dataRequest.Data.Properties.Description = "ShareDialogSingleItemDescription".GetLocalizedResource(); + dataRequest.Data.Properties.Title = string.Format(Strings.ShareDialogTitle.GetLocalizedResource(), item.Name); + dataRequest.Data.Properties.Description = Strings.ShareDialogSingleItemDescription.GetLocalizedResource(); dataRequest.Data.SetWebLink(new Uri(shItem.TargetPath)); dataRequestDeferral.Complete(); @@ -85,12 +80,12 @@ async void Manager_DataRequested(DataTransferManager sender, DataRequestedEventA if (items.Count == 1) { - dataRequest.Data.Properties.Title = string.Format("ShareDialogTitle".GetLocalizedResource(), items.First().Name); - dataRequest.Data.Properties.Description = "ShareDialogSingleItemDescription".GetLocalizedResource(); + dataRequest.Data.Properties.Title = string.Format(Strings.ShareDialogTitle.GetLocalizedResource(), items.First().Name); + dataRequest.Data.Properties.Description = Strings.ShareDialogSingleItemDescription.GetLocalizedResource(); } else if (items.Count == 0) { - dataRequest.FailWithDisplayText("ShareDialogFailMessage".GetLocalizedResource()); + dataRequest.FailWithDisplayText(Strings.ShareDialogFailMessage.GetLocalizedResource()); dataRequestDeferral.Complete(); return; @@ -98,10 +93,10 @@ async void Manager_DataRequested(DataTransferManager sender, DataRequestedEventA else { dataRequest.Data.Properties.Title = string.Format( - "ShareDialogTitleMultipleItems".GetLocalizedResource(), + Strings.ShareDialogTitleMultipleItems.GetLocalizedResource(), items.Count, "ItemsCount.Text".GetLocalizedResource()); - dataRequest.Data.Properties.Description = "ShareDialogMultipleItemsDescription".GetLocalizedResource(); + dataRequest.Data.Properties.Description = Strings.ShareDialogMultipleItemsDescription.GetLocalizedResource(); } dataRequest.Data.SetStorageItems(items, false); diff --git a/src/Files.App/Helpers/StringsHelper.cs b/src/Files.App/Helpers/StringsHelper.cs new file mode 100644 index 000000000000..2ff6c7455bf0 --- /dev/null +++ b/src/Files.App/Helpers/StringsHelper.cs @@ -0,0 +1,6 @@ +namespace Files.App.Helpers +{ + public sealed partial class Strings + { + } +} diff --git a/src/Files.App/Helpers/TypesConverter.cs b/src/Files.App/Helpers/TypesConverter.cs index 22227f3dd1b4..f9c3213f3c1d 100644 --- a/src/Files.App/Helpers/TypesConverter.cs +++ b/src/Files.App/Helpers/TypesConverter.cs @@ -1,9 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System; -using System.Text.Json; -using System.Text.Json.Serialization; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Helpers { diff --git a/src/Files.App/Helpers/UI/AppSystemBackdrop.cs b/src/Files.App/Helpers/UI/AppSystemBackdrop.cs index 091cfc0da6ba..725ee9cfb778 100644 --- a/src/Files.App/Helpers/UI/AppSystemBackdrop.cs +++ b/src/Files.App/Helpers/UI/AppSystemBackdrop.cs @@ -7,7 +7,7 @@ namespace Files.App.Helpers { - internal sealed class AppSystemBackdrop : SystemBackdrop + internal sealed partial class AppSystemBackdrop : SystemBackdrop { private bool isSecondaryWindow; private IUserSettingsService userSettingsService; @@ -131,7 +131,7 @@ private void SetThinAcrylicBackdropProperties(DesktopAcrylicController controlle // This sets all properties to work around other properties not updating when fallback color is changed // This uses the Thin Acrylic recipe from the WinUI Figma toolkit - switch(theme) + switch (theme) { case SystemBackdropTheme.Light: controller.TintColor = Color.FromArgb(0xff, 0xd3, 0xd3, 0xd3); diff --git a/src/Files.App/Helpers/UI/AppThemeResourcesHelper.cs b/src/Files.App/Helpers/UI/AppThemeResourcesHelper.cs index 12c9b00a93f5..427ee02d0bef 100644 --- a/src/Files.App/Helpers/UI/AppThemeResourcesHelper.cs +++ b/src/Files.App/Helpers/UI/AppThemeResourcesHelper.cs @@ -1,10 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using CommunityToolkit.WinUI.Helpers; -using Files.App.Services; -using Files.App.Services.Settings; -using System; namespace Files.App.Helpers { @@ -26,19 +23,19 @@ public static void LoadAppResources(this IResourcesService service, IAppearanceS try { - service.SetAppThemeBackgroundColor(ColorHelper.ToColor(appThemeBackgroundColor).FromWindowsColor()); + service.SetAppThemeBackgroundColor(appThemeBackgroundColor.ToColor()); } catch { appearance.AppThemeBackgroundColor = "#00000000"; // reset to default - service.SetAppThemeBackgroundColor(ColorHelper.ToColor("#00000000").FromWindowsColor()); + service.SetAppThemeBackgroundColor("#00000000".ToColor()); } if (!string.IsNullOrWhiteSpace(appThemeAddressBarBackgroundColor)) { try { - service.SetAppThemeAddressBarBackgroundColor(ColorHelper.ToColor(appThemeAddressBarBackgroundColor).FromWindowsColor()); + service.SetAppThemeAddressBarBackgroundColor(appThemeAddressBarBackgroundColor.ToColor()); } catch { @@ -50,7 +47,7 @@ public static void LoadAppResources(this IResourcesService service, IAppearanceS { try { - service.SetAppThemeToolbarBackgroundColor(ColorHelper.ToColor(appThemeToolbarBackgroundColor).FromWindowsColor()); + service.SetAppThemeToolbarBackgroundColor(appThemeToolbarBackgroundColor.ToColor()); } catch { @@ -62,7 +59,7 @@ public static void LoadAppResources(this IResourcesService service, IAppearanceS { try { - service.SetAppThemeSidebarBackgroundColor(ColorHelper.ToColor(appThemeSidebarBackgroundColor).FromWindowsColor()); + service.SetAppThemeSidebarBackgroundColor(appThemeSidebarBackgroundColor.ToColor()); } catch { @@ -74,7 +71,7 @@ public static void LoadAppResources(this IResourcesService service, IAppearanceS { try { - service.SetAppThemeFileAreaBackgroundColor(ColorHelper.ToColor(appThemeFileAreaBackgroundColor).FromWindowsColor()); + service.SetAppThemeFileAreaBackgroundColor(appThemeFileAreaBackgroundColor.ToColor()); } catch { @@ -86,7 +83,7 @@ public static void LoadAppResources(this IResourcesService service, IAppearanceS { try { - service.SetAppThemeFileAreaSecondaryBackgroundColor(ColorHelper.ToColor(appThemeFileAreaSecondaryBackgroundColor).FromWindowsColor()); + service.SetAppThemeFileAreaSecondaryBackgroundColor(appThemeFileAreaSecondaryBackgroundColor.ToColor()); } catch { @@ -98,7 +95,7 @@ public static void LoadAppResources(this IResourcesService service, IAppearanceS { try { - service.SetAppThemeInfoPaneBackgroundColor(ColorHelper.ToColor(appThemeInfoPaneBackgroundColor).FromWindowsColor()); + service.SetAppThemeInfoPaneBackgroundColor(appThemeInfoPaneBackgroundColor.ToColor()); } catch { diff --git a/src/Files.App/Helpers/UI/DragZoneHelper.cs b/src/Files.App/Helpers/UI/DragZoneHelper.cs index fedfdee7ad16..dbdcb3d9ae59 100644 --- a/src/Files.App/Helpers/UI/DragZoneHelper.cs +++ b/src/Files.App/Helpers/UI/DragZoneHelper.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Windows.Graphics; using Microsoft.UI.Input; using Microsoft.UI.Xaml; +using Windows.Graphics; namespace Files.App.Helpers { diff --git a/src/Files.App/Helpers/UI/UIFilesystemHelpers.cs b/src/Files.App/Helpers/UI/UIFilesystemHelpers.cs index 875390362052..0d819b7b109c 100644 --- a/src/Files.App/Helpers/UI/UIFilesystemHelpers.cs +++ b/src/Files.App/Helpers/UI/UIFilesystemHelpers.cs @@ -1,14 +1,14 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.Dialogs; -using Files.App.Storage.Storables; using Microsoft.Extensions.Logging; using System.IO; using System.Net; using System.Text; using Windows.ApplicationModel.DataTransfer; using Windows.Storage; +using Windows.Win32.UI.WindowsAndMessaging; namespace Files.App.Helpers { @@ -26,6 +26,26 @@ public static async Task PasteItemAsync(string destinationPath, IShellPage assoc } } + public static async Task PasteItemAsShortcutAsync(string destinationPath, IShellPage associatedInstance) + { + FilesystemResult packageView = await FilesystemTasks.Wrap(() => Task.FromResult(Clipboard.GetContent())); + if (packageView.Result.Contains(StandardDataFormats.StorageItems)) + { + var items = await packageView.Result.GetStorageItemsAsync(); + await Task.WhenAll(items.Select(async item => + { + var fileName = FilesystemHelpers.GetShortcutNamingPreference(item.Name); + var filePath = Path.Combine(destinationPath ?? string.Empty, fileName); + + if (!await FileOperationsHelpers.CreateOrUpdateLinkAsync(filePath, item.Path)) + await HandleShortcutCannotBeCreated(fileName, item.Path); + })); + } + + if (associatedInstance is not null) + await associatedInstance.RefreshIfNoWatcherExistsAsync(); + } + public static async Task RenameFileItemAsync(ListedItem item, string newName, IShellPage associatedInstance, bool showExtensionDialog = true) { if (item is AlternateStreamItem ads) // For alternate streams Name is not a substring ItemNameRaw @@ -87,7 +107,7 @@ public static async Task CreateFileFromDialogResultTypeAsync(AddItemDialogItemTy string? userInput = null; if (itemType != AddItemDialogItemType.File || itemInfo?.Command is null) { - DynamicDialog dialog = DynamicDialogFactory.GetFor_RenameDialog(); + DynamicDialog dialog = DynamicDialogFactory.GetFor_CreateItemDialog(itemType.ToString().GetLocalizedResource().ToLower(), itemInfo?.Name); await dialog.TryShowAsync(); // Show rename dialog if (dialog.DynamicResult != DynamicDialogResult.Primary) @@ -101,14 +121,14 @@ public static async Task CreateFileFromDialogResultTypeAsync(AddItemDialogItemTy switch (itemType) { case AddItemDialogItemType.Folder: - userInput = !string.IsNullOrWhiteSpace(userInput) ? userInput : "NewFolder".GetLocalizedResource(); + userInput = !string.IsNullOrWhiteSpace(userInput) ? userInput : Strings.NewFolder.GetLocalizedResource(); created = await associatedInstance.FilesystemHelpers.CreateAsync( StorageHelpers.FromPathAndType(PathNormalization.Combine(currentPath, userInput), FilesystemItemType.Directory), true); break; case AddItemDialogItemType.File: - userInput = !string.IsNullOrWhiteSpace(userInput) ? userInput : itemInfo?.Name ?? "NewFile".GetLocalizedResource(); + userInput = !string.IsNullOrWhiteSpace(userInput) ? userInput : itemInfo?.Name ?? Strings.NewFile.GetLocalizedResource(); created = await associatedInstance.FilesystemHelpers.CreateAsync( StorageHelpers.FromPathAndType(PathNormalization.Combine(currentPath, userInput + itemInfo?.Extension), FilesystemItemType.File), true); @@ -117,13 +137,16 @@ public static async Task CreateFileFromDialogResultTypeAsync(AddItemDialogItemTy // Add newly created item to recent files list if (created.Status == ReturnResult.Success && created.Item?.Path is not null) - App.RecentItemsManager.AddToRecentItems(created.Item.Path); + { + IWindowsRecentItemsService windowsRecentItemsService = Ioc.Default.GetRequiredService(); + windowsRecentItemsService.Add(created.Item.Path); + } else if (created.Status == ReturnResult.AccessUnauthorized) { await DialogDisplayHelper.ShowDialogAsync ( - "AccessDenied".GetLocalizedResource(), - "AccessDeniedCreateDialog/Text".GetLocalizedResource() + Strings.AccessDenied.GetLocalizedResource(), + Strings.AccessDeniedCreateDialog_Text.GetLocalizedResource() ); } @@ -198,42 +221,38 @@ public static async Task CreateShortcutFromDialogAsync(IShellPage associatedInst if (result != DialogResult.Primary || viewModel.ShortcutCreatedSuccessfully) return; - await HandleShortcutCannotBeCreated(viewModel.ShortcutCompleteName, viewModel.DestinationItemPath); + await HandleShortcutCannotBeCreated(viewModel.ShortcutCompleteName, viewModel.FullPath, viewModel.Arguments); await associatedInstance.RefreshIfNoWatcherExistsAsync(); } - public static async Task HandleShortcutCannotBeCreated(string shortcutName, string destinationPath) + public static async Task HandleShortcutCannotBeCreated(string shortcutName, string destinationPath, string arguments = "") { var result = await DialogDisplayHelper.ShowDialogAsync ( - "CannotCreateShortcutDialogTitle".ToLocalized(), - "CannotCreateShortcutDialogMessage".ToLocalized(), - "Create".ToLocalized(), - "Cancel".ToLocalized() + Strings.CannotCreateShortcutDialogTitle.ToLocalized(), + Strings.CannotCreateShortcutDialogMessage.ToLocalized(), + Strings.Create.ToLocalized(), + Strings.Cancel.ToLocalized() ); if (!result) return false; var shortcutPath = Path.Combine(Constants.UserEnvironmentPaths.DesktopPath, shortcutName); - return await FileOperationsHelpers.CreateOrUpdateLinkAsync(shortcutPath, destinationPath); + return await FileOperationsHelpers.CreateOrUpdateLinkAsync(shortcutPath, destinationPath, arguments); } /// /// Updates ListedItem properties for a shortcut /// - /// - /// - /// - /// - /// - public static void UpdateShortcutItemProperties(ShortcutItem item, string targetPath, string arguments, string workingDir, bool runAsAdmin) + public static void UpdateShortcutItemProperties(IShortcutItem item, string targetPath, string arguments, string workingDir, bool runAsAdmin, SHOW_WINDOW_CMD showWindowCommand) { item.TargetPath = Environment.ExpandEnvironmentVariables(targetPath); item.Arguments = arguments; item.WorkingDirectory = workingDir; item.RunAsAdmin = runAsAdmin; + item.ShowWindowCommand = showWindowCommand; } public async static Task RequestPassword(IPasswordProtectedItem sender) diff --git a/src/Files.App/Helpers/UI/UIHelpers.cs b/src/Files.App/Helpers/UI/UIHelpers.cs index ba71fe889557..5307297fc968 100644 --- a/src/Files.App/Helpers/UI/UIHelpers.cs +++ b/src/Files.App/Helpers/UI/UIHelpers.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.Helpers.Application; using Microsoft.UI.Xaml.Controls; @@ -49,8 +49,8 @@ public static async Task ShowDeviceEjectResultAsync(Data.Items.DriveType type, b Debug.WriteLine("Can't eject device"); await DialogDisplayHelper.ShowDialogAsync( - "EjectNotificationErrorDialogHeader".GetLocalizedResource(), - "EjectNotificationErrorDialogBody".GetLocalizedResource()); + Strings.EjectNotificationErrorDialogHeader.GetLocalizedResource(), + Strings.EjectNotificationErrorDialogBody.GetLocalizedResource()); } } @@ -92,21 +92,22 @@ private static ContentDialog SetContentDialogRoot(ContentDialog contentDialog) public static void CloseAllDialogs() { + if (MainWindow.Instance?.Content?.XamlRoot == null) + return; + var openedDialogs = VisualTreeHelper.GetOpenPopupsForXamlRoot(MainWindow.Instance.Content.XamlRoot); foreach (var item in openedDialogs) - { if (item.Child is ContentDialog dialog) - { dialog.Hide(); - } - } } private static IEnumerable SidebarIconResources = LoadSidebarIconResources(); private static IconFileInfo ShieldIconResource = LoadShieldIconResource(); + private static IconFileInfo SearchIconResource = LoadSearchIconResource(); + public static IconFileInfo GetSidebarIconResourceInfo(int index) { var icons = UIHelpers.SidebarIconResources; @@ -129,6 +130,13 @@ public static IconFileInfo GetSidebarIconResourceInfo(int index) : null; } + public static async Task GetSearchIconResource() + { + return SearchIconResource is not null + ? await SearchIconResource.IconData.ToBitmapAsync() + : null; + } + private static IEnumerable LoadSidebarIconResources() { string imageres = Path.Combine(Constants.UserEnvironmentPaths.SystemRootPath, "System32", "imageres.dll"); @@ -152,7 +160,17 @@ private static IconFileInfo LoadShieldIconResource() Constants.ImageRes.ShieldIcon }, 16); - return imageResList.First(); + return imageResList.FirstOrDefault(); + } + + private static IconFileInfo LoadSearchIconResource() + { + string imageres = Path.Combine(Constants.UserEnvironmentPaths.SystemRootPath, "System32", "imageres.dll"); + var imageResList = Win32Helper.ExtractSelectedIconsFromDLL(imageres, new List() { + Constants.ImageRes.SearchIcon + }, 48); + + return imageResList.FirstOrDefault(); } } -} \ No newline at end of file +} diff --git a/src/Files.App/Helpers/WMI/ManagementEventWatcher.cs b/src/Files.App/Helpers/WMI/ManagementEventWatcher.cs index 09a470019743..318d2b0b225b 100644 --- a/src/Files.App/Helpers/WMI/ManagementEventWatcher.cs +++ b/src/Files.App/Helpers/WMI/ManagementEventWatcher.cs @@ -1,10 +1,8 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.Management.Infrastructure; using Microsoft.Management.Infrastructure.Generic; -using System; -using System.Threading; namespace Files.App.Helpers { @@ -17,7 +15,7 @@ namespace Files.App.Helpers /// Original Sourced from: https://codereview.stackexchange.com/questions/255055/trying-to-replace-managementeventwatcher-class-in-system-management-to-switch-to /// Adapted to newer versions of MMI /// - public class ManagementEventWatcher : IDisposable, IObserver + public partial class ManagementEventWatcher : IDisposable, IObserver { internal enum CimWatcherStatus { @@ -51,10 +49,7 @@ public ManagementEventWatcher(WqlEventQuery query) { string queryExpression = query.QueryExpression; - if (string.IsNullOrWhiteSpace(queryExpression)) - { - throw new ArgumentNullException(nameof(queryExpression)); - } + ArgumentNullException.ThrowIfNullOrWhiteSpace(queryExpression); _nameSpace = DefaultNameSpace; _queryDialect = DefaultQueryDialect; @@ -70,10 +65,7 @@ public ManagementEventWatcher(WqlEventQuery query) /// public ManagementEventWatcher(string queryDialect, string queryExpression) { - if (string.IsNullOrWhiteSpace(queryExpression)) - { - throw new ArgumentNullException(nameof(queryExpression)); - } + ArgumentNullException.ThrowIfNullOrWhiteSpace(queryExpression); _nameSpace = DefaultNameSpace; _queryDialect = queryDialect ?? DefaultQueryDialect; @@ -90,10 +82,7 @@ public ManagementEventWatcher(string queryDialect, string queryExpression) /// public ManagementEventWatcher(string nameSpace, string queryDialect, string queryExpression) { - if (string.IsNullOrWhiteSpace(queryExpression)) - { - throw new ArgumentNullException(nameof(queryExpression)); - } + ArgumentNullException.ThrowIfNullOrWhiteSpace(queryExpression); _nameSpace = nameSpace ?? DefaultNameSpace; _queryDialect = queryDialect ?? DefaultQueryDialect; @@ -111,10 +100,7 @@ public ManagementEventWatcher(string nameSpace, string queryDialect, string quer /// public ManagementEventWatcher(string computerName, string nameSpace, string queryDialect, string queryExpression) { - if (string.IsNullOrWhiteSpace(queryExpression)) - { - throw new ArgumentNullException(nameof(queryExpression)); - } + ArgumentNullException.ThrowIfNullOrWhiteSpace(queryExpression); _computerName = computerName; _nameSpace = nameSpace ?? DefaultNameSpace; @@ -160,10 +146,7 @@ public void Start() { lock (_myLock) { - if (_isDisposed) - { - throw new ObjectDisposedException(nameof(ManagementEventWatcher)); - } + ObjectDisposedException.ThrowIf(_isDisposed, this); if (_cimWatcherStatus != CimWatcherStatus.Default && _cimWatcherStatus != CimWatcherStatus.Stopped) { @@ -180,10 +163,7 @@ public void Stop() { lock (_myLock) { - if (_isDisposed) - { - throw new ObjectDisposedException(nameof(ManagementEventWatcher)); - } + ObjectDisposedException.ThrowIf(_isDisposed, this); if (_cimWatcherStatus != CimWatcherStatus.Started) { diff --git a/src/Files.App/Helpers/WMI/WqlEventQuery.cs b/src/Files.App/Helpers/WMI/WqlEventQuery.cs index d5388b6c7680..fd39fae39de5 100644 --- a/src/Files.App/Helpers/WMI/WqlEventQuery.cs +++ b/src/Files.App/Helpers/WMI/WqlEventQuery.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Helpers { diff --git a/src/Files.App/Helpers/Win32/Win32Helper.Process.cs b/src/Files.App/Helpers/Win32/Win32Helper.Process.cs index 1f73eeb9d795..e0a1c78a6ccb 100644 --- a/src/Files.App/Helpers/Win32/Win32Helper.Process.cs +++ b/src/Files.App/Helpers/Win32/Win32Helper.Process.cs @@ -1,10 +1,6 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Microsoft.Extensions.Logging; -using Windows.Foundation.Metadata; -using Windows.System; - namespace Files.App.Helpers { /// @@ -12,33 +8,6 @@ namespace Files.App.Helpers /// public static partial class Win32Helper { - private static bool? isRunningOnArm = null; - public static bool IsRunningOnArm - { - get - { - // https://stackoverflow.com/questions/54456140/how-to-detect-were-running-under-the-arm64-version-of-windows-10-in-net - // https://learn.microsoft.com/windows/win32/sysinfo/image-file-machine-constants - if (isRunningOnArm is null) - { - isRunningOnArm = IsArmProcessor(); - App.Logger.LogInformation("Running on ARM: {0}", isRunningOnArm); - } - - return isRunningOnArm ?? false; - } - } - - private static bool? isHasThreadAccessPropertyPresent = null; - public static bool IsHasThreadAccessPropertyPresent - { - get - { - isHasThreadAccessPropertyPresent ??= ApiInformation.IsPropertyPresent(typeof(DispatcherQueue).FullName, "HasThreadAccess"); - return isHasThreadAccessPropertyPresent ?? false; - } - } - public static async Task InvokeWin32ComponentAsync(string applicationPath, IShellPage associatedInstance, string arguments = null, bool runAsAdmin = false, string workingDirectory = null) { return await InvokeWin32ComponentsAsync(applicationPath.CreateEnumerable(), associatedInstance, arguments, runAsAdmin, workingDirectory); @@ -157,23 +126,5 @@ public static List WhoIsLocking(string[] resources) return processes; } - - private static bool IsArmProcessor() - { - var handle = Process.GetCurrentProcess().Handle; - if (!Win32PInvoke.IsWow64Process2(handle, out _, out var nativeMachine)) - return false; - - return - nativeMachine == 0xaa64 || - nativeMachine == 0x01c0 || - nativeMachine == 0x01c2 || - nativeMachine == 0x01c4; - } - - public static Task GetFileAssociationAsync(string filePath) - { - return GetFileAssociationAsync(filePath, true); - } } } diff --git a/src/Files.App/Helpers/Win32/Win32Helper.Shell.cs b/src/Files.App/Helpers/Win32/Win32Helper.Shell.cs index 2e9202ffbc15..0368aba3a2a0 100644 --- a/src/Files.App/Helpers/Win32/Win32Helper.Shell.cs +++ b/src/Files.App/Helpers/Win32/Win32Helper.Shell.cs @@ -13,10 +13,6 @@ namespace Files.App.Helpers /// public static partial class Win32Helper { - private readonly static ShellFolder _controlPanel = new(Shell32.KNOWNFOLDERID.FOLDERID_ControlPanelFolder); - - private readonly static ShellFolder _controlPanelCategoryView = new("::{26EE0668-A00A-44D7-9371-BEB064C98683}"); - public static async Task<(ShellFileItem Folder, List Enumerate)> GetShellFolderAsync(string path, bool getFolder, bool getEnumerate, int from, int count, params string[] properties) { if (path.StartsWith("::{", StringComparison.Ordinal)) @@ -24,7 +20,7 @@ public static partial class Win32Helper path = $"shell:{path}"; } - return await Win32Helper.StartSTATask(() => + return await STATask.Run(() => { var flc = new List(); var folder = (ShellFileItem)null; @@ -32,6 +28,8 @@ public static partial class Win32Helper try { using var shellFolder = ShellFolderExtensions.GetShellItemFromPathOrPIDL(path) as ShellFolder; + using ShellFolder _controlPanel = new(Shell32.KNOWNFOLDERID.FOLDERID_ControlPanelFolder); + using ShellFolder _controlPanelCategoryView = new("::{26EE0668-A00A-44D7-9371-BEB064C98683}"); if (shellFolder is null || (_controlPanel.PIDL.IsParentOf(shellFolder.PIDL, false) || @@ -77,26 +75,17 @@ public static partial class Win32Helper } return (folder, flc); - }); + }, App.Logger); } - public static (bool HasRecycleBin, long NumItems, long BinSize) QueryRecycleBin(string drive = "") + public static string GetFolderFromKnownFolderGUID(Guid guid) { - Win32PInvoke.SHQUERYRBINFO queryBinInfo = new Win32PInvoke.SHQUERYRBINFO(); - queryBinInfo.cbSize = Marshal.SizeOf(queryBinInfo); - - var res = Win32PInvoke.SHQueryRecycleBin(drive, ref queryBinInfo); - if (res == HRESULT.S_OK) - { - var numItems = queryBinInfo.i64NumItems; - var binSize = queryBinInfo.i64Size; + nint pszPath; + Win32PInvoke.SHGetKnownFolderPath(guid, 0, nint.Zero, out pszPath); + string path = Marshal.PtrToStringUni(pszPath); + Marshal.FreeCoTaskMem(pszPath); - return (true, numItems, binSize); - } - else - { - return (false, 0, 0); - } + return path; } } } diff --git a/src/Files.App/Helpers/Win32/Win32Helper.Storage.cs b/src/Files.App/Helpers/Win32/Win32Helper.Storage.cs index 41406ad7d6a3..3b5c9c4be07f 100644 --- a/src/Files.App/Helpers/Win32/Win32Helper.Storage.cs +++ b/src/Files.App/Helpers/Win32/Win32Helper.Storage.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. See the LICENSE. using Microsoft.Extensions.Logging; +using Microsoft.Win32; using Microsoft.Win32.SafeHandles; using System.Collections.Concurrent; using System.Drawing; @@ -13,6 +14,11 @@ using System.Windows.Forms; using Vanara.PInvoke; using Windows.System; +using Windows.Win32; +using Windows.Win32.Storage.FileSystem; +using COMPRESSION_FORMAT = Windows.Win32.Storage.FileSystem.COMPRESSION_FORMAT; +using HRESULT = Vanara.PInvoke.HRESULT; +using HWND = Vanara.PInvoke.HWND; namespace Files.App.Helpers { @@ -21,165 +27,15 @@ namespace Files.App.Helpers /// public static partial class Win32Helper { - public static Task StartSTATask(Func func) + public static async Task GetDefaultFileAssociationAsync(string filename, bool checkDesktopFirst = true) { - var taskCompletionSource = new TaskCompletionSource(); - Thread thread = new Thread(async () => - { - Ole32.OleInitialize(); - - try - { - await func(); - taskCompletionSource.SetResult(); - } - catch (Exception ex) - { - taskCompletionSource.SetResult(); - App.Logger.LogWarning(ex, ex.Message); - } - finally - { - Ole32.OleUninitialize(); - } - }) - - { - IsBackground = true, - Priority = ThreadPriority.Normal - }; - - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - - return taskCompletionSource.Task; - } - - public static Task StartSTATask(Action action) - { - var taskCompletionSource = new TaskCompletionSource(); - Thread thread = new Thread(() => - { - Ole32.OleInitialize(); - - try - { - action(); - taskCompletionSource.SetResult(); - } - catch (Exception ex) - { - taskCompletionSource.SetResult(); - App.Logger.LogWarning(ex, ex.Message); - } - finally - { - Ole32.OleUninitialize(); - } - }) - - { - IsBackground = true, - Priority = ThreadPriority.Normal - }; - - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - - return taskCompletionSource.Task; - } - - public static Task StartSTATask(Func func) - { - var taskCompletionSource = new TaskCompletionSource(); + // check if there exists an user choice first + var userChoice = GetUserChoiceFileAssociation(filename); + if (!string.IsNullOrEmpty(userChoice)) + return userChoice; - Thread thread = new Thread(() => - { - Ole32.OleInitialize(); + return await GetFileAssociationAsync(filename, checkDesktopFirst); - try - { - taskCompletionSource.SetResult(func()); - } - catch (Exception ex) - { - taskCompletionSource.SetResult(default); - App.Logger.LogWarning(ex, ex.Message); - //tcs.SetException(e); - } - finally - { - Ole32.OleUninitialize(); - } - }) - - { - IsBackground = true, - Priority = ThreadPriority.Normal - }; - - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - - return taskCompletionSource.Task; - } - - public static Task StartSTATask(Func> func) - { - var taskCompletionSource = new TaskCompletionSource(); - - Thread thread = new Thread(async () => - { - Ole32.OleInitialize(); - try - { - taskCompletionSource.SetResult(await func()); - } - catch (Exception ex) - { - taskCompletionSource.SetResult(default); - App.Logger.LogInformation(ex, ex.Message); - //tcs.SetException(e); - } - finally - { - Ole32.OleUninitialize(); - } - }) - - { - IsBackground = true, - Priority = ThreadPriority.Normal - }; - - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - - return taskCompletionSource.Task; - } - - public static async Task GetFileAssociationAsync(string filename, bool checkDesktopFirst = false) - { - // Find UWP apps - async Task GetUwpAssoc() - { - var uwpApps = await Launcher.FindFileHandlersAsync(Path.GetExtension(filename)); - return uwpApps.Any() ? uwpApps[0].PackageFamilyName : null; - } - - // Find desktop apps - string? GetDesktopAssoc() - { - var lpResult = new StringBuilder(2048); - var hResult = Shell32.FindExecutable(filename, null, lpResult); - - return hResult.ToInt64() > 32 ? lpResult.ToString() : null; - } - - if (checkDesktopFirst) - return GetDesktopAssoc() ?? await GetUwpAssoc(); - - return await GetUwpAssoc() ?? GetDesktopAssoc(); } public static string ExtractStringFromDLL(string file, int number) @@ -312,7 +168,7 @@ public static string ExtractStringFromDLL(string file, int number) if (iconOptions.HasFlag(IconOptions.ReturnOnlyIfCached)) flags |= Shell32.SIIGBF.SIIGBF_INCACHEONLY; - var hres = shellFactory.GetImage(new SIZE(size, size), flags, out var hbitmap); + var hres = shellFactory.GetImage(new Vanara.PInvoke.SIZE(size, size), flags, out var hbitmap); if (hres == HRESULT.S_OK) { using var image = GetBitmapFromHBitmap(hbitmap); @@ -324,7 +180,7 @@ public static string ExtractStringFromDLL(string file, int number) } if (iconData is not null || iconOptions.HasFlag(IconOptions.ReturnThumbnailOnly)) - return iconData; + return iconData; else { var shfi = new Shell32.SHFILEINFO(); @@ -333,7 +189,7 @@ public static string ExtractStringFromDLL(string file, int number) // Cannot access file, use file attributes var useFileAttibutes = iconData is null; - var ret = Shell32.SHGetFileInfo(path, isFolder ? FileAttributes.Directory : 0, ref shfi, Shell32.SHFILEINFO.Size, flags); + var ret = Shell32.SHGetFileInfo(path, isFolder ? FileAttributes.Directory : 0, ref shfi, Shell32.SHFILEINFO.Size, flags); if (ret == IntPtr.Zero) return iconData; @@ -398,9 +254,9 @@ public static string ExtractStringFromDLL(string file, int number) } } - public static async Task RunPowershellCommandAsync(string command, bool runAsAdmin) + public static async Task RunPowershellCommandAsync(string command, PowerShellExecutionOptions options, string? workingDirectory = null) { - using Process process = CreatePowershellProcess(command, runAsAdmin); + using Process process = CreatePowershellProcess(command, options, workingDirectory); using var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(30 * 1000)); try @@ -425,11 +281,11 @@ public static async Task RunPowershellCommandAsync(string command, bool ru } } - public static bool RunPowershellCommand(string command, bool runAsAdmin) + public static bool RunPowershellCommand(string command, PowerShellExecutionOptions options, string? workingDirectory = null) { try { - using Process process = CreatePowershellProcess(command, runAsAdmin); + using Process process = CreatePowershellProcess(command, options, workingDirectory); process.Start(); @@ -544,24 +400,24 @@ public static Task OpenFormatDriveDialog(string drive) { // Format requires elevation int driveIndex = drive.ToUpperInvariant()[0] - 'A'; - return RunPowershellCommandAsync($"-command \"$Signature = '[DllImport(\\\"shell32.dll\\\", SetLastError = false)]public static extern uint SHFormatDrive(IntPtr hwnd, uint drive, uint fmtID, uint options);'; $SHFormatDrive = Add-Type -MemberDefinition $Signature -Name \"Win32SHFormatDrive\" -Namespace Win32Functions -PassThru; $SHFormatDrive::SHFormatDrive(0, {driveIndex}, 0xFFFF, 0x0001)\"", true); + return RunPowershellCommandAsync($"-command \"$Signature = '[DllImport(\\\"shell32.dll\\\", SetLastError = false)]public static extern uint SHFormatDrive(IntPtr hwnd, uint drive, uint fmtID, uint options);'; $SHFormatDrive = Add-Type -MemberDefinition $Signature -Name \"Win32SHFormatDrive\" -Namespace Win32Functions -PassThru; $SHFormatDrive::SHFormatDrive(0, {driveIndex}, 0xFFFF, 0x0001)\"", PowerShellExecutionOptions.Elevated | PowerShellExecutionOptions.Hidden); } public static void SetVolumeLabel(string drivePath, string newLabel) { // Rename requires elevation - RunPowershellCommand($"-command \"$Signature = '[DllImport(\\\"kernel32.dll\\\", SetLastError = false)]public static extern bool SetVolumeLabel(string lpRootPathName, string lpVolumeName);'; $SetVolumeLabel = Add-Type -MemberDefinition $Signature -Name \"Win32SetVolumeLabel\" -Namespace Win32Functions -PassThru; $SetVolumeLabel::SetVolumeLabel('{drivePath}', '{newLabel}')\"", true); + RunPowershellCommand($"-command \"$Signature = '[DllImport(\\\"kernel32.dll\\\", SetLastError = false)]public static extern bool SetVolumeLabel(string lpRootPathName, string lpVolumeName);'; $SetVolumeLabel = Add-Type -MemberDefinition $Signature -Name \"Win32SetVolumeLabel\" -Namespace Win32Functions -PassThru; $SetVolumeLabel::SetVolumeLabel('{drivePath}', '{newLabel}')\"", PowerShellExecutionOptions.Elevated | PowerShellExecutionOptions.Hidden); } public static void SetNetworkDriveLabel(string driveName, string newLabel) { - RunPowershellCommand($"-command \"(New-Object -ComObject Shell.Application).NameSpace('{driveName}').Self.Name='{newLabel}'\"", false); + RunPowershellCommand($"-command \"(New-Object -ComObject Shell.Application).NameSpace('{driveName}').Self.Name='{newLabel}'\"", PowerShellExecutionOptions.Hidden); } public static Task MountVhdDisk(string vhdPath) { // Mounting requires elevation - return RunPowershellCommandAsync($"-command \"Mount-DiskImage -ImagePath '{vhdPath}'\"", true); + return RunPowershellCommandAsync($"-command \"Mount-DiskImage -ImagePath '{vhdPath}'\"", PowerShellExecutionOptions.Elevated | PowerShellExecutionOptions.Hidden); } public static Bitmap? GetBitmapFromHBitmap(HBITMAP hBitmap) @@ -632,7 +488,7 @@ private static bool IsAlphaBitmap(BitmapData bmpData) Color pixelColor = Color.FromArgb( Marshal.ReadInt32(bmpData.Scan0, (bmpData.Stride * y) + (4 * x))); - if (pixelColor.A > 0 & pixelColor.A < 255) + if (pixelColor.A < 255) return true; } } @@ -850,29 +706,36 @@ public static async Task InstallFontsAsync(string[] fontFilePaths, bool forAllUs if (psCommand.Length + appendCommand.Length > 32766) { // The command is too long to run at once, so run the command once up to this point. - await RunPowershellCommandAsync(psCommand.Append("\"").ToString(), true); + await RunPowershellCommandAsync(psCommand.Append('"').ToString(), PowerShellExecutionOptions.Elevated | PowerShellExecutionOptions.Hidden); psCommand.Clear().Append("-command \""); } psCommand.Append(appendCommand); } - await RunPowershellCommandAsync(psCommand.Append("\"").ToString(), true); + await RunPowershellCommandAsync(psCommand.Append('"').ToString(), PowerShellExecutionOptions.Elevated | PowerShellExecutionOptions.Hidden); } - private static Process CreatePowershellProcess(string command, bool runAsAdmin) + private static Process CreatePowershellProcess(string command, PowerShellExecutionOptions options, string? workingDirectory = null) { Process process = new(); - if (runAsAdmin) + process.StartInfo.FileName = "powershell.exe"; + if (options.HasFlag(PowerShellExecutionOptions.Elevated)) { process.StartInfo.UseShellExecute = true; process.StartInfo.Verb = "runas"; } - process.StartInfo.FileName = "powershell.exe"; - process.StartInfo.CreateNoWindow = true; - process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; + if (options.HasFlag(PowerShellExecutionOptions.Hidden)) + { + process.StartInfo.CreateNoWindow = true; + process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; + } + + if (workingDirectory is not null) + process.StartInfo.WorkingDirectory = workingDirectory; + process.StartInfo.Arguments = command; return process; @@ -881,35 +744,42 @@ private static Process CreatePowershellProcess(string command, bool runAsAdmin) public static SafeFileHandle CreateFileForWrite(string filePath, bool overwrite = true) { return new SafeFileHandle(Win32PInvoke.CreateFileFromApp(filePath, - Win32PInvoke.GENERIC_WRITE, 0, IntPtr.Zero, overwrite ? Win32PInvoke.CREATE_ALWAYS : Win32PInvoke.OPEN_ALWAYS, (uint)Win32PInvoke.File_Attributes.BackupSemantics, IntPtr.Zero), true); + (uint)FILE_ACCESS_RIGHTS.FILE_GENERIC_WRITE, 0, IntPtr.Zero, overwrite ? Win32PInvoke.CREATE_ALWAYS : Win32PInvoke.OPEN_ALWAYS, (uint)Win32PInvoke.File_Attributes.BackupSemantics, IntPtr.Zero), true); } public static SafeFileHandle OpenFileForRead(string filePath, bool readWrite = false, uint flags = 0) { return new SafeFileHandle(Win32PInvoke.CreateFileFromApp(filePath, - Win32PInvoke.GENERIC_READ | (readWrite ? Win32PInvoke.GENERIC_WRITE : 0), (uint)(Win32PInvoke.FILE_SHARE_READ | (readWrite ? 0 : Win32PInvoke.FILE_SHARE_WRITE)), IntPtr.Zero, Win32PInvoke.OPEN_EXISTING, (uint)Win32PInvoke.File_Attributes.BackupSemantics | flags, IntPtr.Zero), true); + (uint)FILE_ACCESS_RIGHTS.FILE_GENERIC_READ | (uint)(readWrite ? FILE_ACCESS_RIGHTS.FILE_GENERIC_WRITE : 0u), (uint)(Win32PInvoke.FILE_SHARE_READ | (readWrite ? 0 : Win32PInvoke.FILE_SHARE_WRITE)), IntPtr.Zero, Win32PInvoke.OPEN_EXISTING, (uint)Win32PInvoke.File_Attributes.BackupSemantics | flags, IntPtr.Zero), true); } - public static bool GetFileDateModified(string filePath, out FILETIME dateModified) + public static bool GetFileDateModified(string filePath, out System.Runtime.InteropServices.ComTypes.FILETIME dateModified) { - using var hFile = new SafeFileHandle(Win32PInvoke.CreateFileFromApp(filePath, Win32PInvoke.GENERIC_READ, Win32PInvoke.FILE_SHARE_READ, IntPtr.Zero, Win32PInvoke.OPEN_EXISTING, (uint)Win32PInvoke.File_Attributes.BackupSemantics, IntPtr.Zero), true); + using var hFile = new SafeFileHandle(Win32PInvoke.CreateFileFromApp(filePath, (uint)FILE_ACCESS_RIGHTS.FILE_GENERIC_READ, Win32PInvoke.FILE_SHARE_READ, IntPtr.Zero, Win32PInvoke.OPEN_EXISTING, (uint)Win32PInvoke.File_Attributes.BackupSemantics, IntPtr.Zero), true); return Win32PInvoke.GetFileTime(hFile.DangerousGetHandle(), out _, out _, out dateModified); } - public static bool SetFileDateModified(string filePath, FILETIME dateModified) + public static bool SetFileDateModified(string filePath, System.Runtime.InteropServices.ComTypes.FILETIME dateModified) { - using var hFile = new SafeFileHandle(Win32PInvoke.CreateFileFromApp(filePath, Win32PInvoke.FILE_WRITE_ATTRIBUTES, 0, IntPtr.Zero, Win32PInvoke.OPEN_EXISTING, (uint)Win32PInvoke.File_Attributes.BackupSemantics, IntPtr.Zero), true); + using var hFile = new SafeFileHandle(Win32PInvoke.CreateFileFromApp(filePath, (uint)FILE_ACCESS_RIGHTS.FILE_WRITE_ATTRIBUTES, 0, IntPtr.Zero, Win32PInvoke.OPEN_EXISTING, (uint)Win32PInvoke.File_Attributes.BackupSemantics, IntPtr.Zero), true); return Win32PInvoke.SetFileTime(hFile.DangerousGetHandle(), new(), new(), dateModified); } - public static bool HasFileAttribute(string lpFileName, FileAttributes dwAttrs) + public static FileAttributes GetFileAttributes(string lpFileName) { if (Win32PInvoke.GetFileAttributesExFromApp( lpFileName, Win32PInvoke.GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, out var lpFileInfo)) { - return (lpFileInfo.dwFileAttributes & dwAttrs) == dwAttrs; + return lpFileInfo.dwFileAttributes; } - return false; + return FileAttributes.None; + } + + public static bool HasFileAttribute(string lpFileName, FileAttributes dwAttrs) + { + Debug.Assert(dwAttrs != FileAttributes.None); + + return GetFileAttributes(lpFileName).HasFlag(dwAttrs); } public static bool SetFileAttribute(string lpFileName, FileAttributes dwAttrs) @@ -932,10 +802,62 @@ public static bool UnsetFileAttribute(string lpFileName, FileAttributes dwAttrs) return Win32PInvoke.SetFileAttributesFromApp(lpFileName, lpFileInfo.dwFileAttributes & ~dwAttrs); } + public static unsafe bool CanCompressContent(string path) + { + path = Path.GetPathRoot(path) ?? string.Empty; + uint dwFileSystemFlags = 0; + + var success = PInvoke.GetVolumeInformation( + path, + [], + out _, + out _, + out dwFileSystemFlags, + []); + + if (!success) + return false; + + return (dwFileSystemFlags & PInvoke.FILE_FILE_COMPRESSION) != 0; + } + + public static unsafe bool SetCompressionAttributeIoctl(string lpFileName, bool isCompressed) + { + // GENERIC_READ | GENERIC_WRITE flags are needed here + // FILE_FLAG_BACKUP_SEMANTICS is used to open directories + using var hFile = PInvoke.CreateFile( + lpFileName, + (uint)(FILE_ACCESS_RIGHTS.FILE_GENERIC_READ | FILE_ACCESS_RIGHTS.FILE_GENERIC_WRITE | FILE_ACCESS_RIGHTS.FILE_WRITE_ATTRIBUTES), + FILE_SHARE_MODE.FILE_SHARE_READ | FILE_SHARE_MODE.FILE_SHARE_WRITE, + lpSecurityAttributes: null, + FILE_CREATION_DISPOSITION.OPEN_EXISTING, + FILE_FLAGS_AND_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL | FILE_FLAGS_AND_ATTRIBUTES.FILE_FLAG_BACKUP_SEMANTICS, + hTemplateFile: null); + + if (hFile.IsInvalid) + return false; + + var bytesReturned = 0u; + var compressionFormat = isCompressed + ? COMPRESSION_FORMAT.COMPRESSION_FORMAT_DEFAULT + : COMPRESSION_FORMAT.COMPRESSION_FORMAT_NONE; + + var result = PInvoke.DeviceIoControl( + new(hFile.DangerousGetHandle()), + PInvoke.FSCTL_SET_COMPRESSION, + &compressionFormat, + sizeof(ushort), + null, + 0u, + &bytesReturned); + + return result; + } + public static string ReadStringFromFile(string filePath) { IntPtr hFile = Win32PInvoke.CreateFileFromApp(filePath, - Win32PInvoke.GENERIC_READ, + (uint)FILE_ACCESS_RIGHTS.FILE_GENERIC_READ, Win32PInvoke.FILE_SHARE_READ, IntPtr.Zero, Win32PInvoke.OPEN_EXISTING, @@ -984,7 +906,7 @@ public static string ReadStringFromFile(string filePath) public static bool WriteStringToFile(string filePath, string str, Win32PInvoke.File_Attributes flags = 0) { IntPtr hStream = Win32PInvoke.CreateFileFromApp(filePath, - Win32PInvoke.GENERIC_WRITE, 0, IntPtr.Zero, Win32PInvoke.CREATE_ALWAYS, (uint)(Win32PInvoke.File_Attributes.BackupSemantics | flags), IntPtr.Zero); + (uint)FILE_ACCESS_RIGHTS.FILE_GENERIC_WRITE, 0, IntPtr.Zero, Win32PInvoke.CREATE_ALWAYS, (uint)(Win32PInvoke.File_Attributes.BackupSemantics | flags), IntPtr.Zero); if (hStream.ToInt64() == -1) { return false; @@ -1146,5 +1068,98 @@ public static bool GetWin32FindDataForPath(string targetPath, out Win32PInvoke.W return false; } + + private static string? GetPackageFamilyNameFromAppRegistryName(string appRegistryName) + { + using var appXKey = Registry.ClassesRoot.OpenSubKey(appRegistryName + @"\Application"); + var appUserModelIdObj = appXKey?.GetValue("AppUserModelId"); + string? appUserModelId = appUserModelIdObj?.ToString(); + string? packageFamilyName = null; + if (!string.IsNullOrEmpty(appUserModelId)) + { + int bangIndex = appUserModelId.IndexOf('!'); + packageFamilyName = bangIndex > 0 ? appUserModelId[..bangIndex] : appUserModelId; + } + + return packageFamilyName; + } + + private static string? GetUserChoiceFileAssociation(string filename) + { + var fileExtension = Path.GetExtension(filename); + if (string.IsNullOrEmpty(filename)) + return null; + + try + { + // Get ProgId from UserChoice + using var userChoiceKey = Registry.CurrentUser.OpenSubKey($@"Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\{fileExtension}\UserChoice"); + var progIdObj = userChoiceKey?.GetValue("ProgId"); + string? progId = progIdObj?.ToString(); + + if (string.IsNullOrEmpty(progId)) + return null; + + // Get the package family name if it's an AppX app + if (progId.StartsWith("AppX", StringComparison.OrdinalIgnoreCase)) + { + string? packageFamilyName = GetPackageFamilyNameFromAppRegistryName(progId); + if (!string.IsNullOrEmpty(packageFamilyName)) + return packageFamilyName; + } + + // Find the open command for the ProgId + using var commandKey = Registry.ClassesRoot.OpenSubKey($@"{progId}\shell\open\command"); + var command = commandKey?.GetValue(null)?.ToString(); + + if (string.IsNullOrEmpty(command)) + return null; + + // Extract executable path from command string (e.g. "\"C:\\Program Files\\App\\app.exe\" \"%1\"") + var exePath = command.Trim(); + if (exePath.StartsWith("\"")) + { + int endQuote = exePath.IndexOf('\"', 1); + if (endQuote > 1) + exePath = exePath.Substring(1, endQuote - 1); + } + else + { + int firstSpace = exePath.IndexOf(' '); + if (firstSpace > 0) + exePath = exePath.Substring(0, firstSpace); + } + + return File.Exists(exePath) ? exePath : null; + } + catch + { + return null; + } + } + + private static async Task GetFileAssociationAsync(string filename, bool checkDesktopFirst = true) + { + // Find UWP apps + async Task GetUwpAssoc() + { + var uwpApps = await Launcher.FindFileHandlersAsync(Path.GetExtension(filename)); + return uwpApps.Any() ? uwpApps[0].PackageFamilyName : null; + } + + // Find desktop apps + string? GetDesktopAssoc() + { + var lpResult = new StringBuilder(2048); + var hResult = Shell32.FindExecutable(filename, null, lpResult); + + return hResult.ToInt64() > 32 ? lpResult.ToString() : null; + } + + if (checkDesktopFirst) + return GetDesktopAssoc() ?? await GetUwpAssoc(); + + return await GetUwpAssoc() ?? GetDesktopAssoc(); + } } } diff --git a/src/Files.App/Helpers/Win32/Win32Helper.WindowManagement.cs b/src/Files.App/Helpers/Win32/Win32Helper.WindowManagement.cs index 1b28a1f8717e..996c7af3bed7 100644 --- a/src/Files.App/Helpers/Win32/Win32Helper.WindowManagement.cs +++ b/src/Files.App/Helpers/Win32/Win32Helper.WindowManagement.cs @@ -1,13 +1,9 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Microsoft.UI.Input; -using Microsoft.UI.Xaml; -using System.Reflection; -using Vanara.PInvoke; +using System.Runtime.InteropServices; using Windows.Win32; using Windows.Win32.UI.WindowsAndMessaging; -using static Vanara.PInvoke.User32; namespace Files.App.Helpers { @@ -44,36 +40,14 @@ public static unsafe void BringToForegroundEx(Windows.Win32.Foundation.HWND hWnd } /// - /// Sets cursor when hovering on a specific element. + /// Force window to stay at bottom of other upper windows. /// - /// An element to be changed. - /// Cursor to change. - public static void ChangeCursor(this UIElement uiElement, InputCursor cursor) + /// The lParam of the message. + public static void ForceWindowPosition(nint lParam) { - Type type = typeof(UIElement); - - type.InvokeMember( - "ProtectedCursor", - BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty | BindingFlags.Instance, - null, - uiElement, - [cursor] - ); - } - - /// - /// Changes an attribute of the specified window. - /// - /// A handle to the window and, indirectly, the class to which the window belongs. - /// The zero-based offset to the value to be set. - /// The replacement value. - /// If the function succeeds, the return value is the previous value of the specified offset. - public static IntPtr SetWindowLong(HWND hWnd, WindowLongFlags nIndex, IntPtr dwNewLong) - { - return - IntPtr.Size == 4 - ? Win32PInvoke.SetWindowLongPtr32(hWnd, nIndex, dwNewLong) - : Win32PInvoke.SetWindowLongPtr64(hWnd, nIndex, dwNewLong); + var windowPos = Marshal.PtrToStructure(lParam); + windowPos.flags |= SET_WINDOW_POS_FLAGS.SWP_NOZORDER; + Marshal.StructureToPtr(windowPos, lParam, false); } } } diff --git a/src/Files.App/Helpers/Win32/Win32PInvoke.Consts.cs b/src/Files.App/Helpers/Win32/Win32PInvoke.Consts.cs index f1a573124ce9..d36433d37833 100644 --- a/src/Files.App/Helpers/Win32/Win32PInvoke.Consts.cs +++ b/src/Files.App/Helpers/Win32/Win32PInvoke.Consts.cs @@ -22,19 +22,14 @@ public static partial class Win32PInvoke public const int FILE_NOTIFY_CHANGE_SECURITY = 256; public const int INVALID_HANDLE_VALUE = -1; - public const uint GENERIC_READ = 0x80000000; - public const uint GENERIC_WRITE = 0x40000000; public const int FILE_SHARE_READ = 0x00000001; public const int FILE_SHARE_WRITE = 0x00000002; public const uint FILE_SHARE_DELETE = 0x00000004; public const int OPEN_EXISTING = 3; - public const int FSCTL_LOCK_VOLUME = 0x00090018; - public const int FSCTL_DISMOUNT_VOLUME = 0x00090020; public const int IOCTL_STORAGE_EJECT_MEDIA = 0x2D4808; public const int IOCTL_STORAGE_MEDIA_REMOVAL = 0x002D4804; public const uint FILE_APPEND_DATA = 0x0004; - public const uint FILE_WRITE_ATTRIBUTES = 0x100; public const uint FILE_BEGIN = 0; @@ -45,8 +40,10 @@ public static partial class Win32PInvoke public const uint OPEN_ALWAYS = 4; public const uint TRUNCATE_EXISTING = 5; - public const int MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16 * 1024; + // FSCTL public const int FSCTL_GET_REPARSE_POINT = 0x000900A8; + + public const int MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16 * 1024; public const uint IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003; public const uint IO_REPARSE_TAG_SYMLINK = 0xA000000C; diff --git a/src/Files.App/Helpers/Win32/Win32PInvoke.Enums.cs b/src/Files.App/Helpers/Win32/Win32PInvoke.Enums.cs index 55bb016bb843..c9d3acb14e80 100644 --- a/src/Files.App/Helpers/Win32/Win32PInvoke.Enums.cs +++ b/src/Files.App/Helpers/Win32/Win32PInvoke.Enums.cs @@ -81,88 +81,6 @@ public enum StreamInfoLevels FindStreamInfoStandard = 0 } - [Flags] - public enum CryptProtectFlags - { - CRYPTPROTECT_UI_FORBIDDEN = 0x1, - CRYPTPROTECT_LOCAL_MACHINE = 0x4, - CRYPTPROTECT_CRED_SYNC = 0x8, - CRYPTPROTECT_AUDIT = 0x10, - CRYPTPROTECT_NO_RECOVERY = 0x20, - CRYPTPROTECT_VERIFY_PROTECTION = 0x40, - CRYPTPROTECT_CRED_REGENERATE = 0x80 - } - - public enum TOKEN_INFORMATION_CLASS - { - TokenUser = 1, - TokenGroups, - TokenPrivileges, - TokenOwner, - TokenPrimaryGroup, - TokenDefaultDacl, - TokenSource, - TokenType, - TokenImpersonationLevel, - TokenStatistics, - TokenRestrictedSids, - TokenSessionId, - TokenGroupsAndPrivileges, - TokenSessionReference, - TokenSandBoxInert, - TokenAuditPolicy, - TokenOrigin, - TokenElevationType, - TokenLinkedToken, - TokenElevation, - TokenHasRestrictions, - TokenAccessInformation, - TokenVirtualizationAllowed, - TokenVirtualizationEnabled, - TokenIntegrityLevel, - TokenUIAccess, - TokenMandatoryPolicy, - TokenLogonSid, - TokenIsAppContainer, - TokenCapabilities, - TokenAppContainerSid, - TokenAppContainerNumber, - TokenUserClaimAttributes, - TokenDeviceClaimAttributes, - TokenRestrictedUserClaimAttributes, - TokenRestrictedDeviceClaimAttributes, - TokenDeviceGroups, - TokenRestrictedDeviceGroups, - TokenSecurityAttributes, - TokenIsRestricted - } - - [Serializable] - public enum TOKEN_TYPE - { - TokenPrimary = 1, - TokenImpersonation = 2 - } - - [Flags] - public enum TokenAccess : uint - { - TOKEN_ASSIGN_PRIMARY = 0x0001, - TOKEN_DUPLICATE = 0x0002, - TOKEN_IMPERSONATE = 0x0004, - TOKEN_QUERY = 0x0008, - TOKEN_QUERY_SOURCE = 0x0010, - TOKEN_ADJUST_PRIVILEGES = 0x0020, - TOKEN_ADJUST_GROUPS = 0x0040, - TOKEN_ADJUST_DEFAULT = 0x0080, - TOKEN_ADJUST_SESSIONID = 0x0100, - TOKEN_ALL_ACCESS_P = 0x000F00FF, - TOKEN_ALL_ACCESS = 0x000F01FF, - TOKEN_READ = 0x00020008, - TOKEN_WRITE = 0x000200E0, - TOKEN_EXECUTE = 0x00020000 - } - public enum FINDEX_INFO_LEVELS { FindExInfoStandard = 0, diff --git a/src/Files.App/Helpers/Win32/Win32PInvoke.Methods.cs b/src/Files.App/Helpers/Win32/Win32PInvoke.Methods.cs index 49624896c45c..0c214c2d2c49 100644 --- a/src/Files.App/Helpers/Win32/Win32PInvoke.Methods.cs +++ b/src/Files.App/Helpers/Win32/Win32PInvoke.Methods.cs @@ -1,12 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.IO; using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; using System.Text; -using Vanara.PInvoke; -using static Vanara.PInvoke.User32; +using Windows.Win32.Foundation; +using Windows.Win32.System.Com; namespace Files.App.Helpers { @@ -56,13 +55,6 @@ public static extern int RmGetList( ref uint lpdwRebootReasons ); - [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - public static extern bool SetPropW( - IntPtr hWnd, - string lpString, - IntPtr hData - ); - [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] public static extern IntPtr CreateEvent( IntPtr lpEventAttributes, @@ -76,34 +68,6 @@ public static extern bool SetEvent( IntPtr hEvent ); - [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - public static extern int GetDpiForWindow( - IntPtr hwnd - ); - - [DllImport("ole32.dll")] - public static extern uint CoWaitForMultipleObjects( - uint dwFlags, - uint dwMilliseconds, - ulong nHandles, - IntPtr[] pHandles, - out uint dwIndex - ); - - [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLong")] - public static extern int SetWindowLongPtr32( - HWND hWnd, - WindowLongFlags nIndex, - IntPtr dwNewLong - ); - - [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLongPtr")] - public static extern IntPtr SetWindowLongPtr64( - HWND hWnd, - WindowLongFlags nIndex, - IntPtr dwNewLong - ); - [DllImport("shell32.dll")] public static extern IntPtr SHBrowseForFolder( ref BROWSEINFO lpbi @@ -121,39 +85,12 @@ public static extern bool CloseHandle( IntPtr hObject ); - [DllImport("api-ms-win-core-io-l1-1-1.dll")] - public static extern bool GetOverlappedResult( - IntPtr hFile, - OVERLAPPED lpOverlapped, - out int lpNumberOfBytesTransferred, - bool bWait - ); - - [DllImport("api-ms-win-core-io-l1-1-1.dll")] - public static extern bool CancelIo( - IntPtr hFile - ); - [DllImport("api-ms-win-core-io-l1-1-1.dll")] public static extern bool CancelIoEx( IntPtr hFile, IntPtr lpOverlapped ); - [DllImport("api-ms-win-core-synch-l1-2-0.dll")] - public static extern uint WaitForMultipleObjectsEx( - uint nCount, - IntPtr[] lpHandles, - bool bWaitAll, - uint dwMilliseconds, - bool bAlertable - ); - - [DllImport("api-ms-win-core-synch-l1-2-0.dll", SetLastError = true)] - public static extern bool ResetEvent( - IntPtr hEvent - ); - [DllImport("api-ms-win-core-synch-l1-2-0.dll", SetLastError = true)] public static extern uint WaitForSingleObjectEx( IntPtr hHandle, @@ -221,16 +158,6 @@ public static extern bool DeviceIoControl( out uint lpBytesReturned, IntPtr lpOverlapped); - [DllImport("user32.dll")] - public static extern int ToUnicode( - uint virtualKeyCode, - uint scanCode, - byte[] keyboardState, - [Out, MarshalAs(UnmanagedType.LPWStr, SizeConst = 64)] StringBuilder receivingBuffer, - int bufferSize, - uint flags - ); - [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern int ToUnicodeEx( uint virtualKeyCode, @@ -242,28 +169,6 @@ public static extern int ToUnicodeEx( IntPtr keyboardLayout ); - [DllImport("user32.dll")] - public static extern bool GetKeyboardState( - byte[] lpKeyState - ); - - [DllImport("user32.dll", CharSet = CharSet.Auto)] - public static extern IntPtr GetKeyboardLayout - ( - uint idThread - ); - - [DllImport("user32.dll")] - public static extern uint MapVirtualKey( - uint code, - uint mapType - ); - - [DllImport("user32.dll")] - public static extern bool TranslateMessage( - ref MSG lpMsg - ); - [DllImport("api-ms-win-core-file-fromapp-l1-1-0.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)] public static extern IntPtr CreateFileFromApp( string lpFileName, @@ -288,14 +193,6 @@ public static extern bool SetFileAttributesFromApp( string lpFileName, FileAttributes dwFileAttributes); - [DllImport("api-ms-win-core-file-l1-2-1.dll", ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)] - public static extern uint SetFilePointer( - IntPtr hFile, - long lDistanceToMove, - IntPtr lpDistanceToMoveHigh, - uint dwMoveMethod - ); - [DllImport("api-ms-win-core-file-l1-2-1.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)] public unsafe static extern bool ReadFile( IntPtr hFile, @@ -325,17 +222,17 @@ public static extern bool WriteFileEx( [DllImport("api-ms-win-core-file-l1-2-1.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)] public static extern bool GetFileTime( [In] IntPtr hFile, - out FILETIME lpCreationTime, - out FILETIME lpLastAccessTime, - out FILETIME lpLastWriteTime + out System.Runtime.InteropServices.ComTypes.FILETIME lpCreationTime, + out System.Runtime.InteropServices.ComTypes.FILETIME lpLastAccessTime, + out System.Runtime.InteropServices.ComTypes.FILETIME lpLastWriteTime ); [DllImport("api-ms-win-core-file-l1-2-1.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)] public static extern bool SetFileTime( [In] IntPtr hFile, - in FILETIME lpCreationTime, - in FILETIME lpLastAccessTime, - in FILETIME lpLastWriteTime + in System.Runtime.InteropServices.ComTypes.FILETIME lpCreationTime, + in System.Runtime.InteropServices.ComTypes.FILETIME lpLastAccessTime, + in System.Runtime.InteropServices.ComTypes.FILETIME lpLastWriteTime ); [DllImport("api-ms-win-core-file-l2-1-1.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)] @@ -346,14 +243,6 @@ public static extern bool GetFileInformationByHandleEx( uint dwBufferSize ); - [DllImport("api-ms-win-core-file-l2-1-1.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)] - public static extern bool GetFileInformationByHandleEx( - IntPtr hFile, - FILE_INFO_BY_HANDLE_CLASS infoClass, - IntPtr dirInfo, - uint dwBufferSize - ); - [DllImport("kernel32.dll", ExactSpelling = true, CharSet = CharSet.Auto, SetLastError = true)] public static extern IntPtr FindFirstStreamW( string lpFileName, @@ -369,49 +258,6 @@ public static extern bool FindNextStreamW( [In, Out, MarshalAs(UnmanagedType.LPStruct)] WIN32_FIND_STREAM_DATA lpFindStreamData ); - [DllImport("Shcore.dll", SetLastError = true)] - public static extern int GetDpiForMonitor( - IntPtr hmonitor, - uint dpiType, - out uint dpiX, - out uint dpiY - ); - - [DllImport("api-ms-win-core-processthreads-l1-1-0.dll", SetLastError = true, ExactSpelling = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool OpenProcessToken( - [In] IntPtr ProcessHandle, TokenAccess DesiredAccess, out IntPtr TokenHandle); - - [DllImport("api-ms-win-core-processthreads-l1-1-2.dll", SetLastError = true, ExactSpelling = true)] - public static extern IntPtr GetCurrentProcess(); - - [DllImport("api-ms-win-security-base-l1-1-0.dll", SetLastError = true, ExactSpelling = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool GetTokenInformation( - IntPtr hObject, - TOKEN_INFORMATION_CLASS tokenInfoClass, - IntPtr pTokenInfo, - int tokenInfoLength, - out int returnLength - ); - - [DllImport("api-ms-win-security-base-l1-1-0.dll", ExactSpelling = true, SetLastError = true)] - public static extern int GetLengthSid( - IntPtr pSid - ); - - [DllImport("crypt32.dll", SetLastError = true, CharSet = CharSet.Auto)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool CryptUnprotectData( - in CRYPTOAPI_BLOB pDataIn, - StringBuilder szDataDescr, - in CRYPTOAPI_BLOB pOptionalEntropy, - IntPtr pvReserved, - IntPtr pPromptStruct, - CryptProtectFlags dwFlags, - out CRYPTOAPI_BLOB pDataOut - ); - [DllImport("api-ms-win-core-wow64-l1-1-1.dll", SetLastError = true)] public static extern bool IsWow64Process2( IntPtr process, @@ -432,7 +278,7 @@ IntPtr hFindFile [DllImport("api-ms-win-core-timezone-l1-1-0.dll", SetLastError = true)] public static extern bool FileTimeToSystemTime( - ref FILETIME lpFileTime, + ref System.Runtime.InteropServices.ComTypes.FILETIME lpFileTime, out SYSTEMTIME lpSystemTime ); @@ -446,19 +292,6 @@ public static extern IntPtr FindFirstFileExFromApp( int dwAdditionalFlags ); - [DllImport("api-ms-win-core-string-l1-1-0.dll", CharSet = CharSet.Unicode)] - public static extern int CompareStringEx( - string localeName, - int flags, - string str1, - int count1, - string str2, - int count2, - IntPtr versionInformation, - IntPtr reserved, - int param - ); - [LibraryImport("shell32.dll", EntryPoint = "#865", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static partial bool IsElevationRequired( @@ -497,27 +330,16 @@ public static extern uint RegisterApplicationRestart( int dwFlags ); - [DllImport(Lib.Shell32, SetLastError = false, CharSet = CharSet.Unicode)] - public static extern int SHQueryRecycleBin( - string pszRootPath, - ref SHQUERYRBINFO pSHQueryRBInfo - ); - [DllImport("shell32.dll")] - static extern int SHGetKnownFolderPath( + public static extern int SHGetKnownFolderPath( [MarshalAs(UnmanagedType.LPStruct)] Guid rfid, uint dwFlags, IntPtr hToken, out IntPtr pszPath ); - public static string GetFolderFromKnownFolderGUID(Guid guid) - { - IntPtr pPath; - SHGetKnownFolderPath(guid, 0, IntPtr.Zero, out pPath); - string path = Marshal.PtrToStringUni(pPath); - System.Runtime.InteropServices.Marshal.FreeCoTaskMem(pPath); - return path; - } + // cryptui.dll + [DllImport("cryptui.dll", SetLastError = true, CharSet = CharSet.Auto)] + public unsafe static extern bool CryptUIDlgViewSignerInfo(CRYPTUI_VIEWSIGNERINFO_STRUCT* pViewInfo); } } diff --git a/src/Files.App/Helpers/Win32/Win32PInvoke.Structs.cs b/src/Files.App/Helpers/Win32/Win32PInvoke.Structs.cs index e49ec32eb862..d1453d5cd285 100644 --- a/src/Files.App/Helpers/Win32/Win32PInvoke.Structs.cs +++ b/src/Files.App/Helpers/Win32/Win32PInvoke.Structs.cs @@ -3,7 +3,8 @@ using System.IO; using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; +using Windows.Win32.Foundation; +using Windows.Win32.Security.Cryptography; namespace Files.App.Helpers { @@ -90,9 +91,9 @@ public struct REPARSE_DATA_BUFFER public struct WIN32_FILE_ATTRIBUTE_DATA { public FileAttributes dwFileAttributes; - public FILETIME ftCreationTime; - public FILETIME ftLastAccessTime; - public FILETIME ftLastWriteTime; + public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime; + public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime; + public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime; public uint nFileSizeHigh; public uint nFileSizeLow; } @@ -139,12 +140,6 @@ public sealed class WIN32_FIND_STREAM_DATA public string cStreamName; } - [StructLayout(LayoutKind.Sequential)] - public struct TOKEN_USER - { - public SID_AND_ATTRIBUTES User; - } - [StructLayout(LayoutKind.Sequential)] public struct SID_AND_ATTRIBUTES { @@ -153,13 +148,6 @@ public struct SID_AND_ATTRIBUTES public uint Attributes; } - [StructLayout(LayoutKind.Sequential)] - public struct CRYPTOAPI_BLOB - { - public uint cbData; - public IntPtr pbData; - } - [StructLayout(LayoutKind.Sequential)] public struct SYSTEMTIME { @@ -196,9 +184,9 @@ public struct WIN32_FIND_DATA { public uint dwFileAttributes; - public FILETIME ftCreationTime; - public FILETIME ftLastAccessTime; - public FILETIME ftLastWriteTime; + public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime; + public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime; + public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime; public uint nFileSizeHigh; public uint nFileSizeLow; @@ -212,27 +200,36 @@ public struct WIN32_FIND_DATA public string cAlternateFileName; } - // There is usually no need to define Win32 COM interfaces/P-Invoke methods here. - // The Vanara library contains the definitions for all members of Shell32.dll, User32.dll and more - // The ones below are due to bugs in the current version of the library and can be removed once fixed - // Structure used by SHQueryRecycleBin. - [StructLayout(LayoutKind.Sequential, Pack = 0)] - public struct SHQUERYRBINFO + [StructLayout(LayoutKind.Sequential)] + public unsafe struct SignDataHandle { - public int cbSize; - public long i64Size; - public long i64NumItems; + public uint dwObjSize; + public CMSG_SIGNER_INFO* pSignerInfo; + public HCERTSTORE hCertStoreHandle; } [StructLayout(LayoutKind.Sequential)] - public struct MSG - { - public IntPtr hwnd; - public uint message; - public IntPtr wParam; - public IntPtr lParam; - public uint time; - public System.Drawing.Point pt; + public unsafe struct CRYPTOAPI_BLOB + { + public uint cbData; + public void* pbData; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public unsafe struct CRYPTUI_VIEWSIGNERINFO_STRUCT + { + public uint dwSize; + public HWND hwndParent; + public uint dwFlags; + public PCSTR szTitle; + public CMSG_SIGNER_INFO* pSignerInfo; + public void* hMsg; + public PCSTR pszOID; + public uint? dwReserved; + public uint cStores; + public HCERTSTORE* rghStores; + public uint cPropPages; + public void* rgPropPages; } } } diff --git a/src/Files.App/Helpers/Xaml/DependencyObjectHelpers.cs b/src/Files.App/Helpers/Xaml/DependencyObjectHelpers.cs index 3730be878b67..938bf6917776 100644 --- a/src/Files.App/Helpers/Xaml/DependencyObjectHelpers.cs +++ b/src/Files.App/Helpers/Xaml/DependencyObjectHelpers.cs @@ -1,10 +1,8 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Media; -using System; -using System.Collections.Generic; using System.Reflection; namespace Files.App.Helpers diff --git a/src/Files.App/Helpers/Xaml/SystemTypeToXaml.cs b/src/Files.App/Helpers/Xaml/SystemTypeToXaml.cs index 60d730e08f17..8ea8c858c5cb 100644 --- a/src/Files.App/Helpers/Xaml/SystemTypeToXaml.cs +++ b/src/Files.App/Helpers/Xaml/SystemTypeToXaml.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Markup; namespace Files.App.Helpers { - public sealed class SystemTypeToXaml : MarkupExtension + public sealed partial class SystemTypeToXaml : MarkupExtension { #region Private Members diff --git a/src/Files.App/MainWindow.xaml b/src/Files.App/MainWindow.xaml index c5a77c7490de..101a403aba79 100644 --- a/src/Files.App/MainWindow.xaml +++ b/src/Files.App/MainWindow.xaml @@ -1,9 +1,10 @@ - + diff --git a/src/Files.App/MainWindow.xaml.cs b/src/Files.App/MainWindow.xaml.cs index 0bd6c7279cbc..879710d37d3f 100644 --- a/src/Files.App/MainWindow.xaml.cs +++ b/src/Files.App/MainWindow.xaml.cs @@ -1,85 +1,79 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. +using Microsoft.Extensions.Logging; using Microsoft.UI; using Microsoft.UI.Windowing; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media.Animation; -using Microsoft.UI.Xaml.Navigation; +using System.IO; +using System.Runtime.InteropServices; +using Windows.ApplicationModel; using Windows.ApplicationModel.Activation; using Windows.Storage; -using WinUIEx; using IO = System.IO; namespace Files.App { - public sealed partial class MainWindow : WindowEx + public sealed partial class MainWindow : WinUIEx.WindowEx { private static MainWindow? _Instance; public static MainWindow Instance => _Instance ??= new(); - public IntPtr WindowHandle { get; } + public nint WindowHandle { get; } + private bool CanWindowToFront { get; set; } = true; + private readonly object _canWindowToFrontLock = new(); - private MainWindow() + public MainWindow() { - WindowHandle = this.GetWindowHandle(); - InitializeComponent(); - EnsureEarlyWindow(); - } - - private void EnsureEarlyWindow() - { - // Set PersistenceId + WindowHandle = WinUIEx.WindowExtensions.GetWindowHandle(this); + MinHeight = 316; + MinWidth = 416; + ExtendsContentIntoTitleBar = true; + Title = "Files"; PersistenceId = "FilesMainWindow"; - - // Set minimum sizes - MinHeight = 416; - MinWidth = 516; - - AppWindow.Title = "Files"; - AppWindow.SetIcon(AppLifecycleHelper.AppIconPath); - AppWindow.TitleBar.ExtendsContentIntoTitleBar = true; AppWindow.TitleBar.ButtonBackgroundColor = Colors.Transparent; AppWindow.TitleBar.ButtonInactiveBackgroundColor = Colors.Transparent; + AppWindow.TitleBar.ButtonPressedBackgroundColor = Colors.Transparent; + AppWindow.TitleBar.ButtonHoverBackgroundColor = Colors.Transparent; + AppWindow.SetIcon(AppLifecycleHelper.AppIconPath); - // Workaround for full screen window messing up the taskbar - // https://github.com/microsoft/microsoft-ui-xaml/issues/8431 - // This property should only be set if the "Automatically hide the taskbar" in Windows 11, - // or "Automatically hide the taskbar in desktop mode" in Windows 10 is enabled. - // Setting this property when the setting is disabled will result in the taskbar overlapping the application - if (AppLifecycleHelper.IsAutoHideTaskbarEnabled()) - Win32PInvoke.SetPropW(WindowHandle, "NonRudeHWND", new IntPtr(1)); + WinUIEx.WindowManager.Get(this).WindowMessageReceived += WindowManager_WindowMessageReceived; } public void ShowSplashScreen() { var rootFrame = EnsureWindowIsInitialized(); - rootFrame.Navigate(typeof(SplashScreenPage)); + rootFrame?.Navigate(typeof(SplashScreenPage)); } public async Task InitializeApplicationAsync(object activatedEventArgs) { + var rootFrame = EnsureWindowIsInitialized(); + + if (rootFrame is null) + return; + // Set system backdrop SystemBackdrop = new AppSystemBackdrop(); - var rootFrame = EnsureWindowIsInitialized(); - switch (activatedEventArgs) { case ILaunchActivatedEventArgs launchArgs: if (launchArgs.Arguments is not null && - (CommandLineParser.SplitArguments(launchArgs.Arguments, true)[0].EndsWith($"files.exe", StringComparison.OrdinalIgnoreCase) - || CommandLineParser.SplitArguments(launchArgs.Arguments, true)[0].EndsWith($"files", StringComparison.OrdinalIgnoreCase))) + (CommandLineParser.SplitArguments(launchArgs.Arguments, true)[0].EndsWith($"files-dev.exe", StringComparison.OrdinalIgnoreCase) + || CommandLineParser.SplitArguments(launchArgs.Arguments, true)[0].EndsWith($"files-dev", StringComparison.OrdinalIgnoreCase) + || CommandLineParser.SplitArguments(launchArgs.Arguments, true)[0].Equals(Path.Join(Package.Current.InstalledLocation.Path, "Files.exe"), StringComparison.OrdinalIgnoreCase))) { // WINUI3: When launching from commandline the argument is not ICommandLineActivatedEventArgs (#10370) var ppm = CommandLineParser.ParseUntrustedCommands(launchArgs.Arguments); if (ppm.IsEmpty()) rootFrame.Navigate(typeof(MainPage), null, new SuppressNavigationTransitionInfo()); else - await InitializeFromCmdLineArgsAsync(rootFrame, ppm); + await InitializeFromCmdLineArgsAsync(rootFrame, ppm, Environment.CurrentDirectory); } else if (rootFrame.Content is null || rootFrame.Content is SplashScreenPage || !MainPageViewModel.AppInstances.Any()) { @@ -101,7 +95,7 @@ public async Task InitializeApplicationAsync(object activatedEventArgs) break; case IProtocolActivatedEventArgs eventArgs: - if (eventArgs.Uri.AbsoluteUri == "files-uwp:") + if (eventArgs.Uri.AbsoluteUri == "files-dev:") { rootFrame.Navigate(typeof(MainPage), null, new SuppressNavigationTransitionInfo()); @@ -140,7 +134,7 @@ public async Task InitializeApplicationAsync(object activatedEventArgs) if (ppm.IsEmpty()) rootFrame.Navigate(typeof(MainPage), null, new SuppressNavigationTransitionInfo()); else - await InitializeFromCmdLineArgsAsync(rootFrame, ppm); + await InitializeFromCmdLineArgsAsync(rootFrame, ppm, Environment.CurrentDirectory); break; default: rootFrame.Navigate(typeof(MainPage), null, new SuppressNavigationTransitionInfo()); @@ -166,23 +160,39 @@ public async Task InitializeApplicationAsync(object activatedEventArgs) break; case IFileActivatedEventArgs fileArgs: - var index = 0; - if (rootFrame.Content is null || rootFrame.Content is SplashScreenPage || !MainPageViewModel.AppInstances.Any()) - { - // When the navigation stack isn't restored navigate to the first page, - // configuring the new page by passing required information as a navigation parameter - rootFrame.Navigate(typeof(MainPage), fileArgs.Files.First().Path, new SuppressNavigationTransitionInfo()); - index = 1; - } - else + try { - // Bring to foreground (#14730) - Win32Helper.BringToForegroundEx(new(WindowHandle)); - } + if (fileArgs.Files is null || fileArgs.Files.Count == 0) + { + break; + } - for (; index < fileArgs.Files.Count; index++) + var index = 0; + if (rootFrame.Content is null || rootFrame.Content is SplashScreenPage || !MainPageViewModel.AppInstances.Any()) + { + // When the navigation stack isn't restored navigate to the first page, + // configuring the new page by passing required information as a navigation parameter + rootFrame.Navigate(typeof(MainPage), fileArgs.Files.First().Path, new SuppressNavigationTransitionInfo()); + index = 1; + } + else + { + // Bring to foreground (#14730) + Win32Helper.BringToForegroundEx(new(WindowHandle)); + } + + for (; index < fileArgs.Files.Count; index++) + { + await NavigationHelpers.AddNewTabByPathAsync(typeof(ShellPanesPage), fileArgs.Files[index].Path, true); + } + } + catch (Exception ex) { - await NavigationHelpers.AddNewTabByPathAsync(typeof(ShellPanesPage), fileArgs.Files[index].Path, true); + App.Logger.LogWarning(ex, "Failed to open files."); + if (rootFrame.Content is null || rootFrame.Content is SplashScreenPage || !MainPageViewModel.AppInstances.Any()) + rootFrame.Navigate(typeof(MainPage), null, new SuppressNavigationTransitionInfo()); + else + Win32Helper.BringToForegroundEx(new(WindowHandle)); } break; @@ -202,38 +212,43 @@ public async Task InitializeApplicationAsync(object activatedEventArgs) // When resuming the cached instance AppWindow.Show(); Activate(); + + // Bring to foreground (#14730) in case Activate() doesn't + Win32Helper.BringToForegroundEx(new(WindowHandle)); } if (Windows.Win32.PInvoke.IsIconic(new(WindowHandle))) - Instance.Restore(); // Restore window if minimized + WinUIEx.WindowExtensions.Restore(Instance); // Restore window if minimized } - public Frame EnsureWindowIsInitialized() + private Frame? EnsureWindowIsInitialized() { - // NOTE: - // Do not repeat app initialization when the Window already has content, - // just ensure that the window is active - if (Instance.Content is not Frame rootFrame) + try { - // Create a Frame to act as the navigation context and navigate to the first page - rootFrame = new() { CacheSize = 1 }; - rootFrame.NavigationFailed += OnNavigationFailed; + // NOTE: + // Do not repeat app initialization when the Window already has content, + // just ensure that the window is active + if (Instance.Content is not Frame rootFrame) + { + // Create a Frame to act as the navigation context and navigate to the first page + rootFrame = new() { CacheSize = 1 }; + rootFrame.NavigationFailed += (s, e) => + { + throw new Exception("Failed to load Page " + e.SourcePageType.FullName); + }; - // Place the frame in the current Window - Instance.Content = rootFrame; - } + // Place the frame in the current Window + Instance.Content = rootFrame; + } - return rootFrame; + return rootFrame; + } + catch (COMException) + { + return null; + } } - /// - /// Invoked when Navigation to a certain page fails - /// - /// The Frame which failed navigation - /// Details about the navigation failure - private void OnNavigationFailed(object sender, NavigationFailedEventArgs e) - => throw new Exception("Failed to load Page " + e.SourcePageType.FullName); - private async Task InitializeFromCmdLineArgsAsync(Frame rootFrame, ParsedCommands parsedCommands, string activationPath = "") { async Task PerformNavigationAsync(string payload, string selectItem = null) @@ -247,11 +262,26 @@ async Task PerformNavigationAsync(string payload, string selectItem = null) } var generalSettingsService = Ioc.Default.GetService(); + + double boundsWidth = 0; + try + { + boundsWidth = Bounds.Width; + } + catch (Exception ex) + { + // Handle exception in case WinUI Windows is closed + // (see https://github.com/files-community/Files/issues/15599) + + App.Logger.LogWarning(ex, ex.Message); + return; + } + var paneNavigationArgs = new PaneNavigationArguments { LeftPaneNavPathParam = payload, LeftPaneSelectItemParam = selectItem, - RightPaneNavPathParam = Bounds.Width > Constants.UI.MultiplePaneWidthThreshold && (generalSettingsService?.AlwaysOpenDualPaneInNewTab ?? false) ? "Home" : null, + RightPaneNavPathParam = boundsWidth > Constants.UI.MultiplePaneWidthThreshold && (generalSettingsService?.AlwaysOpenDualPaneInNewTab ?? false) ? "Home" : null, }; if (rootFrame.Content is MainPage && MainPageViewModel.AppInstances.Any()) @@ -259,7 +289,17 @@ async Task PerformNavigationAsync(string payload, string selectItem = null) // Bring to foreground (#14730) Win32Helper.BringToForegroundEx(new(WindowHandle)); - await NavigationHelpers.AddNewTabByParamAsync(typeof(ShellPanesPage), paneNavigationArgs); + var existingTabIndex = MainPageViewModel.AppInstances + .Select((tabItem, idx) => new { tabItem, idx }) + .FirstOrDefault(x => + x.tabItem.NavigationParameter.NavigationParameter is PaneNavigationArguments paneArgs && + (paneNavigationArgs.LeftPaneNavPathParam == paneArgs.LeftPaneNavPathParam || + paneNavigationArgs.LeftPaneNavPathParam == paneArgs.RightPaneNavPathParam))?.idx ?? -1; + + if (existingTabIndex >= 0) + App.AppModel.TabStripSelectedIndex = existingTabIndex; + else + await NavigationHelpers.AddNewTabByParamAsync(typeof(ShellPanesPage), paneNavigationArgs); } else rootFrame.Navigate(typeof(MainPage), paneNavigationArgs, new SuppressNavigationTransitionInfo()); @@ -322,5 +362,27 @@ async Task PerformNavigationAsync(string payload, string selectItem = null) } } } + + public bool SetCanWindowToFront(bool canWindowToFront) + { + lock (_canWindowToFrontLock) + { + if (CanWindowToFront != canWindowToFront) + { + CanWindowToFront = canWindowToFront; + return true; + } + return false; + } + } + + private void WindowManager_WindowMessageReceived(object? sender, WinUIEx.Messaging.WindowMessageEventArgs e) + { + if ((!CanWindowToFront) && e.Message.MessageId == Windows.Win32.PInvoke.WM_WINDOWPOSCHANGING) + { + Win32Helper.ForceWindowPosition(e.Message.LParam); + e.Handled = true; + } + } } } diff --git a/src/Files.App/NativeMethods.json b/src/Files.App/NativeMethods.json deleted file mode 100644 index f03b2ad6671d..000000000000 --- a/src/Files.App/NativeMethods.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "$schema": "https://aka.ms/CsWin32.schema.json", - "allowMarshaling": true, - "multiTargetingFriendlyAPIs": false, - "useSafeHandles": true, - "wideCharOnly": true, - "emitSingleFile": false, - "className": "PInvoke", - "public": true -} diff --git a/src/Files.App/NativeMethods.txt b/src/Files.App/NativeMethods.txt deleted file mode 100644 index 92b8c114269d..000000000000 --- a/src/Files.App/NativeMethods.txt +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -WNDPROC -WNDCLASSEXW -RegisterClassEx -CreateWindowEx -DestroyWindow -GetModuleHandle -RECT -NOTIFYICONIDENTIFIER -Shell_NotifyIconGetRect -RegisterWindowMessage -NOTIFYICONDATAW -Shell_NotifyIcon -GetCursorPos -DestroyMenu -AppendMenu -CreatePopupMenu -SetForegroundWindow -TrackPopupMenuEx -TRACK_POPUP_MENU_FLAGS -GetSystemMetricsForDpi -DefWindowProc -SYSTEM_METRICS_INDEX -GetDpiForWindow -HWND -LRESULT -WPARAM -LPARAM -WM_LBUTTONUP -WM_RBUTTONUP -WM_DESTROY -SetForegroundWindow -GetForegroundWindow -GetCurrentThreadId -GetWindowThreadProcessId -AttachThreadInput -SetWindowPos -SetFocus -SetActiveWindow -IsIconic -CopyFileFromApp -MoveFileFromApp -DeleteFileFromApp -RemoveDirectoryFromApp -GetKeyState -CreateDirectoryFromApp -WNetCancelConnection2 -NET_USE_CONNECT_FLAGS -NETRESOURCEW -WNetAddConnection3 -CREDENTIALW -CredWrite -WNetConnectionDialog1 -CONNECTDLGSTRUCTW -DwmSetWindowAttribute -WIN32_ERROR -CoCreateInstance -FileOpenDialog -IFileOpenDialog -SHCreateItemFromParsingName -FileSaveDialog -IFileSaveDialog -D3D_DRIVER_TYPE -D3D_FEATURE_LEVEL -ID3D11Device -ID3D11DeviceContext -D3D11CreateDevice -IDXGIDevice -DCompositionCreateDevice -IDCompositionDevice -GetNamedSecurityInfo -ConvertSidToStringSid -ConvertStringSidToSid -SetNamedSecurityInfo -GetAclInformation -IsValidAcl -GetAce -SetEntriesInAcl -ACL_SIZE_INFORMATION -DeleteAce -EXPLICIT_ACCESS -ACCESS_ALLOWED_ACE -LookupAccountSid -GetComputerName -AddAccessAllowedAceEx -LocalAlloc -InitializeAcl -AddAce -LocalFree diff --git a/src/Files.App (Package)/Package.appxmanifest b/src/Files.App/Package.appxmanifest similarity index 91% rename from src/Files.App (Package)/Package.appxmanifest rename to src/Files.App/Package.appxmanifest index 041b05c7b5a2..53ada86ed481 100644 --- a/src/Files.App (Package)/Package.appxmanifest +++ b/src/Files.App/Package.appxmanifest @@ -1,5 +1,5 @@ - - + + + Version="4.0.38.0" /> Files - Dev @@ -51,19 +51,18 @@ + - - @@ -100,7 +99,7 @@ Wide310x150Logo="Assets\AppTiles\Dev\Wide310x150Logo.png" Square71x71Logo="Assets\AppTiles\Dev\Small71x71Logo.png" Square310x310Logo="Assets\AppTiles\Dev\Large310x310Logo.png" - ShortName="Files"> + ShortName="Files - Dev"> @@ -121,12 +120,14 @@ - + + - + + @@ -148,6 +149,7 @@ .tar .jar .mrpack + .gz assets\archives\ExtensionIcon.png @@ -158,11 +160,11 @@ - + - Files.App\WinRT.Host.dll + WinRT.Host.dll diff --git a/src/Files.App/Program.cs b/src/Files.App/Program.cs index a0239827dc27..91702da505af 100644 --- a/src/Files.App/Program.cs +++ b/src/Files.App/Program.cs @@ -1,14 +1,16 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.Shared.Helpers; using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml; using Microsoft.Windows.AppLifecycle; using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using Windows.Win32.UI.WindowsAndMessaging; using Windows.ApplicationModel.Activation; using Windows.Storage; -using static Files.App.Helpers.Win32PInvoke; namespace Files.App { @@ -20,9 +22,6 @@ namespace Files.App /// internal sealed class Program { - private const uint CWMO_DEFAULT = 0; - private const uint INFINITE = 0xFFFFFFFF; - public static Semaphore? Pool { get; set; } static Program() @@ -55,10 +54,11 @@ static Program() [STAThread] private static void Main() { + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); WinRT.ComWrappersSupport.InitializeComWrappers(); // We are about to do the first WinRT server call, in case the WinRT server is hanging - // we need to kill the server if there is no other Files instances already running + // we need to kill the server if there are no other Files instances already running static bool ProcessPathPredicate(Process p) { @@ -97,13 +97,29 @@ static bool ProcessPathPredicate(Process p) } // NOTE: - // This has been commentted out since out-of-proc WinRT server seems not to support elevetion. + // This has been commented out since out-of-proc WinRT server seems not to support elevation. // For more info, see the GitHub issue (#15384). // Now we can do the first WinRT server call //Server.AppInstanceMonitor.StartMonitor(Environment.ProcessId); var OpenTabInExistingInstance = ApplicationData.Current.LocalSettings.Values.Get("OpenTabInExistingInstance", true); - var activatedArgs = AppInstance.GetCurrent().GetActivatedEventArgs(); + + AppActivationArguments activatedArgs; + try + { + activatedArgs = AppInstance.GetCurrent().GetActivatedEventArgs(); + } + catch (COMException ex) when (ex.HResult == unchecked((int)0x80040154)) + { + Windows.Win32.PInvoke.MessageBox( + default, + Constants.Startup.MissingRuntimeMessage, + Constants.Startup.MissingRuntimeTitle, + MESSAGEBOX_STYLE.MB_ICONERROR); + + throw new InvalidOperationException(Constants.Startup.MissingRuntimeMessage, ex); + } + var commandLineArgs = GetCommandLineArgs(activatedArgs); if (commandLineArgs is not null) @@ -220,8 +236,8 @@ static bool ProcessPathPredicate(Process p) var cmdLaunchArgs = activatedArgs.Data is ILaunchActivatedEventArgs launchArgs && launchArgs.Arguments is not null && CommandLineParser.SplitArguments(launchArgs.Arguments, true).FirstOrDefault() is string arg0 && - (arg0.EndsWith($"files.exe", StringComparison.OrdinalIgnoreCase) || - arg0.EndsWith($"files", StringComparison.OrdinalIgnoreCase)) ? launchArgs.Arguments : null; + (arg0.EndsWith($"files-dev.exe", StringComparison.OrdinalIgnoreCase) || + arg0.EndsWith($"files-dev", StringComparison.OrdinalIgnoreCase)) ? launchArgs.Arguments : null; var cmdProtocolArgs = activatedArgs.Data is IProtocolActivatedEventArgs protocolArgs && protocolArgs.Uri.Query.TrimStart('?').Split('=') is string[] parsedArgs && parsedArgs.Length == 2 && parsedArgs[0] == "cmd" ? Uri.UnescapeDataString(parsedArgs[1]) : null; @@ -248,20 +264,7 @@ private static async void OnActivated(object? sender, AppActivationArguments arg /// public static void RedirectActivationTo(AppInstance keyInstance, AppActivationArguments args) { - IntPtr eventHandle = CreateEvent(IntPtr.Zero, true, false, null); - - Task.Run(() => - { - keyInstance.RedirectActivationToAsync(args).AsTask().Wait(); - SetEvent(eventHandle); - }); - - _ = CoWaitForMultipleObjects( - CWMO_DEFAULT, - INFINITE, - 1, - [eventHandle], - out uint handleIndex); + keyInstance.RedirectActivationToAsync(args).AsTask().Wait(); } public static void OpenShellCommandInExplorer(string shellCommand, int pid) @@ -271,20 +274,7 @@ public static void OpenShellCommandInExplorer(string shellCommand, int pid) public static void OpenFileFromTile(string filePath) { - IntPtr eventHandle = CreateEvent(IntPtr.Zero, true, false, null); - - Task.Run(() => - { - LaunchHelper.LaunchAppAsync(filePath, null, null).Wait(); - SetEvent(eventHandle); - }); - - _ = CoWaitForMultipleObjects( - CWMO_DEFAULT, - INFINITE, - 1, - [eventHandle], - out uint handleIndex); + LaunchHelper.LaunchAppAsync(filePath, null, null).Wait(); } } } diff --git a/src/Files.App/Properties/PublishProfiles/win-arm64.pubxml b/src/Files.App/Properties/PublishProfiles/win-arm64.pubxml index fa22017606e1..c94e10787961 100644 --- a/src/Files.App/Properties/PublishProfiles/win-arm64.pubxml +++ b/src/Files.App/Properties/PublishProfiles/win-arm64.pubxml @@ -7,7 +7,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem arm64 win-arm64 - true + false False + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Նոր պատուհան + + + Պատճենել ուղին + + + Copy item path + + + Պատճենել ուղին չակերտներով + + + Copy selected item path with quotes + + + Զննել + + + Չափ + + + Ստեղծված է. + + + Ուղի + + + Չափ. + + + Չափը սկառավարակում. + + + Չսեղմվածի չափ․ + + + Այս գործողությունը չի կարող ավարտվել + + + The destination folder + + + is a subfolder of the source folder + + + Բաց թողնել + + + Select all + + + Շրջել ընտրությունը + + + Մաքրել ընտրությունը + + + Փոփոխված. + + + Մուտք գործած. + + + Մաքրել բոլոր տարրերը + + + Գրեք ուղի նավարկելու համար կամ մուտքագրեք «>»՝ հրամանների պնակը բացելու համար + + + Որոնել + + + Նիշքերը, որոնց նախկինում մուտք եք գործել, կցուցադրվեն այստեղ + + + Հեռացնել այս տարրը + + + GitHub պահեստ + + + Մասին + + + Բաց աղբյուրներ + + + Ամսաթվի ձեւաչափ + + + Ոճ + + + Ցույց տալ ընդլայնումները հայտնի նիշքերի տեսակների համար + + + Ցույց տալ թաքնված նիշքերը եւ պանակները + + + Show dot files + + + Տեսք + + + Խորքի գույն + + + Ընդլայնված + + + Շարունակել այնտեղ, որտեղ լքել եք + + + Բացել նոր ներդիր + + + Բացեք որոշակի էջ կամ էջեր + + + Սեղան + + + Փաստաթղթեր + + + Ներբեռնումներ + + + Անուն + + + Աճման կարգով + + + Կպցնել + + + Կպցնել կարճատը + + + Նոր + + + Հատկություններ + + + Բացել + + + Բացել նոր ներդիրում + + + Բացել նոր պատուհան + + + Կիսվել + + + Կտրել + + + Ջնջել + + + Մեխել Կողագոտուն + + + Բարի գալո՛ւստ Files + + + Շնորհել թույլտվություն + + + Սկսելու համար դուք պետք է մեզ թույլտվություն տաք՝ ցուցադրելու ձեր նիշքերը: Սա կբացի Կայանքների էջը, որտեղ դուք կարող եք մեզ տրամադրել այս թույլտվությունը: Այս քայլն ավարտելուց հետո դուք պետք է նորից բացեք հավելվածը: + + + Ցուցադրիչ + + + Գրեք տարրի անունը + + + Ստեղծել նոր {0} + + + Լուսավոր + + + Մութ + + + Use system setting + + + Հավելված + + + Համակարգ + + + Նոր նիշք + + + Նոր նիշք + + + Այս պանակը դատարկ է + + + Հետ + + + Առաջ + + + Վերեւ + + + Թարմացնել + + + An item with this name already exists in this directory. + + + Replace existing item + + + Item already exists + + + Կայել որպես կողպէկրանի սեղանի խորք + + + Կայել որպես հավելվածի խորք + + + We weren't able to delete this item + + + Please insert the necessary drive to access this item. + + + Սկավառավարն անջատված է + + + The file you are attempting to access may have been moved or deleted. + + + Cannot open properties for this file + + + The file you are attempting to access may have been moved or deleted. + + + Նիշքը Չի Գտնվել + + + Պանակը, որին փորձում եք մուտք գործել, կարող է տեղափոխվել է կամ ջնջվել: + + + Did you delete this folder? + + + The file you are attempting to access is currently being used by {0} + + + The file you are attempting to access is currently being used by another application + + + Նիշքը օգտագործվում է + + + Դիրք + + + Կարդալ-միայն + + + {0, plural, one {# օր առաջ} other {# օր առաջ}} + + + 1 օր առաջ + + + {0} օր առաջ + + + {0, plural, one {# ժամ առաջ} other {# ժամ առաջ}} + + + 1 ժամ առաջ + + + {0} ժամ առաջ + + + {0, plural, one {# րոպե առաջ} other {# րոպե առաջ}} + + + 1 րոպե առաջ + + + {0} րոպե առաջ + + + {0, plural, one {# վարկյան առաջ} other {# վարկյան առաջ}} + + + 1 վարկյան առաջ + + + {0} վարկյան առաջ + + + Հիմա + + + The requested operation is not supported + + + {1}-ից ազատ է {0} + + + Ուղարկել դեպի + + + Տարրի անունը չպետք է պարունակի հետեւյալ նիշերը. \ / : * ? « < > | + + + The item name specified is invalid + + + {0, plural, one {# ընտրված տարր} other {# ընտրված տարր}} + ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural + + + {0, plural, one {տարր} other {տարր}} + ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural + + + Այո + + + Իսկապե՞ս ուզում եք ընդմիշտ ջնջել բոլոր այս տարրերը + + + Դատարկել աղբարկղը + + + բայթ + + + ԿԲ + + + ՄԲ + + + ԳԲ + + + ՏԲ + + + ՊԲ + + + Բ + + + {0, number} {0, plural, one {նիշք} other {նիշք}}, {1, number} {1, plural, one {պանակ} other {պանակ}} + + + {0, number} {0, plural, one {նիշք} other {նիշք}}, {1, number} {1, plural, one {պանակը} other {պանակը}} {2, number} {2, plural, one {տեղադրությունից} other {տեղադրությունից}} + + + Run as another user + + + {0}-ի բոլոր տեսակները + + + Տարբեր տեսակներ + + + Բոլորը {0}-ում + + + Օգտագորված տարածք. + + + Ազատ տարածք. + + + Տարողություն. + + + Նիշքային համակարգ + + + Վերջիններ + + + Տեղափոխել ներդիրը այստեղ + + + Վիճակ + + + Ոչ մի արդյունք + + + Can't access any items to display + + + Տեղափոխել դեպի {0} + + + Պատճենել դեպի {0} + + + Clone to {0} + + + Ստեղծել կարճատ + + + Բացել նիշքի տեղադրությունը + + + Արգումենտներ․ + + + Նպատակակետ․ + + + Կարճատի տեսակ. + + + Վեպ հղում + + + Ընդհանուր + + + Կարճատ + + + Համացանցի կարճատ + + + {0} - կարճատ + + + Files ran into a problem that the developers didn't prepare for yet. + + + Something went wrong! + + + Զեկուցել այս խնդիրը + + + Հղված տարրը կամ անվավեր է կամ անհասանելի:{0}Սխալի ուղերձ.{0}{1} + + + Անվավեր տարր + + + Վարկած. + + + There's nothing to share right now... + + + The items you've selected will be shared + + + The selected item will be shared + + + Կիսվել «{0}»-ով + + + Կիսում {0} {1} + + + The item you're attempting to rename no longer exists. Please verify the correct location of the item you need to rename. + + + Item no longer exists + + + Նշված անունը անվավեր էր: Խնդրում ենք ստուգել ցանկալի տարրի անունը եւ փորձել նորից: + + + Անվավեր տարրի անուն + + + The length of the item name specified exceeds the maximum limit. Please check the desired name and try again. + + + Item name was too long + + + Ցույց տալ ավել ընտրանքներ + + + Այս կայանքները կիրառելու համար հավելվածը պետք է վերագործարկվի, կցանկանա՞ք։ + + + Հովանվորեք մեզ GitHub-ում + + + We weren't able to create this item + + + Մուտքը մերժված է + + + Կայել որպես + + + Չեղարկել + + + Ստեղծել նոր տարր + + + Նշել տեսակ այս նոր տարրի համար ստորեւ + + + Նիշք + + + Ստեղծել դատարկ նիշք + + + Պանակ + + + Ստեղծել դատարկ պանակ + + + Refresh the directory + + + Լեզու + + + Կեղեւի ընդլայնումները տեղափոխել ենթացանկ + + + Ցույցադրել ջնջման հաստատման երկխոսությունը տարրը ջնջելիս + + + There was an issue saving some properties. + + + Սխալ + + + Փայկել այնուհանդերձ + + + Գնահատում + + + Տարրի Ուղին + + + Տարրի Տեսակ + + + Վերնագիր + + + Առարկա + + + Մեկնաբանություն + + + Պատճենաշնորհ + + + Փոփոխման Ամսաթիվ + + + Բիթային խորություն + + + Չափսերը + + + Հորիզոնական չափսը + + + Ուղղաձիգ Չափ + + + Horizontal Resolution + + + Vertical Resolution + + + Գունային Տարածություն + + + sRGB + + + Նշված չէ + + + Տասական երկայնություն + + + Տասնորդական լայնություն + + + Բարձրություն + + + Նկարահանման ամսաթիվը + + + Camera Manufacturer + + + Խցիկի մակնիշը + + + Լուսարկման ժամանակը + + + Կիզակետային Հեռավորություն + + + Բացվածք + + + Մարդկանց Անուններ + + + Ալիքի քանակ + + + Ձեւաչափ + + + Նմուշառման հաճախականությունը + + + Ալբոմի Կատարողներ + + + Ալբոմի Վերնագիր + + + Արվեստող + + + Հարված՝ րոպեում + + + Կազմող + + + Խմբավար + + + Սկավառակի համար + + + Ոճ + + + Հեւագծման համար + + + Տեւողություն + + + Կադրերի քանակը + + + Պաշտպանության տեսակը + + + Հեղինակի Url + + + Content Distributor + + + Թողարկման Ամսաթիվ + + + Շարքի անունը + + + Փուլի Համար + + + Դրվագի Համար + + + Արտադրող + + + Գովարշավի Url + + + Տրամադրողի Ոճ + + + Հրապարակող + + + Thumbnail Large Path + + + Thumbnail Large Uri + + + Thumbnail Small Path + + + Thumbnail Small Uri + + + Օգտագործել Վեպ URL + + + Գրող + + + Տարի + + + Մասնակից + + + Նախորդ Հեղինակ + + + Վերանայման համարը + + + Վարկած + + + Ստեղծման Ամսաթիվ + + + Total Editing Time + + + Ձեւանմուշ + + + Բառերի Քանակ + + + Նիշի Քանակը + + + Տողերի Քանակ + + + Պարբերության Քանակ + + + Էջերի Քանակ + + + Սահիկների Քանակ + + + Կադրերի Հաճախականություն + + + Կոդավորման բիթայնությունը + + + Ձայնանյութի կոդավորման բիթայնությունը + + + Տեսանյութի կոդավորման բիթայնությունը + + + Սեղմում + + + Կադրի լայնությունը + + + Կադրի Բարձրություն + + + Կողմնորոշում + + + Միջուկ + + + Պատկեր + + + Լուսո + + + GPS + + + Մեդիա + + + Ձայնո + + + Երաժշտություն + + + Տեսո + + + Փաստաթուղթ + + + Հասցե + + + Ընտրության ընտրանքներ + + + Նշել + + + Աղբաման + + + Կարճատ տարր + + + Հիշասարքեր + + + Տեղափոխել այստեղ + + + Safe to remove hardware + + + The device can now be safely removed from the computer. + + + Problem Ejecting Device + + + This device is currently in use. Close any programs, windows or tabs that might be using the device, and then try again. + + + Հանել + + + Կրկնօրինակել ներդիրը + + + Տեղափոխել ներդիրը նոր պատուհան + + + Նոր ներդիր + + + Some properties may contain personal information. + + + Clear All Properties + + + Check the status of file operations here + + + Վիճակի կենտոն + + + Search results in {1} for {0} + + + Search results for `{0}` + + + Մանրամասներ + + + Ոչ + + + Ցույց տալ պաշտպանված համակարգի նիշքերը + + + Համաժամել դիրքը եւ դասավորելու հատկություններ գրացուցակների միջեւ + + + Անհատականացրեք աջ կտտացման համաշարի ընտրացանկ + + + Բնօրինակ ուղի + + + Հավելել + + + Խմբագրել + + + Հեռացնել + + + Բացել ներդիրները երկակի փեղկի կերպում + + + Ցույց տալ ընտրանք՝ պանակները նոր փեղկում բացելու համար + + + Ցույց տալ ուղին պատճենելու ընտրանքը + + + Ցույց տալ ընտրանք, ստեղծելու համար պանակ՝ ընտրվածով + + + Ցույց տալ ընտրանք՝ ստեղծելու կարճատ + + + Նոր փեղկ + + + Բացել նոր փեղկում + + + Նիշքեր և Պանակներ + + + Մանրամասներ (Ctrl+Shift+1) + + + Ամսաթիվը ջնջված է + + + Ամպային Հիշասարքեր + + + Հաստատել + + + Ցանկալի անուն + + + Փոխարկել նախադիտման փեղկը + + + Փոխարկել մանրամասների փեղկի + + + Փոխարկել մանրամասների փեղկը դեպի դիտելու հիմնական նիշքերի հատկությունները + + + Փոխարկել տեղեկի փեղկի + + + Toggle visibility of the detail/preview panes + + + Toggle toolbar + + + Toggle visibility of the toolbar + + + Toggle filter header + + + Toggle visibility of the filter header + + + Toggle dual pane + + + Toggle dual pane mode + + + Նախադիտումը հասանելի չէ + + + Տարրի Անուն + + + Ապամեխել Կողագոտուց + + + Ցանց + + + Նիշքի մանրամասներ + + + Նիշքի նախադիտում + + + Ընտրված նիշքի նախադիտման փեղկ + + + Հետադարձ Կապ + + + Available when online + + + Փաստաթղթավորում + + + Հասանելի է առցանց + + + Partially available offline + + + Համաժամեցում + + + Excluded from sync + + + Ոչ հաշվելի + + + Անհայտ + + + Եթե ​​փոխեք նիշքի ընդլայնումը, նիշքը կարող է անօգտագործելի դառնալ: Վստա՞հ եք, որ ցանկանում եք փոխել այն: + + + CD ROM Հիշասարք + + + Ամպի Հիշասարք + + + Անշարժ Սկավառակ + + + Ճկուն սկավառակի սարք + + + Ցանցի Հիշասարք + + + Չտեղակայված սկավառավար + + + RAM սկավառավար + + + Removable Storage Device + + + Անհայտ + + + Կարծական Հիշասարք + + + Ստեղծման ամսաթիվ + + + Ավել ընտրանքներ... + + + Կցել ցանցային սկավառակը + + + Մեխված + + + Դարաններ + + + Ոչինչ ընտրված չէ + + + Թիրախ + + + Արգումենտներ + + + Լռելյայն + + + Տարրերի քանակ + + + Այս գործողությունը պահանջում է վարիչի իրավունքները + + + Կցանկանա՞ք շարունակել որպես վարիչ + + + Սյուններ (Ctrl+Shift+6) + + + Pin to the Start Menu + + + Unpin from the Start Menu + + + Դարան + + + Տեղադրություններ. + + + Set as default save path + + + Չկան տեղադրություններ + + + Input field cannot be empty! + + + The name must not contain the following characters: \ / : * ? " < > | + + + Using this name is not allowed! + + + Library with the same name already exists! + + + Open Storage Sense + + + Open the Storage Sense page in Windows Settings + + + Մաքրում + + + Պատճենել + + + Պատճենել {0, plural, one {տարր} other {տարր}} + + + Ջնջել {0, plural, one {տարր} other {տարր}} + + + Շարժել + + + {0, plural, one {Մեկ տարր կտեղափոխվի} other {# տարր կտեղափոխվի}} + + + Տեղափոխել {0, plural, one {տարր} other {տարր}} + + + {0, plural, one {Մեկ տարր կջնջվի} other {# տարր կջնջվի}} + + + Շարունակել + + + {0, plural, one {There is one conflicting file name} other {There are # conflicting file names}} + + + {0, plural, one {There is one conflicting file name} other {There are # conflicting file names}}, and {1, plural, one {one outgoing item} other {# outgoing items}} + + + Conflicting {0, plural, one {file name} other {file names}} + + + {0, plural, one {Մեկ տարր կպատճենվի} other {# տարր կպատճենվի}} + + + Ջնջել ընդմիշտ + + + ISO-ի արագություն + + + Փոխարինել առկան + + + Արտադրել նոր անուն + + + Ստեղծել պանակ ընտրվածով + + + Տեսակ. + + + Փոփոխման ամսաթիվը․ + + + Բացել հետեւյալով + + + Նիշքի պատկերակ + + + Original path column + + + Տարրի տեսակի սյունակ + + + Ամսաթվի փոփոխման սյունակ + + + Date deleted column + + + Դասավորել + + + Փոփոխման ամսաթիվ + + + Բնօրինակ պանակ + + + Նվազման կարգով + + + Հսկայական + + + Շատ մեծ + + + Սեծ + + + Միջին + + + Փոքր + + + Փոքրիկ + + + Ապագա + + + Այսօր + + + Երեկ + + + Ավելի վաղ այս շաբաթ + + + Նախորդ շաբաթ + + + Earlier this month + + + Անցյալ ամիս + + + Ավելի վաղ այս տարի + + + Անցյալ տարի + + + {0} Տարի + + + {0} տարր + + + {0} տարր + + + Reopen tab + + + Վերանվանել + + + Գաղտնիություն + + + Աղբարկղը + + + Չեղարկում + + + Մաքրել + + + Զպրտիչներ + + + Համաժամման վիճակ + + + Անվտանգություն + + + Ընդլայնված թույլտվություններ + + + Թույլատրել + + + Արգելել + + + Ամբողջ հսկում + + + List directory contents + + + Փոփոխել + + + Թույլատրություններ {0}-ի համար + + + Կարդալ եւ կատարել + + + Կարդալ + + + Group or user names + + + Գրել + + + Անանուն հաշիվ + + + You do not have permissions to view the security properties of this object. Click "Advanced permissions" to proceed. + + + Տեր. + + + Անհայտ տեր + + + Արտահանել պահոցը + + + Ավարտելուց հետո բացեք նպատակակետի պանակը + + + Արտահանել դեպի {0}\ + + + Ստեղծել նոր դարան + + + Վերականգնել լռելյային դարանները + + + Are you sure you want to restore the default libraries? All your files will remain on your storage. + + + Restore Libraries + + + Տեր + + + Հատուկ + + + Մուտք + + + Կիրառվում է առ + + + Կառավարիչ + + + նիշքեր + + + այս պանակը + + + ենթապանակներ + + + Ժառանգված + + + Թույլտվություններ + + + You do not have permissions to view the security properties of this object. You can try to take ownership of this object. + + + Այս պանակը եւ նիշքերը + + + This folder, subfolders and files + + + Միայն նիշքեր + + + Միայն ենթապանակները + + + Only subfolders and files + + + Միայն այս պանակում + + + Այս պանակը եւ ենթապանակները + + + Հավելել տվյալներ + + + Փոխել թույլատրությունը + + + Ստեղծել պանակներ + + + Ստեղծել նիշքեր + + + Delete subdirectories and files + + + Կատարման նիշքեր + + + Կարդալ հատկանիշները + + + Կարդալ տվյալները + + + Read extended attributes + + + Կարդալու թույլտվությունները + + + Դառնալ սեփականատեր + + + Այցելել պանակ + + + Գրելու հատկանիշները + + + Գրել տվյալները + + + Write extended attributes + + + Convert inherited permissions into explicit permissions + + + Enable inheritance + + + Remove all inherited permissions + + + Reset child permissions + + + Replace all child object permissions entries with inheritable permission entries from this object + + + Close pane + + + Մտնել սեղմ կերպ + + + Ելք սեղմ կերպից + + + Նիշքի նկարագությունը + + + Ընկերություն + + + Լեզու + + + Ապրանքանիշներ + + + Նիշքի վարկած + + + Load full preview + + + Downloads the item from the cloud and loads the preview + + + {0, plural, one {# տարր} other {# տարր}} ({1, plural, one {# նիշք} other {# նիշք}}, {2, plural, one {# պանակ} other {# պանակ}}) + + + Uncompressed size + + + WSL + + + Թաքնված {0} ընտրություն + + + Հավելել նիշք + + + Updates available + + + Updates are ready to install + + + Փակել + + + Արդիացնել + + + Տուն + + + The archive extraction completed successfully. + + + Extracting archive + + + Extracting complete! + + + Բացել ծնող-պանակը + + + Անհայտ + + + Խմբագրել պիտակները + + + Լրակազմի մաս + + + Բացել հիշասարքը + + + Please insert a disc into drive {0} + + + Զետեղել սկավառակ + + + ԼԱՎ + + + Credential required + + + Անանուն + + + Provide your credential: + + + ՕգտԱնուն + + + Միույթներ չեն գտնվել + + + Բացել մատյան տեղադրություն + + + Ստեղծել հղում {0}-ում + + + Սյունակներ + + + Քարտեր + + + Բացել պանակները նոր ներդիրում + + + Վիճակի կենտրոն + + + Ստեղծման ամսաթիվի սյունակ + + + Տարրի չափի սյունակ + + + Փորձնական հատկությունների դրոշակներ + + + Օգնություն եւ աջակցություն + + + Ներկայացենել հատկության հայցը + + + Ներկայացնել սխալի զեկույց + + + Կայել Files-ը որպես լռելյալին նիշքի կառավարիչ + + + Use Files as Open File Dialog + + + Պիտակ + + + Open files with a single click + + + Պանակի ուղին + + + Արտածելու կայանքներ + + + Ներածման կայանքներ + + + Couldn't import settings. The settings file is corrupted. + + + Error importing settings + + + Դատարկել Աղբարկղը + + + Կողագոտի + + + Choose a custom folder icon + + + Անհատականացում + + + Վերականգնել լռելյային + + + Բացել ներդիրը գոյություն ունեցող օրինակում՝ այլ հավելվածից Files բացելիս + + + Մեկնարկի կայանքներ + + + Համատեղելիություն + + + Ոչ մի + + + Windows մտնելիս + + + Այս ծրագիրը մեկնարկելիս + + + Համակարգ + + + Համակարգ (Ընդլայնված) + + + 16-bit (65536) գույն + + + 8-bit (256) գույն + + + Անջատել լիէկրանի լավարկում + + + Աշխատում է 640 x 480 էկրանի լուծաչափով + + + Անտեսել բարձր DPI-ի մասշտաբային վարքը + + + Նվազեցված գույնի կերպ + + + Գրանցել այս ծրագիրը վերագործարկման համար + + + Մեկնարկի պատուհան + + + Նորմալ պատուհան + + + Նվազեցված + + + Առավելացված + + + Քշել որպես վարիչ + + + Քշել համատեղելիության անսարքությունները + + + Օգտագործել հիմնական ցուցադրիչի DPI-ի կայանքները + + + Չկարգավորել DPI-ը + + + Չանտեսել DPI-ը + + + Համատեղելիության կերպ + + + Ոչ նվազեցված գույն + + + Երրորդ կողմի դարանները + + + Այս ընտրանքը փոփոխում է համակարգի գրանցամատյանը եւ կարող է անսպասելի կողմնակի ազդեցություններ ունենալ ձեր սարքի վրա: Շարունակեք ձեր պատասխանատվությամբ: + + + The flatten operations are permanent and not recommended. Continue at your own risk. + + + Ստեղծել Դարան + + + Գրեք Դարանի անունը + + + Հաշվել պանակի չափերը + + + Վերջին նիշքերը + + + Բացել Նիշքերը Windows-ը մեկնարկելիս + + + Հատկանիշներ + + + Թաքնված + + + Ավելի մանրամասներ + + + Միայն կարդալ + + + Մաքրել ձեր հիշասարքերի բովանդակությունը + + + Տեսակ + + + Բայթեր + + + Արտահանել + + + Արտահանել նիշքերը + + + Արտահանել այստեղ + + + Ctrl+E + + + Արտածել առ {0} + + + Քշել ծրագրույթ + + + Քշել PowerShell-ով + + + Պանակների չափերի հաշվարկը պաշարներ է պահանջում եւ կարող է հանգեցնել մշակիչի օգտագործման ավելացմանը + + + Պտտեք ձախ + + + Պտտել աջ + + + Տեղադրել + + + Փակել ներդիրը դեպի ձախ + + + Փակել ներդիրը դեպի աջ + + + Փակել այլ ներդիրներ + + + Փակել բոլոր ներդիրները + + + Երաժշտություն + + + Նկարներ + + + Արդիացնել Files-ը + + + Պիտակներ + + + Ընդհանրական + + + օր.՝ {0}, {1} + + + Ցույց տալ մանրապատկերը + + + Նորից փորձել + + + Move operation is not supported in this context. Do you want to copy the items instead? + + + Թաքնված տարրեր + + + Անհատական + + + Կայել որպես սեղանի սահկահանդես + + + Բոլոր չափերը սյունակների տեղավորելու համար + + + Հարմարվողական դիրք + + + Adaptive layout (Ctrl+Shift+7) + + + Ctrl+Shift+7 + + + Ցույց տալ այլընտրանքային տվյալների հոսքերը + + + Բնութագիր + + + Hello! + + + Enjoying Files? Please consider reviewing in the Microsoft Store. + + + Enjoying Files? Please consider supporting the project on GitHub. + + + Sponsor + + + Rate us + + + Dismiss + + + Կայել որպես խորք + + + Կայել որպես սեղանի խորք + + + Կայեր որպես լռելյայն + + + Ամսաթվի սյունակ + + + Ստեղծման ամսաթվի սյունակ + + + Չափի սյունակ + + + Պիտակի սյունակ + + + Տեսակի սյունակ + + + Կողպէկրան + + + Open folders with a single click + + + Բացված տարրեր + + + Սեղմել + + + Move all contents from subfolders into the selected location + + + Հարթել պանակը + + + Հարթել + + + Պանակը հարթեցնելու դեպքում ամբողջ բովանդակությունը նրա ենթապանակներից կտեղափոխվի ընտրված վայր: Այս գործողությունը մշտական ​​է եւ հնարավոր չէ հետարկել: Օգտագործելով այս փորձարարական հատկությունը՝ դուք ընդունում եք ռիսկը եւ համաձայնում եք, որ Files թիմը պատասխանատվություն չի կրի տվյալների կորստի համար: + + + Show flatten options + + + Ընտրեք նիշքեր եւ պանակներ, երբ ճախրում եք վրեն + + + Are you sure you want to restore all items in the recycle bin? + + + Վերականգնել բոլոր տարրերը + + + Do you want to restore the {0, plural, one {selected item} other {{0} selected items}}? + + + Վերականգնել ընտրությունը + + + Վերականգնել Բոլոր Տարրերը + + + Վերականգնել + + + Shortcut cannot be opened + + + Թիրախային նպատակակետը հնարավոր չէ գտնել {0}: Ցանկանո՞ւմ եք ջնջել այս կարճատը: + + + You don't have permission to access this folder. + + + Պահոցի գաղտնաբառը + + + Encoding + + + {0} (detected) + + + Ուղի + + + Դասավորում եւ խմբավորում + + + Ստեղծել պահոց + + + Ստեղծել + + + Ստեղծել {0} + + + Գրել անունը + + + Սեղմնվան մակարդակը + + + Գեր + + + Բարձր + + + Նորմալ + + + Ցածր + + + Արագ + + + Ոչ մի + + + Բաժանման չափ + + + Do not split + + + Auto + + + Dictionary size + + + Word size + + + Estimated memory usage: {0} + + + Available memory: {0} + + + CD + + + DVD + + + FAT + + + Blu-ray + + + Կոդավորում + + + Գաղտնաբառ + + + Ձեւաչափ + + + Համաժամել սյունների վիճակը + + + Խումբավորել ըստ + + + Դասավորել ըստ + + + Խմբավորել նվազման կարգի + + + Դասավորել նվազման կարգի + + + Ստեղծել նոր կարճատ + + + Create shortcuts to local or network programs, files, folders, computers or Internet addresses. + + + Enter the location of the item: + + + Ստեղծել կարճատ + + + Recently used files is currently disabled in Windows File Explorer. + + + Խմբագրել կայանքների նիշքը + + + Open settings file in your default editor + + + Release Notes + + + Open Release Notes + + + Այստեղ կարճատ ստեղծելու համար անհրաժեշտ են վարիչի արտոնություններ + + + Would you like to create the shortcut on the desktop instead? + + + The specified item name is invalid + + + Կայանքներ + + + Կրկնակի կտտացրեք դատարկ տարածության վրա՝ մեկ գրացուցակ վեպ բարձրանալու համար + + + Դիտել ավելին + + + Ցույց տալ խմբագրման պիտակի ենթացանկը + + + Ցույց տալ սեղմման ընտրանքները + + + Ցույց տալ ուղարկման ցանկը + + + Ցույց տալ ընտրանք՝ պանակները նոր ներդիրում բացելու համար + + + Ցույց տալ ընտրանք՝ պանակները նոր պատուհանում բացելու համար + + + Արագ մուտք + + + Enter your credentials to connect to: {0} + + + Enter network credentials + + + Remember my credentials + + + Network folder error + + + Հավելվածի վարկած + + + Windows-ի վարկած + + + Միշտ + + + Միայն մշտական ​​ջնջում + + + Երբեք + + + Պիտակի գույն + + + Նոր պիտակ + + + Ստեղծել նոր պիտակ + + + Բեռնում... + + + Համաշարային ցանկի ընտրանքներ + + + File extensions + + + Ձեւաչափ + + + Օգնություն + + + Full screen + + + Are you sure you want to delete this tag? + + + Play + + + Բարձրություն + + + Լայնություն + + + Կիրառեք այս գործողությունը բոլոր հակասական տարրերի վրա + + + Բացել Windows Տերինալում + + + Բացել Windows Տերինալում որպես վարիչ + + + Պահել + + + Բազմընտրություն + + + Վերադասավորել կողագոտու տարրերը + + + Հեշեր + + + An error occurred during the calculation + + + Failed to calculate the hash, please close the file and try again. + + + Hashes aren't available for online files + + + Ալգորիթմ + + + Հեշ արժեքը + + + Select hashes to show + + + Հաշվարկում... + + + Մեխված տարրեր + + + Նվազեցման չափով + + + Աճման չափով + + + Sort direction + + + Անվավեր Անուն + + + Name must not be empty or start or end with a period. + + + Տեսանյութեր + + + Preview popup + + + Փոխարկել սեղմ կերպից + + + Open the online help page in your browser + + + Toggle full screen mode + + + Enter compact overlay mode + + + Exit compact overlay mode + + + Toggle compact overlay mode + + + Start search in the OmniBar + + + Toggle visibility of hidden items + + + Toggle visibility of dot files + + + Toggle visibility of file extensions + + + Փոխարկել նախադիտման փեղկը դեպի նիշքի նախադիտումների դիտում + + + Toggle whether to show sidebar + + + Copy selected {0, plural, one {item} other {items}} + + + Copy path of the current directory + + + Copy path of selected items + + + Copy path of selected items with quotes + + + Copy path of the current directory with quotes + + + Cut selected {0, plural, one {item} other {items}} + + + Paste items to the current folder + + + Paste items to the current folder as shortcuts + + + Paste items to the selected folder + + + Paste to selected folder + + + Delete selected {0, plural, one {item} other {items}} + + + Ստեղծել նոր պանակ + + + Create new {0, plural, one {shortcut} other {shortcuts}} to selected {0, plural, one {item} other {items}} + + + Ստեղծել նոր կարճատ ցանկացած տարրին + + + Empty the contents of Recycle Bin + + + Open "Format Drive" menu for selected item + + + Restore selected {0, plural, one {item} other {items}} from recycle bin + + + Վերականգնել բոլոր տարրերը աղբարկղից + + + Open {0, plural, one {item} other {items}} + + + Open {0, plural, one {item} other {items}} with selected application + + + Open parent folder of searched item + + + Թարմացնել էջի բովանդակությունը + + + Վերանվանել ընտրված տարրը + + + Ընտրել բոլոր տարրերը + + + Invert selected items + + + Clear selected items + + + Փոխարկել տարրի ընտրությունը + + + Share selected {0, plural, one {file} other {files}} with others + + + Pin {0, plural, one {item} other {items}} to the Start Menu + + + Unpin {0, plural, one {item} other {items}} from the Start Menu + + + Pin {0, plural, one {folder} other {folders}} to Sidebar + + + Unpin {0, plural, one {folder} other {folders}} from Sidebar + + + Set selected picture as desktop background + + + Set selected pictures as desktop slideshow + + + Set selected picture as lockscreen background + + + Set selected picture as the app background + + + Install font + + + Install driver + + + Install certificate + + + Install selected {0, plural, one {font} other {fonts}} + + + Install {0, plural, one {driver} other {drivers}} using selected inf {0, plural, one {file} other {files}} + + + Install selected {0, plural, one {certificate} other {certificates}} + + + Run selected application as administrator + + + Run selected application as another user + + + Run selected PowerShell script + + + Մեկնարկել նախադիտումը թռչող պատուհանում + + + Create archive with selected {0, plural, one {item} other {items}} + + + Create 7z archive with selected {0, plural, one {item} other {items}} + + + Create zip archive with selected {0, plural, one {item} other {items}} + + + Extract selected {0, plural, one {archive} other {archives}} to any folder + + + Extract selected {0, plural, one {archive} other {archives}} to the current folder + + + Extract selected {0, plural, one {archive} other {archives}} to new folder + + + Rotate selected {0, plural, one {image} other {images}} to the left + + + Rotate selected {0, plural, one {image} other {images}} to the right + + + Բացել կայանքների էջը + + + Բացել պանակը տերմինալում + + + Բացել պանակը տերմինալում որպես վարիչ + + + Նվազեցրեք տարրի չափը ընթացիկ տեսքում + + + Աճեցրեք տարրի չափը ընթացիկ տեսքում + + + Փոխարկել մանրամասների տեսքի + + + Switch to cards view + + + Փոխարկել ցուցակի տեսքի + + + Ցուցակ + + + Ցանց + + + Փոխարկել ցանցի տեսքի + + + Փոխարկել սույնակների տեսքի + + + Փոխարկեք տեսքերը հարմարվողականի + + + Դասավորել տարրերը ըստ անվան + + + Դասավորել տարրերը ըստ ամսաթվի փոփխման + + + Դասավորել տարրերը ըստ ստեղծման ամսաթվի + + + Դասավորել տարրերը ըստ չափի + + + Դասավորել տարրերը ըստ տեսակի + + + Sort items by sync status + + + Դասավորել տարրերը ըստ պիտակների + + + Sort items by original folder + + + Դասավորել տարրերը ըստ ջնջման ամսաթվի + + + Դասավորել տարրերը ըստ աճման կարգի + + + Դասավորել տարրերը ըստ նվազման կարգի + + + Փոխարկել տարրի տեսակավորման ուղղությունը + + + Ցուցակի տարրերը առանց խմբավորման + + + Տարրերի խումբ ըստ անվան + + + Տարրերի խումբ ըստ ամսաթվի փոփխման + + + Տարրերի խումբ ըստ ստեղծման ամսաթվի + + + Տարրերի խումբ ըստ չափի + + + Տարրերի խումբ ըստ տեսակի + + + Խմբավորել տարրերը ըստ համաժամեցման վիճակի + + + Տարրերի խումբ ըստ պիտակների + + + Group items by original folder + + + Group items by date deleted + + + Խմբավորել տարրերը ըստ պանակի ուղղու + + + Sort groups in ascending order + + + Sort groups in descending order + + + Toggle group sort direction + + + Բացել նոր ներդիր + + + Navigate backward + + + Navigate forward + + + Նավարկել վերեւ մեկ պանակ + + + Կրկնօրինակել ընթացիկ ներդիրը + + + Կրկնօրինակել ընտրված ներդիրը + + + Փակել ընթացիկ ներդիրի ձախ կողմում գտնվող ներդիրները + + + Close tabs to the left of selected tab + + + Close tabs to the right of current tab + + + Close tabs to the right of selected tab + + + Close tabs other than current tab + + + Close tabs other than selected tab + + + Փակել բոլոր ներդիրները, ներառյալ ընթացիկ ներդիրը + + + Reopen recently closed tab + + + Տեղափոխել նախորդ ներդիր + + + Տեղափոխել հաջորդ ներդիր + + + Փակել ընթացիկ ներդիրը + + + Close the active pane + + + Կիզակիտում այլ փեղկին + + + Switch focus to the other pane + + + Toggle the sidebar + + + Alt + Key name for hotkeys in menus. Use abbreviation if possible. + + + Ctrl + Key name for hotkeys in menus. Use abbreviation if possible. + + + Shift + Key name for hotkeys in menus. Use abbreviation if possible. + + + Win + Key name for hotkeys in menus. Use abbreviation if possible. + + + Enter + Key name for hotkeys in menus. Use abbreviation if possible. + + + Բացատ + Key name for hotkeys in menus. Use abbreviation if possible. + + + Esc + Key name for hotkeys in menus. Use abbreviation if possible. + + + Backspace + Key name for hotkeys in menus. Use abbreviation if possible. + + + Tab + Key name for hotkeys in menus. Use abbreviation if possible. + + + Ins + Key name for hotkeys in menus. Use abbreviation if possible. + + + Del + Key name for hotkeys in menus. Use abbreviation if possible. + + + Ձախ + Key name for hotkeys in menus. Use abbreviation if possible. + + + Աջ + Key name for hotkeys in menus. Use abbreviation if possible. + + + Ներքեւ + Key name for hotkeys in menus. Use abbreviation if possible. + + + Վերեւ + Key name for hotkeys in menus. Use abbreviation if possible. + + + Տուն + Key name for hotkeys in menus. Use abbreviation if possible. + + + End + Key name for hotkeys in menus. Use abbreviation if possible. + + + PageDown + Key name for hotkeys in menus. Use abbreviation if possible. + + + PageUp + Key name for hotkeys in menus. Use abbreviation if possible. + + + | + Key name for hotkeys in menus. Use abbreviation if possible. + + + Pause + Key name for hotkeys in menus. Use abbreviation if possible. + + + Sleep + Key name for hotkeys in menus. Use abbreviation if possible. + + + Մաքրել + Key name for hotkeys in menus. Use abbreviation if possible. + + + Print + Key name for hotkeys in menus. Use abbreviation if possible. + + + Օգնություն + Key name for hotkeys in menus. Use abbreviation if possible. + + + Mouse4 + Key name for hotkeys in menus. Use abbreviation if possible. + + + Mouse5 + Key name for hotkeys in menus. Use abbreviation if possible. + + + Հավելված + Key name for hotkeys in menus. Use abbreviation if possible. + + + Հավելված1 + Key name for hotkeys in menus. Use abbreviation if possible. + + + Հավելված2 + Key name for hotkeys in menus. Use abbreviation if possible. + + + Փոստ + Key name for hotkeys in menus. Use abbreviation if possible. + + + GoHome + Key name for hotkeys in menus. Use abbreviation if possible. + + + GoBack + Key name for hotkeys in menus. Use abbreviation if possible. + + + GoForward + Key name for hotkeys in menus. Use abbreviation if possible. + + + ԶննիչիԹարմացում + Key name for hotkeys in menus. Use abbreviation if possible. + + + ԶնննիչիԿանգ + Key name for hotkeys in menus. Use abbreviation if possible. + + + Որոնել + Key name for hotkeys in menus. Use abbreviation if possible. + + + Սիրվածներ + Key name for hotkeys in menus. Use abbreviation if possible. + + + PlayPause + Key name for hotkeys in menus. Use abbreviation if possible. + + + MediaStop + Key name for hotkeys in menus. Use abbreviation if possible. + + + PrevTrack + Key name for hotkeys in menus. Use abbreviation if possible. + + + NextTrack + Key name for hotkeys in menus. Use abbreviation if possible. + + + ԿրիչիԸնտրություն + Key name for hotkeys in menus. Use abbreviation if possible. + + + Լռեցնել + Key name for hotkeys in menus. Use abbreviation if possible. + + + VolDown + Key name for hotkeys in menus. Use abbreviation if possible. + + + VolUp + Key name for hotkeys in menus. Use abbreviation if possible. + + + Փոխել + + + Փոխարկել + + + Ցույց տալ զգուշացում նիշքերի ընդլայնումները փոխելիս + + + Այս ԱՀ-ն + + + Աղբաման + + + Ապապիտակված + + + Previous tab + + + Next tab + + + Close tab + + + Խմբագրել ուղին + + + Վերարկել + + + Հետարկել + + + Անվավեր տեղադրություն + + + Are you sure you want to copy these files without their properties? + + + These files have properties that can't be copied to the new location + + + These files have properties that can't be moved to the new location + + + Are you sure you want to move these files without their properties? + + + Ցույց տալ նշատուփերը, երբ ընտրում եք տարրերը + + + Edit path in the OmniBar + + + Ստեղծել նոր տարր + + + No groups or users have permission to access this object. However, the owner of this object can assign permissions. + + + Open the item's location + + + Ջնջնել ընդմիշտ + + + Delete selected {0, plural, one {item} other {items}} permanently + + + Play the selected media files + + + Հետարկել վերջին նիշքի նախապատրաստում + + + Վերարկել վերջին նիշքի նախապատրաստում + + + Տեղադրություն. + + + Group items by month + + + Group items by year + + + Ամիս + + + Օր + + + Toggle unit for grouping by date + + + Toggle grouping unit + + + Տարի + + + Խումբավորել ըստ ամսաթվի միավորի + + + Group items by day of date created + + + Group items by month of date created + + + Տարրերի խումբ ըստ ստեղծման տարվա ամսաթվի + + + Group items by day of date deleted + + + Group items by month of date deleted + + + Group items by year of date deleted + + + Տարրերի խումբ ըստ փոփոխման օրվա + + + Տարրերի խումբ ըստ փոփխման ամիս + + + Տարրերի խումբ ըստ փոփոխման տարվա + + + Click 'Advanced permissions' to continue. + + + You must have Read permissions to view the properties of this item. + + + To try taking ownership of the item, which includes permission to view its properties, click Change above. + + + Unable to display permissions. + + + Leave my changes on '{0}' + + + Discard my changes + + + Bring my changes to '{0}' + + + You have uncommitted changes on this branch. What would you like to do with them? + + + You have an ongoing merge with unresolved conflicts. Please resolve the conflicts or abort the merge to switch branches. + + + Abort merge and switch to '{0}' + + + Stay on '{0}' and resolve conflicts + + + Փոխարկել մասնաճյուղ + + + Մասնաճյուղեր + + + Փոխարկել + + + Նոր մասնաճուղ + + + Invalid branch name + + + Ստեղծել մասնաճուղ + + + Ստեղծել մասնաճուղ + + + Հիմնված է + + + Փոխարկել նոր մասնաճուղի + + + Create a folder with the currently selected {0, plural, one {item} other {items}} + + + Հատկություններ + + + Open File Explorer properties + + + Բացել հատկությունների պատուհանը + + + Բացեք Նիշքախույզի հատկությունների պատուհանը + + + Տեղայիններ + + + Հեռակառավարումներ + + + Թարգմանել Crowdin-ում + + + Բերել + + + Իջեցնել + + + Քշել git fetch-ը + + + Clone a git repo + + + Քշել git pull + + + Նախադիտում + + + Git-ի վիճակ + + + Git-ի սխալ + + + git pull failed due to a timeout error + + + (բազմաթիվ արժեքներ) + Text indicating that multiple selected files have different metadata values. + + + Հեղինակ + + + Կատարման ամսաթիվ + + + Կատարման ուղերձ + + + Պարտավորվել SHA + + + Git + + + Ջրաներկ + + + Փայլար + + + Փայլար Այլ + + + Հետնանյութ + + + Պինդ + + + Կառվարաել ճյուղերը + + + Հրել + + + Քշել git push + + + Հմժմ + + + Run git pull and then git push + + + {0} outgoing / {1} incoming commits + + + Կապակցել GitHub-ին + + + Enter the following code at the link below to start working with GitHub repositories. + + + Files-ը չի կարող մտնել GitHub այս պահին + + + Բացել պանակը {0}-ում + + + Open the current directory in {0} + + + Բացել պահուստը առ {0} + + + Open the root of the Git repo in {0} + + + Պատճենել կոդը + + + Հավելված + + + Ջնջված + + + Փոփոխված + + + Անհետեւելի + + + Unable to display current owner. + + + Great! You are now authorized. + + + Գործարկել պահուստը + + + Initialize current folder as a git repository + + + Remote Repository Name + + + Ընթացիկ ճյուղ + + + Ուղու սյունակ + + + Sort items by path + + + Open selected directory in a new pane + + + Open selected directory in a new tab + + + Open selected directory in a new window + + + Բացել բոլորը + + + Open all tagged items + + + Հիշասարքներ ({0}) + {0} is the drive letter. + + + Սխալ հրաման + + + «{0}»-ը չի ճանաչվում որպես հրաման: + + + Command not executable + + + The '{0}' command is not ready to be executed. + + + Command Palette + + + Open Command Palette in the OmniBar + + + Սկսել ի. + + + Միացնել BitLocker-ը + + + Կառավարել BitLocker-ը + + + The following items are too large to be copied to this drive + + + Ձեւաչափել հիշասարք + + + Այլեւս չցուցադրել + + + Files is running as administrator + + + Windows-ի եւ WinAppSdk-ի սահմանափակման պատճառով, քաշել եւ գցել հնարավոր չէ Files-ը որպես վարիչ գործարկելիս: Եթե ​​ցանկանում եք օգտագործել քաշել եւ գցել, կարող եք հաղթահարել այս սահմանափակումը՝ բացելով UAC-ը (User Account Control) Մեկնարկի Ցանկից եւ ընտրելով երրորդ մակարդակը ու վերագործարկելով Windows-ը: Հակառակ դեպքում, դուք կարող եք շարունակել օգտագործել Files-ը՝ առանց քաշելու և գցելու: + + + Թողել հավելվածի աշխատի խորքում, երբ պատուհանը փակ է + + + Կապույտ + One of the custom color themes + + + Կապտագորշ + One of the custom color themes + + + Աղյուսե Կարմիր + One of the custom color themes + + + Հողագույն + One of the custom color themes + + + Սառը Պայծառ Կապույտ + One of the custom color themes + + + Գորշ + One of the custom color themes + + + Մուգ Գորշ + One of the custom color themes + + + Կանաչ + One of the custom color themes + + + Հիրիկ Կավիճ + One of the custom color themes + + + Բաց Անանուխ + One of the custom color themes + + + Մուգ Կարմիր + One of the custom color themes + + + Պայծառ Նարնջ + One of the custom color themes + + + Ամպամած + One of the custom color themes + + + Կարմիր + One of the custom color themes + + + Պայծառ Վարդ + One of the custom color themes + + + Ծովափրփուռ + One of the custom color themes + + + Փոթորիկ + One of the custom color themes + + + Բաց Կարմիր Մանուշակ + One of the custom color themes + + + Ոսկե Դեղին + One of the custom color themes + + + Մաքրել ավարտածները + + + Անուն. + + + {0} of {1} processed + Shown in a StatusCenter card. Used as "64 MB of 1 GB processed" + + + տարր + + + Files can't initialize this directory as a Git repository. + + + Contributing Artist + + + Հավելել էջ + + + Processing items... + + + Discovering items... + + + Արագություն. + + + Canceled compressing {0} items to "{1}" + Shown in a StatusCenter card. + + + Canceled compressing {0} items from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Compressed {0, plural, one {# item} other {# items}} to "{1}" + Shown in a StatusCenter card. + + + Compressed {0, plural, one {# item} other {# items}} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Error compressing {0, plural, one {# item} other {# items}} to "{1}" + Shown in a StatusCenter card. + + + Failed to compress {0, plural, one {# item} other {# items}} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Compressing {0, plural, one {# item} other {# items}} to "{1}" + Shown in a StatusCenter card. + + + Compressing {0, plural, one {# item} other {# items}} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Canceled cloning {0} to "{1}" + Shown in a StatusCenter card. + + + Canceled cloning {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Cloned "{0}" to "{1}" + Shown in a StatusCenter card. + + + Cloned {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Error cloning "{0}" to "{1}" + Shown in a StatusCenter card. + + + Failed to clone {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Cloning "{0}" to "{1}" + Shown in a StatusCenter card. + + + Cloning {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Canceled installing {0} fonts + Shown in a StatusCenter card. + + + Canceled installing {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Error installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Failed to install {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Canceled copying {0, plural, one {# item} other {# items}} to "{1}" + Shown in a StatusCenter card. + + + Canceled copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Copied {0, plural, one {# item} other {# items}} to "{1}" + Shown in a StatusCenter card. + + + Copied {0, plural, one {# item} other {# items}} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Error copying {0, plural, one {# item} other {# items}} to "{1}" + Shown in a StatusCenter card. + + + Failed to copy {0, plural, one {# item} other {# items}} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Copying {0, plural, one {# item} other {# items}} to "{1}" + Shown in a StatusCenter card. + + + Copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Discovered {0, plural, one {# item} other {# items}} + Shown in a StatusCenter card during file discovery phase. + + + Canceled extracting "{0}" to "{1}" + Shown in a StatusCenter card. + + + Canceled extracting "{0}" from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Արտահանել «{0}» դեպի «{1}» + Shown in a StatusCenter card. + + + Extracted "{0}" from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Error extracting "{0}" to "{1}" + Shown in a StatusCenter card. + + + Failed to extract "{0}" from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Արտահանում «{0}»-ից «{1}» + Shown in a StatusCenter card. + + + Extracting "{0}" from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Canceled deleting {0, plural, one {# item} other {# items}} from "{1}" + Shown in a StatusCenter card. + + + Deleted {0, plural, one {# item} other {# items}} from "{1}" + Shown in a StatusCenter card. + + + Error deleting {0, plural, one {# item} other {# items}} from "{1}" + Shown in a StatusCenter card. + + + Failed to delete {0, plural, one {# item} other {# items}} from "{1}" + Shown in a StatusCenter card. + + + Deleting {0, plural, one {# item} other {# items}} from "{1}" + Shown in a StatusCenter card. + + + Canceled moving {0, plural, one {# item} other {# items}} to "{1}" + Shown in a StatusCenter card. + + + Canceled moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Moved {0, plural, one {# item} other {# items}} to "{1}" + Shown in a StatusCenter card. + + + Moved {0, plural, one {# item} other {# items}} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Moving {0, plural, one {# item} other {# items}} to "{1}" + Shown in a StatusCenter card. + + + Moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Error moving {0, plural, one {# item} other {# items}} to "{1}" + Shown in a StatusCenter card. + + + Failed to move {0, plural, one {# item} other {# items}} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Canceled emptying Recycle Bin + Shown in a StatusCenter card. + + + Emptied Recycle Bin + Shown in a StatusCenter card. + + + Emptying Recycle Bin + Shown in a StatusCenter card. + + + Սխալ Աղբարկղը դատարկելուց + Shown in a StatusCenter card. + + + Աղբարկղը դատակելը ձախողվեց + Shown in a StatusCenter card. + + + Գործակաման նախապատրաստում... + Shown in a StatusCenter card. + + + {0}/{1, plural, one {# item} other {# items}} processed + Shown in a StatusCenter card. Used as "8/20 items processed" + + + Unblock downloaded file + + + Չկան ընթացիկ նիշքի գործողություններ + + + Windows Startup-ում Files-ը բացելու տարբերակը հասանելի չէ ձեր համակարգի կայանքների կամ խմբային քաղաքականության պատճառով: Վերամիացնելու համար բացեք գործարկման էջը Խնդիրների Կառավարիչում: + + + Failed to restore items from Recycle Bin + + + Failed to set the background wallpaper + + + Failed to open the settings file + + + Ջնջել Git մասնաճյուղը + + + Իսկապե՞ս ուզում եք ընդմիշտ ջնջել «{0}» մասնաճյուղը: + + + Կապակցված է GitHub-ին + + + Դուրս գալ + + + Կապակցել GitHub-ին + + + Մտնել + + + Tags are currently only compatible on drives formatted as NTFS. + + + There was an error applying this tag + + + Extract selected {0, plural, one {archive} other {archives}} here for single-item or to new folder for multi-item + + + Արտահանել այստեղ (Խելացի) + + + Դասավորել նիշքերը եւ պանակները միասին + + + Sort files and folders in the same list + + + Դասավորել առաջինը նիշքերը + + + Դասավորել նիշքեր + + + Դասավորել առաջինը պանակները + + + Դասավորել առաջինը պանակները, հետո նիշքերը + + + Դասավորել առաջնահերթությունը + + + Failed to rotate the image + + + Վերագործարել + + + Ելք + + + Failed to share items + + + Վերեւ նավարկելիս ոլորեք դեպի նախորդ պանակ + + + Բացել նոր պատուհան + + + Փոխել ալբոմի շապիկը + + + Failed to rename item + + + Editing "{0}" requires additional permissions + + + Խմբագրել թույլատությունները + + + Նիշքերը դեռ աշխատում են խորքում՝ գործարկման արդյունավետությունը բարելավելու համար: + + + Ո՞ւր է գնացել Files-ը + + + Հարցեր և քննարկումներ + + + Սեղմ + Used to describe layout sizes + + + Փոքր + Used to describe layout sizes + + + Միջին + Used to describe layout sizes + + + Միջին + + Used to describe layout sizes + + + Միջին ++ + Used to describe layout sizes + + + Միջին +++ + Used to describe layout sizes + + + Միջին ++++ + Used to describe layout sizes + + + Միջին +++++ + Used to describe layout sizes + + + Սեծ + Used to describe layout sizes + + + Մեծ + + Used to describe layout sizes + + + Սեծ ++ + Used to describe layout sizes + + + Մեծ +++ + Used to describe layout sizes + + + Չափազանց մեծ + Used to describe layout sizes + + + Մանրամասների դիտում + + + Դիրքի տեսակ + + + Գործողություններ + + + Հրամաններ + + + Հավելել հրաման + + + Վերականգնել լռելյայնի + + + Ընտրել գործողություն + + + Իսկապե՞ս ուզում եք վերականգնել լռելյայն ստեղների կապումը: Այս գործողությունը հնարավոր չէ հետարկել + + + Այս ստեղիտ կապումն արդեն օգտագործվում է, խնդրում ենք ընտրել այլ ստեղն՝ շարունակելու համար: + + + The key binding you choose cannot be used, please try again using a different key binding. + + + Անհատականեցված + + + Հարմարվողական դիրքը հասանելի չէ, երբ նախապատվությունները համաժամացվում են։ Լռելյայն դիրքը փոխվել է Մանրամասների: + + + Խորքի պատկեր + + + Ներքեւում + Image alignment type + + + Կենտրոնում + Image alignment type + + + Լցնել + Image stretch type + + + Ուղղահայաց հավասարեցում + + + Պատկերի լցում + + + Ձախ + Image alignment type + + + Անթափանցիկություն + + + Աջ + Image alignment type + + + Վեր + Image alignment type + + + Միաձեւ + Image stretch type + + + Միաձեւ լցնել + Image stretch type + + + Ուղղաձիգ հավասարեցում + + + Բարակ Ջրաներկ + This is a type of backdrop for the application background + + + Show for all locations + Setting where users can choose to display "Open IDE" button for Git Repos + + + Մշակողի գործիքներ + + + Configure the "Open IDE" button on the status bar + + + Show for Git repos + Setting where users can choose to display "Open IDE" button for all locations. + + + Հավելվածի ընդլայնում + This is the friendly name for DLL files. + + + ICO Նիշք + This is the friendly name for ICO files. + + + ICL Նիշք + This is the friendly name for ICL files. + + + Zip Նիշք + This is the friendly name for ZIP files. + + + Կետապատկերային Նիշքեր + This is the friendly name for bitmap files. + + + Image Files + This is the friendly name for image files. + + + Համար + + + Ցանցի տեղադրություններ + + + There are no network locations. If you don't use network locations, you can disable the widget. + + + Անջատել + + + Խմբագրել Նոթատետրում + + + Edit the selected file in Notepad + + + Չափսեր․ + + + Վիճակ. + + + Ցույց տալ գործիքագոտին + Setting that controls if the toolbar is shown in the main view + + + Ներդիրի գործողությունների ցանկ + + + Split vertically + + + Split pane vertically + + + Split horizontally + + + Split pane horizontally + + + Arrange vertically + + + Arrange panes vertically + + + Arrange horizontally + + + Arrange panes horizontally + + + Split pane + + + Show tab actions button in the title bar + + + Դասավորել փեղկերը + + + Default dual pane split direction + + + Dual pane mode + + + Ուղղահայաց + + + Ուղղահայաց + + + Show Files icon in the System Tray + + + ԿՄՄ հոսքեր + + + Նավարկել դեպի տնէջ + + + Գործիքագոտիներ + + + Օգտվողի ID + + + Խմբային վերանվանում + + + Սեղմել բովանդակությունը + + + Show option to create alternate data stream + + + Create alternate data stream + + + Create alternate data stream for the selected {0, plural, one {item} other {items}} + + + Enter data stream name + + + There was an error creating the alternate data stream + + + Խնդրում ենք նկատի ունենալ, որ այլընտրանքային տվյալների հոսքերն աշխատում են միայն NTFS ձեւաչափված հիշասարքում: + + + Alternate data streams are currently hidden + + + Would you like to display alternate data streams? You can modify this setting anytime from the files and folders settings page. + + + Կառավարել պիտակները + + + Հասանելի + + + Ընդամենը + + + Always switch focus to newly created tab + + + Toggle shelf pane + + + Toggle the visibility of the shelf pane + + + Show shelf pane toggle in address bar + + + Դարակ + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + Մաքրել միույթները + + + Հեռացնել դարակից + + + Հավելել դարակին + Tooltip that displays when dragging items to the Shelf Pane + + + Enter a hash to compare + Placeholder that appears in the compare hash text box + + + Matches {0} + Appears when two compared hashes match, e.g. "Matches SHA256" + + + No matches found + Appears when two compared hashes don't match + + + Ուղի կամ կեղծանուն + + + Անվավեր ուղի + + + Փորձնական ներկառուցում + + + {0} could not be located. Please check your settings and try again. + + + The configured IDE could not be located + + + Բացել կայանքները + + + Visual Studio Code + + + Enter a path or launch alias + + + Please enter a name for the IDE + + + Clone repo + Clone repo dialog title + + + Clone + Primary action button in the clone repo dialog + + + Repository URL + URL textbox header in the clone repo dialog + + + Cannot clone repo + Cannot clone repo dialog title + + + Compare a file + Button that appears in file hash properties that allows the user to compare two files + + + Size format + + + Binary + + + Decimal + + + You can add sections to the sidebar by right-clicking and selecting the sections you want to add + + + Drag files or folders here to interact with them across different tabs + + + Enter a path to navigate to... + + + Find features and commands... + + + Search for files and folders... + + + During file operations + + + Show status center button + + + Status center progress ring + Screen reader name for the status center progress ring + + + Icon files + This is the friendly name for a variety of different icon files. + + + Show collapsed path breadcrumbs + + + Show folders in {0} + + + Show folders in Home + + + There are no commands containing {0} + + + See more + + + Filtering for + + + Filename + + + Signatures + + + Signature list + + + Issued by: + + + Issued to: + + + Valid from: + + + Valid to: + + + No signature was found. + + + Show option to open folders in Windows Terminal + + + Open log file + + + Open log file in your default editor + + + Open log file location in your default file manager + + + Unable to open the log file + + + Show status bar + + + Only in Columns View + + + New {0} + + + Enable smooth scrolling + + + Scrolling + + + Show option to Pin to Sidebar + + + Show option to Pin to the Start Menu + + \ No newline at end of file diff --git a/src/Files.App/Strings/id-ID/Resources.resw b/src/Files.App/Strings/id-ID/Resources.resw index daf5c2c2372f..7fe757c94781 100644 --- a/src/Files.App/Strings/id-ID/Resources.resw +++ b/src/Files.App/Strings/id-ID/Resources.resw @@ -118,13 +118,19 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Window baru + Jendela baru Salin jalur + + Salin jalur item + - Salin path dengan kutipan + Salin jalur dengan kutip + + + Salin direktori berkas dengan kutip Jelajahi @@ -160,10 +166,10 @@ Lewati - Pilih Semua + Pilih semua - Balikkan seleksi + Balikkan pilihan Hapus pilihan @@ -184,7 +190,7 @@ Cari - File yang sebelumnya Anda akses akan muncul di sini + Berkas yang sebelumnya Anda akses akan muncul di sini Hapus item ini @@ -211,13 +217,13 @@ Tampilkan file dan folder tersembunyi - Tampilkan dot files + Tampilkan berkas dot Tampilan - Warna Latar Belakang + Warna latar belakang Lanjutan @@ -249,6 +255,9 @@ Tempel + + Tempel Pintasan + Baru @@ -274,7 +283,7 @@ Hapus - Pin ke Sidebar + Sematkan ke Bilah Sisi Selamat datang di Files! @@ -286,13 +295,13 @@ Untuk memulai, Anda harus memberi kami izin untuk menampilkan file Anda. Ini akan membuka halaman Pengaturan di mana Anda dapat memberi kami izin ini. Anda harus membuka kembali aplikasi setelah menyelesaikan langkah ini. - Menampilkan + Tampilan Masukkan nama item - - Tentukan nama + + Buat baru {0} Terang @@ -300,6 +309,9 @@ Gelap + + Gunakan pengaturan sistem + Aplikasi @@ -322,7 +334,7 @@ Maju - Naik (Alt+Panah Atas) + Naik Segarkan @@ -340,52 +352,52 @@ Tetapkan sebagai latar belakang layar kunci - Atur sebagai latar belakang app + Atur sebagai latar belakang aplikasi Kami tidak dapat menghapus item ini - Silakan masukkan drive yang diperlukan untuk mengakses item ini. + Silakan masukkan cakram yang diperlukan untuk mengakses item ini. Penyimpanan dilepas - File yang Anda coba akses mungkin telah dipindah atau dihapus. + Berkas yang Anda coba akses mungkin telah dipindahkan atau dihapus. - Tidak dapat membuka propeti file ini + Tidak dapat membuka propeti berkas ini - File yang Anda coba akses mungkin telah dipindah atau dihapus. + Berkas yang Anda coba akses mungkin telah dipindahkan atau dihapus. - File Tidak Ditemukan + Berkas Tidak Ditemukan - Folder yang Anda coba akses mungkin telah dipindah atau dihapus. + Folder yang Anda coba akses mungkin telah dipindahkan atau dihapus. Apakah Anda menghapus folder ini? - File yang Anda coba akses saat ini sedang digunakan oleh {0} + Berkas yang Anda coba akses sedang digunakan oleh {0} - File yang Anda coba akses saat ini sedang digunakan oleh aplikasi lain + Berkas yang Anda coba akses sedang digunakan oleh aplikasi lain - File sedang digunakan + Berkas sedang digunakan Tata letak - Baca-saja + Baca saja - {0, plural, one {# day ago} other {# days ago}} + {0, plural,one {# hari lalu}other {# hari lalu}} 1 hari yang lalu @@ -394,7 +406,7 @@ {0} hari yang lalu - {0, plural, one {# hour ago} other {# hours ago}} + {0, plural,one {# jam lalu}other {# jam lalu}} 1 jam yang lalu @@ -403,7 +415,7 @@ {0} jam yang lalu - {0, plural, one {# minute ago} other {# minutes ago}} + {0, plural,one {# menit lalu} other {# menit lalu}} 1 menit yang lalu @@ -412,7 +424,7 @@ {0} menit yang lalu - {0, plural, one {# second ago} other {# seconds ago}} + {0, plural, one {# detik lalu} other {#detik lalu}} 1 detik yang lalu @@ -427,7 +439,7 @@ Operasi yang diminta tidak didukung - {0} ruang bebas dari {1} + Tersisa {0} dari {1} Kirim ke @@ -477,10 +489,10 @@ B - {0, number} {0, plural, one {file} other {files}}, {1, number} {1, plural, one {folder} other {folders}} + {0, number} {0, plural, one {berkas} other {berkas}}, {1, number} {1, plural, one {folder} other {folder}} - {0, number} {0, plural, one {file} other {files}}, {1, number} {1, plural, one {folder} other {folders}} from {2, number} {2, plural, one {location} other {locations}} + {0, number} {0, plural, one {berkas} other {berkas}}, {1, number} {1, plural, one {folder} other {folders}} from {2, number} {2, plural, one {lokasi} other {lokasi}} Jalankan sebagai pengguna lain @@ -498,7 +510,7 @@ Ruang terpakai: - Ruang bebas: + Ruang kosong: Kapasitas: @@ -515,9 +527,6 @@ Status - - Bawaan Windows - Tidak ada hasil @@ -530,11 +539,14 @@ Salin ke {0} + + Salin ke {0} + Buat pintasan - Buka lokasi file + Buka lokasi berkas Argumen: @@ -998,6 +1010,9 @@ Hasil pencarian di {1} untuk {0} + + Hasil pencarian untuk `{0}` + Rincian @@ -1047,14 +1062,11 @@ Buka di panel baru - File dan folder + Folder & berkas Rincian (Ctrl+Shift+1) - - Ubin (Ctrl+Shift+2) - Tanggal dihapus @@ -1080,10 +1092,25 @@ Aktifkan panel info - Aktifkan panel info untuk melihat detail/pratinjau panel + Tampilkan/sembunyikan panel rincian/pratinjau - Aktifkan Toolbar + Tampilkan toolbar + + + Tampilkan/sembunyikan bilah alat + + + Tampilkan/sembunyikan header filter + + + Tampilkan/sembunyikan header filter + + + Tampilkan/sembunyikan panel ganda + + + Tampilkan/sembunyikan mode panel ganda Pratinjau tidak tersedia @@ -1238,41 +1265,47 @@ Buka Sensor Penyimpanan + + Buka halaman Storage Sense di Pengaturan Windows + + + Bersihkan + Salin - Copy {0, plural, one {item} other {items}} + Salin {0, plural, one {item} other {item}} - Delete {0, plural, one {item} other {items}} + Hapus {0, plural, one {item} other {item}} Pindah - {0, plural, one {One item will be moved} other {# items will be moved}} + {0, plural, one {Satu item akan dipindahkan} other {# item akan dipindahkan}} - Move {0, plural, one {item} other {items}} + Pindahkan {0, plural, one {item} other {item}} - {0, plural, one {One item will be deleted} other {# items will be deleted}} + {0, plural, one {Satu item akan dihapus} other {# item akan dihapus}} Lanjutkan - {0, plural, one {There is one conflicting file name} other {There are # conflicting file names}} + {0, plural, one {Ada satu nama berkas yang konflik} other {Ada # nama berkas yang konflik}} - {0, plural, one {There is one conflicting file name} other {There are # conflicting file names}}, and {1, plural, one {one outgoing item} other {# outgoing items}} + {0, plural, one {Ada satu nama berkas yang konflik} other {Ada # nama berkas yang konflik}}, dan {1, plural, one {satu item keluar} other {# item keluar}} - Conflicting {0, plural, one {file name} other {file names}} + {0, plural, one {nama berkas yang konflik} other {nama berkas yang konflik}} - {0, plural, one {One item will be copied} other {# items will be copied}} + {0, plural, one {Satu item akan disalin} other {# item akan disalin}} Hapus secara permanen @@ -1587,7 +1620,7 @@ Ganti semua entri izin objek bawahan dengan entri izin yang dapat diwariskan dari objek ini - Tutup panel aktif + Tutup panel Masuk ke tampilan fleksibel @@ -1617,7 +1650,7 @@ Unduh item dari cloud dan buka pratinjau - {0, plural, one {# item} other {# items}} ({1, plural, one {# file} other {# files}}, {2, plural, one {# folder} other {# folders}}) + {0, plural, one {# item} other {# item}} ({1, plural, one {# berkas} other {# berkas}}, {2, plural, one {# folder} other {# folder}}) Ukuran tidak terkompresi @@ -1646,15 +1679,6 @@ Beranda - - Ctrl+Shift+1 - - - Ctrl+Shift+2 - - - Ctrl+Shift+6 - Ekstraksi arsip berhasil diselesaikan. @@ -1704,7 +1728,7 @@ Tidak ada item yang ditemukan - Buka lokasi log + Buka direktori log Buat tautan di {0} @@ -1712,8 +1736,8 @@ Kolom - - Ubin + + Kartu Buka folder di tab baru @@ -1734,10 +1758,10 @@ Bantuan dan dukungan - Kirim permintaan fitur + Minta fitur baru - Kirim laporan bug + Laporkan bug Atur Files sebagai pengelola file bawaan @@ -1749,7 +1773,7 @@ Tag - Buka item dengan sekali klik + Buka beberapa file dengan satu klik Jalur folder @@ -1770,7 +1794,7 @@ Kosongkan Keranjang Sampah - Bilah sisi + Panel Samping Pilih ikon folder kustom @@ -1826,6 +1850,18 @@ Daftarkan program untuk di muat ulang + + Jendela mulai + + + Jendela normal + + + Perkecil + + + Perbesar + Jalankan sebagai administrator @@ -1853,6 +1889,9 @@ Opsi ini mengubah registri sistem dan dapat memiliki efek samping yang tidak terduga pada perangkat Anda. Lanjutkan dengan risiko Anda sendiri. + + Operasi meratakan bersifat permanen dan tidak disarankan. Lanjutkan atas risiko Anda sendiri. + Buat Pustaka @@ -1931,6 +1970,9 @@ Tutup tab lain + + Tutup semua tab + Musik @@ -1941,7 +1983,7 @@ Perbarui Files - Tag + Penanda Universal @@ -1971,7 +2013,7 @@ Atur semua ukuran kolom ke pas - Menyesuaikan + Penyesuaian Adaptif (Ctrl+Shift+7) @@ -1985,11 +2027,23 @@ Perilaku - - Ulas Files + + Halo! + + + Menikmati Files? Mohon pertimbangkan untuk memberi ulasan di Microsoft Store. - - Apakah Anda ingin memberikan ulasan Files? + + Menikmati Files? Mohon pertimbangkan untuk memberi ulasan di GitHub. + + + Sponsor + + + Beri Nilai Kami + + + Abaikan Atur sebagai latar belakang @@ -2018,8 +2072,8 @@ Layar kunci - - Buka folder dengan sekali klik di Tata Letak Kolom + + Buka beberapa folder dengan satu klik Membuka item @@ -2027,6 +2081,21 @@ Kompres + + Pindahkan semua konten dari subfolder ke lokasi yang dipilih + + + Ratakan folder + + + Ratakan + + + Meratakan folder akan memindahkan semua isi dari subfolder ke lokasi yang dipilih. Operasi ini bersifat permanen dan tidak dapat dibatalkan. Dengan menggunakan fitur eksperimental ini, Anda mengakui risikonya dan setuju untuk tidak menuntut tim Files atas kehilangan data apa pun. + + + Tampilkan opsi perataan + Pilih file dan folder saat mengarahkan kursor ke atasnya @@ -2037,7 +2106,7 @@ Pulihkan semua item - Apakah Anda ingin memulihkan {0} item yang dipilih? + Apakah Anda ingin memulihkan {0, plural, one {item terpilih} other {{0} item terpilih}}? Pulihkan yang dipilih @@ -2060,6 +2129,12 @@ Kata sandi arsip + + Pengodean + + + {0} (terdeteksi) + Jalur @@ -2102,9 +2177,24 @@ Ukuran pemisahan - + Jangan dipisah + + Auto + + + Dictionary size + + + Word size + + + Estimated memory usage: {0} + + + Available memory: {0} + CD @@ -2159,8 +2249,14 @@ Ubah file pengaturan - - Yang baru + + Buka pengaturan file di editor default Anda + + + Catatan Rilis + + + Buka Catatan Rilis Membuat pintasan di lokasi ini memerlukan hak istimewa administrator @@ -2241,25 +2337,22 @@ Opsi menu konteks - Tampilkan ekstensi file - - - Tampilkan item tersembunyi + Tampilkan ekstensi berkas - Format... + Format Bantuan - Beralih ke layar penuh + Layar penuh Yakin ingin menghapus tag ini? - Putar semua + Putar Tinggi @@ -2319,7 +2412,7 @@ Tingkatkan ukuran - Pilih arah pengurutan + Arah Pengurutan Nama tidak valid @@ -2331,34 +2424,37 @@ Video - Luncurkan pratinjau popup + Pratinjau di jendela pop-up - Tampilkan overlay kompak + Beralih ke tampilan fleksibel - Buka halaman bantuan daring di browser + Buka halaman bantuan secara daring di browser Beralih ke layar penuh - Masuk ke tampilan fleksibel + Masuk ke tampilan kecil - Keluar dari tampilan fleksibel + Keluar dari tampilan kecil - Pilih overlay kompak + Beralih ke tampilan kecil - Pergi ke kotak pencarian + Memulai pencarian di OmniBar - Pilih untuk menunjukkan atau menyembunyikan item + Ubah untuk melihat berkas yang disembunyikan + + + Ubah untuk melihat dot files - Pilih untuk menunjukkan ekstensi berkas + Ubah untuk melihat ekstensi dari suatu berkas Aktifkan paratinjau panel untuk melihat paratinjau file @@ -2367,52 +2463,64 @@ Pilih untuk menujukkan sidebar - salin item(s) ke clipboard + Salin {0, plural, one {item terpilih} other {item terpilih}} - Salin path dari item yang dipilih ke clipboard + Salin jalur direktori saat ini + + + Salin jalur item terpilih + + + Salin jalur item terpilih dengan tanda kutip - Salin path dari item yang dipilih dengan kutipan ke clipboard + Salin jalur direktori saat ini dengan tanda kutip - Salin item(s) ke clipboard + Potong {0, plural, one {item terpilih} other {item terpilih}} - Tempel item(s) dari clipboard ke folder yang sekarang + Tempel item ke folder saat ini + + + Tempel item ke folder saat ini sebagai pintasan - Tempel item(s) dari clipboard ke folder yang dipilih + Tempel item ke folder yang dipilih + + + Tempel ke folder yang dipilih - Hapus item + Hapus {0, plural, one {item terpilih} other {item terpilih}} Buat folder baru - Buat shortcut(s) baru ke item(s) yang dipilih + Buat {0, plural, one {pintasan baru} other {pintasan baru}} ke {0, plural, one {item terpilih} other {item terpilih}} Buat shortcut baru ke item manapun - Kosongkan keranjang sampah + Kosongkan tempat pembuangan Buka "Format Penyimpanan" menu pada item yang dipilih - Kembalikan item(s) terpilih dari recycle bin + Pulihkan {0, plural, one {item terpilih} other {item terpilih}} dari tempat sampah Mengembalikan semua item dari tempat sampah - Buka item + Buka {0, plural, one {item terpilih} other {item terpilih}} - Buka item dengan aplikasi yang dipilih + Buka {0, plural, one {item terpilih} other {item terpilih}} dengan aplikasi terpilih Buka folder induk pada item yang dicari @@ -2427,28 +2535,28 @@ Pilih semua item - Balikan item pilihan + Membalikkan item yang dipilih sebelumnya - Hapus item yang dipilih + Bersihkan item terpilih Aktifkan item pilihan - Bagikan item(s) yang terpilih dengan orang lain + Bagikan {0, plural, one {berkas terpilih} other {berkas terpilih}} dengan lainnya - Sematkan item(s) ke Start Menu + Sematkan {0, plural, one {item terpilih} other {item terpilih}} ke Menu Mulai - Unpin item(s) dari Start Menu + Lepaskan sematan {0, plural, one {item terpilih} other {item terpilih}} dari Menu Mulai - Pin folder(s) ke Sidebar + Sematkan {0, plural, one {folder terpilih} other {folder terpilih}} ke Sidebar - Unpin folder(s) dari Sidebar + Lepaskan sematan {0, plural, one {folder terpilih} other {folder terpilih}} dari Sidebar Gunakan gambar yang diilih sebagai desktop background @@ -2462,14 +2570,23 @@ Gunakan gambar terpilih sebagai latar belakang aplikasi + + Instal font + + + Instal driver + + + Instal sertifikat + - Instal font yang dipilih + Instal {0, plural, one {font terpilih} other {font terpilih}} - Pasang driver(s) menggunakan file(s) inf terpilih + Instal {0, plural, one {driver terpilih} other {driver terpilih}} menggunakan inf {0, plural, one {terpilih} other {terpilih}} - Pasang sertifikat(s) terpilih + Instal {0, plural, one {sertifikat terpilih} other {sertifikat terpilih}} Jalankan aplikasi terpilih sebagai admin @@ -2481,31 +2598,31 @@ Jalankan skrip PowerShell yang dipilih - Luncurkan pratinjau di jendela popup + Luncurkan pratinjau di jendela pop-up - Buat arsip dari file(s) terpilih + Buat arsip dengan {0, plural, one {item terpilih} other {item terpilih}} - Buat 7z arsip dari item(s) yang dipilih + Buat arsip 7z dengan {0, plural, one {item terpilih} other {item terpilih}} - Buat arsip zip dari item(s) yang dipilih + Buat arsip zip dengan {0, plural, one {item terpilih} other {item terpilih}} - Ekstrak item dari arsip(s) yang dipilih ke folder + Ekstrak {0, plural, one {arsip terpilih} other {arsip terpilih}} ke folder mana saja - Ekstrak item dari arsip(s) yang dipilih ke folder sekarang + Ekstrak {0, plural, one {arsip terpilih} other {arsip terpilih}} dari folder saat ini - Ekstrak item dari arsip(s) yang dipilih ke folder baru + Ekstrak {0, plural, one {arsip terpilih} other {arsip terpilih}} ke folder baru - Putar gambar(s) terpilih ke kiri + Putar {0, plural, one {gambar terpilih} other {gambar terpilih}} ke kiri - Putar gambar(s) terpilih ke kanan + Putar {0, plural, one {gambar terpilih} other {gambar terpilih}} ke kanan Buka halaman pengaturan @@ -2525,8 +2642,8 @@ Beralih ke tampilan detail - - Beralih ke tampilan tiles + + Alih ke tampilan kartu Beralih ke tampilan daftar @@ -2628,19 +2745,19 @@ Buka tab baru - Navigasi mundur dalam riwayat navigasi + Kembali ke langkah sebelumnya - Navigasi kedepan dalam riwayat navigasi + Maju ke langkah selanjutnya - Navigasi keatas satu folder + Mundur selangkah dari folder ini - Duplikasi tab sekarang + Gandakan tab ini - Duplikasi tab terpilih + Gandakan tab yang dipilih Tutup tab di sebelah kiri tab saat ini @@ -2660,6 +2777,9 @@ Tutup tab selain tab yang dipilih + + Tutup seluruh tab termasuk yang ini + Buka kembali halaman yang terakhir ditutup @@ -2670,7 +2790,7 @@ Pindah ke tab berikutnya - Tutup tab saat ini + Tutup tab ini Tutup panel aktif @@ -2679,7 +2799,7 @@ Fokus panel lainnya - Ubah fokus ke panel yang tidak aktif + Alih fokus ke panel lainnya Aktifkan sidebar @@ -2887,13 +3007,13 @@ Tidak ditandai - Pindah ke tab sebelumnya + Tab sebelumnya - Pindah ke tab berikutnya + Tab selanjutnya - Tutup tab ini + Tutup Tab Ubah jalur @@ -2923,7 +3043,7 @@ Tampilkan kotak centang saat memilih item - Jalur bar fokus + Ubah jalur di OmniBar Buat item baru @@ -2938,16 +3058,16 @@ Hapus permanen - Hapus item(s) secara permanen + Hapus {0, plural, one {item terpilih} other {item terpilih}} secara permanen Putar media file yang terpilih - Undo operasi file sebelumnya + Urungkan aksi pada file - Redo operasi file sebelumnya + Ulangi aksi setelahnya pada file Lokasi: @@ -3027,6 +3147,15 @@ Anda memiliki perubahan yang belum di commit pada branch ini. Apa yang anda ingin lakukan pada mereka? + + Anda memiliki penggabungan berjalan dengan konflik yang belum terselesaikan. Harap selesaikan konflik atau batalkan penggabungan untuk beralih cabang. + + + Batalkan penggabungan dan beralih ke {0} + + + Tetap di {0} dan selesaikan konflik + Pindah branch @@ -3055,14 +3184,20 @@ Pindah ke branch baru - Buat folder baru dengan item(s) yang sedang dipilih + Buat folder dengan {0, plural, one {item terpilih} other {item terpilih}} saat ini - Buka properti + Properti + + + Buka jendela properti + + Buka jendela properti melalui File Explorer + Lokal @@ -3081,6 +3216,9 @@ Jalankan git fetch + + Kloning repositori git + Jalankan git pull @@ -3125,7 +3263,7 @@ Mica Alt - Backdrop Material + Latar Blur Solid @@ -3157,17 +3295,17 @@ Saat ini, Files tidak dapat mengakses GitHub. - - Buka folder di VS Code + + Buka folder di {0} - - Buka direktori ini pada Visual Studio Code + + Buka direktori saat ini di {0} - - Buka repo di VS Code + + Buka repositori di {0} - - Buka root repo Git di Visual Studio Code + + Buka direktori root repositori Git di {0} Salin kode @@ -3194,7 +3332,7 @@ Inisialisasi repo - Inisialisasi Repositori Git + Pilih folder ini untuk digunakan di git repository Nama Remote Repository @@ -3209,13 +3347,13 @@ Urutkan item berdasarkan jalur - Buka tautan di panel baru + Buka direktori terpilih di panel baru - Buka tautan di tab baru + Buka direktori terpilih di tab baru - Buka tautan di jendela baru + Buka tautan yang dipilih di jendela baru Buka semua @@ -3240,10 +3378,10 @@ Perintah {0} belum siap dilaksanakan. - Daftar Perintah + Daftar perintah - Buka panel perintah + Buka daftar perintah Mulai di: @@ -3373,6 +3511,9 @@ Item sedang di proses... + + Menemukan item... + Kecepatan: @@ -3385,61 +3526,129 @@ Shown in a StatusCenter card. - Kompres {0} item(s) ke "{1}" + Telah mengompresi {0, plural, one {# item} other {# item}} ke "{1}" Shown in a StatusCenter card. - Kompres {0} item(s) dari "{1}" ke "{2}" + Telah mengompresi {0, plural, one {# item} other {# item}} dari "{1}" ke "{2}" Shown in a StatusCenter card. - Gagal mengkompresi {0} item(s) ke "{1}" + Kesalahan mengompresi {0, plural, one {# item} other {# item}} ke "{1}" Shown in a StatusCenter card. - Gagal mengkompres {0} item(s) dari "{1}" ke "{2}" + Gagal mengompresi {0, plural, one {# item} other {# item}} dari "{1}" ke "{2}" Shown in a StatusCenter card. - Kompres {0} item(s) ke "{1}" + Mengompresi {0, plural, one {# item} other {# item}} ke "{1}" Shown in a StatusCenter card. - Mengompres {0} item(s) dari "{1}" ke "{2}" + Mengompresi {0, plural, one {# item} other {# item}} dari "{1}" ke "{2}" + Shown in a StatusCenter card. + + + Dibatalkan: mengkloning "{0}" ke "{1}" + Shown in a StatusCenter card. + + + Dibatalkan: mengkloning "{0}" dari "{1}" ke "{2}" + Shown in a StatusCenter card. + + + Telah mengkloning "{0}" ke "{1}" + Shown in a StatusCenter card. + + + Telah mengkloning {0} dari "{1}" ke "{2}" + Shown in a StatusCenter card. + + + Kesalahan mengkloning "{0}" ke "{1}" + Shown in a StatusCenter card. + + + Gagal mengkloning "{0}" dari "{1}" ke "{2}" + Shown in a StatusCenter card. + + + Sedang mengkloning "{0}" ke "{1}" + Shown in a StatusCenter card. + + + Sedang mengkloning "{0}" dari "{1}" ke "{2}" + Shown in a StatusCenter card. + + + Dibatalkan: menginstal "{0}" font + Shown in a StatusCenter card. + + + Dibatalkan: menginstal {0, plural, one {# font} other {# font}} dari "{1}" + Shown in a StatusCenter card. + + + Telah menginstal {0, plural, one {# font} other {# font}} + Shown in a StatusCenter card. + + + Telah menginstal {0, plural, one {# font} other {# font}} dari "{1}" + Shown in a StatusCenter card. + + + Kesalahan menginstal {0, plural, one {# font} other {# font}} + Shown in a StatusCenter card. + + + Gagal menginstal {0, plural, one {# font} other {# font}} dari "{1}" + Shown in a StatusCenter card. + + + Sedang menginstal {0, plural, one {# font} other {# font}} + Shown in a StatusCenter card. + + + Sedang menginstal {0, plural, one {# font} other {# font}} dari "{1}" Shown in a StatusCenter card. - Membatalkan penyalinan {0} item(s) ke "{1}" + Dibatalkan: menyalin {0, plural, one {# item} other {# item}} ke "{1}" Shown in a StatusCenter card. - Membatalkan penyalinan {0} item(s) dari "{1}" ke "{2}" + Dibatalkan: menyalin {0, plural, one {# item} other {# item}} dari "{1}" ke "{2}" Shown in a StatusCenter card. - Disalin {0} item(s) ke "{1}" + Telah menyalin {0, plural, one {# item} other {# item}} ke "{1}" Shown in a StatusCenter card. - Disalin {0} item(s) dari "{1}" ke "{2}" + Telah menyalin {0, plural, one {# item} other {# item}} dari "{1}" ke "{2}" Shown in a StatusCenter card. - Error menyalin {0} item(s) ke "{1}" + Kesalahan menyalin {0, plural, one {# item} other {# item}} ke "{1}" Shown in a StatusCenter card. - Gagal untuk menyalin {0} item(s) dari "{1}" ke "{2}" + Gagal menyalin {0, plural, one {# item} other {# item}} dari "{1}" ke "{2}" Shown in a StatusCenter card. - Menyalin {0} item(s) ke "{1}" + Sedang menyalin {0, plural, one {# item} other {# item}} ke "{1}" Shown in a StatusCenter card. - Menyalin {0} item(s) dari "{1}" ke "{2}" + Sedang menyalin {0, plural, one {# item} other {# item}} dari "{1}" ke "{2}" Shown in a StatusCenter card. + + Telah menemukan {0, plural, one {# item} other {# item}} + Shown in a StatusCenter card during file discovery phase. + Dibatalkan ekstrasi "{0}" ke "{1}" Shown in a StatusCenter card. @@ -3473,55 +3682,55 @@ Shown in a StatusCenter card. - Membatalkan penghapusan {0} item(s) dari "{1}" + Dibatalkan: menghapus {0, plural, one {# item} other {# item}} dari "{1}" Shown in a StatusCenter card. - Dihapus {0} item(s) dari "{1}" + Telah menghapus {0, plural, one {# item} other {# item}} dari "{1}" Shown in a StatusCenter card. - Error menghapus {0} item(s) dari "{1}" + Kesalahan menghapus {0, plural, one {# item} other {# item}} dari "{1}" Shown in a StatusCenter card. - Gagal menghapus {0} item(s) dari {1}" + Gagal menghapus {0, plural, one {# item} other {# item}} dari "{1}" Shown in a StatusCenter card. - Menghapus {0} item(s) dari "{1}" + Sedang menghapus {0, plural, one {# item} other {# item}} dari "{1}" Shown in a StatusCenter card. - Dibatalkan pemindahan {0} item(s) ke "{1}" + Dibatalkan: memindahkan {0, plural, one {# item} other {# item}} ke "{1}" Shown in a StatusCenter card. - Dibatalkan pemindahan {0} item(s) dari "{1}" ke "{2}" + Dibatalkan: memindahkan {0, plural, one {# item} other {# item}} dari "{1}" ke "{2}" Shown in a StatusCenter card. - Terpindah {0} item(s) ke "{1}" + Telah memindahkan {0, plural, one {# item} other {# item}} ke "{1}" Shown in a StatusCenter card. - Terpindah {0} item(s) dari "{1}" ke "{2}" + Telah memindahkan {0, plural, one {# item} other {# item}} dari "{1}" ke "{2}" Shown in a StatusCenter card. - Memindah {0} item(s) ke "{1}" + Sedang memindahkan {0, plural, one {# item} other {# item}} ke "{1}" Shown in a StatusCenter card. - Memindah {0} item(s) dari "{1}" ke "{2}" + Sedang memindahkan {0, plural, one {# item} other {# item}} dari "{1}" ke "{2}" Shown in a StatusCenter card. - Error memindah {0} item(s) ke "{1}" + Kesalahan memindahkan {0, plural, one {# item} other {# item}} ke "{1}" Shown in a StatusCenter card. - Gagal untuk memindah {0} item(s) dari "{1}" ke "{2}" + Gagal memindahkan {0, plural, one {# item} other {# item}} dari "{1}" ke "{2}" Shown in a StatusCenter card. @@ -3549,7 +3758,7 @@ Shown in a StatusCenter card. - {0}/{1} item(s) diproses + {0}/{1, plural, one {# item} other {# item}} diproses Shown in a StatusCenter card. Used as "8/20 items processed" @@ -3567,6 +3776,9 @@ Gagal untuk set wallpaper latar belakang + + Gagal membuka pengaturan file + Hapus Git branch @@ -3592,7 +3804,7 @@ Terjadi kesalahan saat menerapkan tag ini - Ekstrak item dari arsip(s) yang dipilih ke folder saat ini untuk arsip satu-item, atau ke folder baru untuk arsip multi-item + Ekstrak {0, plural, one {arsip terpilih} other {arsip terpilih}} di sini untuk item tunggal atau ke folder baru untuk multi-item Ekstrak disini (Smart) @@ -3601,7 +3813,7 @@ Urutkan file dan folder bersama - Urutkan file dan folder bersama + Atur berkas dan folder secara bersamaan Urutkan file dahulu @@ -3657,9 +3869,6 @@ Pertanyaan dan Diskusi - - Ukuran tambahan tidak tersedia untuk tampilan Tiles. - Sederhana Used to describe layout sizes @@ -3821,6 +4030,10 @@ Berkas ICO This is the friendly name for ICO files. + + File ICL + This is the friendly name for ICL files. + Berkas Zip This is the friendly name for ZIP files. @@ -3829,6 +4042,10 @@ Berkas Bitmap This is the friendly name for bitmap files. + + File Gambar + This is the friendly name for image files. + Num @@ -3854,23 +4071,23 @@ Status: - Tampilkan Toolbar - Setting that controls if the Toolbar is shown in the main view + Tampilkan bilah alat + Setting that controls if the toolbar is shown in the main view Aksi tab menu - - Tambah panel vertikal + + Bagi secara vertikal - Tambahkan panel vertikal + Pisah panel secara vertikal - - Tambahkan panel horizontal + + Bagi secara horizontal - - Tambahkan panel horizontal + + Pisah panel secara horizontal Susun secara vertikal @@ -3884,8 +4101,8 @@ Susun panel secara horizontal - - Tambahkan panel + + Bagi panel Tampilkan tombol aksi tab di title bar @@ -3893,8 +4110,11 @@ Susun panel - - Susunan panel default + + Arah pemisahan panel ganda default + + + Mode panel ganda Horizontal @@ -3902,4 +4122,259 @@ Vertikal + + Tampilkan ikon Files di Baki Sistem + + + Thread CPU + + + Kembali ke beranda + + + Bilah alat + + + User ID + + + Ubah nama massal + + + Kompres konten + + + Tampilkan opsi untuk membuat aliran data alternatif + + + Buat aliran data alternatif + + + Buat aliran data alternatif untuk {0, plural, one {item terpilih} other {item terpilih}} + + + Masukkan nama aliran data + + + Terjadi kesalahan saat membuat aliran data alternatif + + + Harap dicatat bahwa aliran data alternatif hanya bekerja pada drive yang diformat sebagai NTFS. + + + Aliran data alternatif saat ini tersembunyi + + + Apakah Anda ingin menampilkan aliran data alternatif? Anda dapat mengubah pengaturan ini kapan saja dari halaman pengaturan file dan folder. + + + Kelola tag + + + Tersedia + + + Total + + + Selalu alihkan fokus ke tab yang baru dibuat + + + Tampilkan/sembunyikan panel rak + + + Tampilkan/sembunyikan visibilitas panel rak + + + Tampilkan pengalih panel rak di bilah alamat + + + Rak + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + Bersihkan item + + + Hapus dari rak + + + Tambahkan ke rak + Tooltip that displays when dragging items to the Shelf Pane + + + Masukkan hash untuk dibandingkan + Placeholder that appears in the compare hash text box + + + Kecocokan {0} + Appears when two compared hashes match, e.g. "Matches SHA256" + + + Tidak ada kecocokan yang ditemukan + Appears when two compared hashes don't match + + + Jalur atau alias + + + Jalur tidak valid + + + Uji integrasi + + + {0} tidak dapat ditemukan. Harap periksa pengaturan Anda dan coba lagi. + + + IDE yang dikonfigurasi tidak dapat ditemukan + + + Buka pengaturan + + + Visual Studio Code + + + Masukkan jalur atau alias peluncuran + + + Harap masukkan nama untuk IDE + + + Kloning repositori + Clone repo dialog title + + + Kloning + Primary action button in the clone repo dialog + + + URL Repositori + URL textbox header in the clone repo dialog + + + Tidak dapat mengkloning repositori + Cannot clone repo dialog title + + + Bandingkan file + Button that appears in file hash properties that allows the user to compare two files + + + Format ukuran + + + Binari + + + Desimal + + + Anda dapat menambahkan bagian ke sidebar dengan mengklik kanan dan memilih bagian yang ingin ditambahkan + + + Seret file atau folder ke sini untuk berinteraksi dengan mereka di berbagai tab + + + Masukkan jalur tujuan... + + + Cari fitur dan perintah... + + + Cari file dan folder... + + + Selama operasi berkas + + + Tampilkan tombol pusat status + + + Cincin progres pusat status + Screen reader name for the status center progress ring + + + File ikon + This is the friendly name for a variety of different icon files. + + + Tampilkan breadcrumb jalur yang disingkat + + + Tampilkan folder di {0} + + + Tampilkan folder di Beranda + + + Tidak ada perintah yang berisi {0} + + + Lihat selengkapnya + + + Menyaring untuk + + + Nama file + + + Tanda tangan + + + Daftar tanda tangan + + + Diterbitkan oleh: + + + Diterbitkan untuk: + + + Berlaku dari: + + + Berlaku hingga: + + + Tidak ada tanda tangan yang ditemukan. + + + Tampilkan opsi untuk membuka folder di Windows Terminal + + + Buka file log + + + Buka file log di editor default Anda + + + Buka lokasi file log di file manager default Anda + + + Tidak dapat membuka file log + + + Tampilkan bilah status + + + Hanya di Tampilan Kolom + + + Baru {0} + + + Aktifkan pengguliran mulus + + + Pengguliran + + + Show option to Pin to Sidebar + + + Show option to Pin to the Start Menu + \ No newline at end of file diff --git a/src/Files.App/Strings/it-IT/Resources.resw b/src/Files.App/Strings/it-IT/Resources.resw index c32936d6e180..bf2073c9f1d0 100644 --- a/src/Files.App/Strings/it-IT/Resources.resw +++ b/src/Files.App/Strings/it-IT/Resources.resw @@ -123,9 +123,15 @@ Copia percorso + + Copia il percorso dell'elemento + Copia percorso con virgolette + + Copia il percorso dell'elemento selezionato con le virgolette + Esplora @@ -160,7 +166,7 @@ Salta - Seleziona tutto + Seleziona tutti Inverti selezione @@ -249,6 +255,9 @@ Incolla + + Incolla collegamento + Nuovo @@ -291,8 +300,8 @@ Inserisci un nome per l'elemento - - Imposta il nome + + Crea nuovo/a {0} Chiaro @@ -300,6 +309,9 @@ Scuro + + Usa le impostazioni di sistema + Applicazione @@ -443,7 +455,7 @@ ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural - {0, plural, one {item} other {items}} + {0, plural, one {elemento} other {elementi}} ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural @@ -515,9 +527,6 @@ Stato - - Predefinita di sistema - Nessun risultato @@ -530,6 +539,9 @@ Copia in {0} + + Clona in {0} + Crea collegamento @@ -998,6 +1010,9 @@ Cerca risultati in {1} per {0} + + Risultati della ricerca per `{0}` + Dettagli @@ -1052,9 +1067,6 @@ Dettagli (Ctrl + Shift + 1) - - Riquadri (Ctrl + Shift + 2) - Data di eliminazione @@ -1074,16 +1086,31 @@ Mostra o nascondi il riquadro dettagli - Commuta il riquadro dettagli per visualizzare le proprietà di base dei file + Attiva/disattiva il riquadro dettagli per visualizzare le proprietà di base dei file Mostra o nascondi il riquadro informazioni - Commuta il riquadro informazioni per visualizzare i riquadri dettaglio/anteprima + Mostra/Nascondi i pannelli di dettaglio/anteprima - Attiva/disattiva la barra degli strumenti + Attiva/Disattiva la barra degli strumenti + + + Mostra/Nascondi la barra degli strumenti + + + Attiva/Disattiva intestazione filtro + + + Mostra/Nascondi l'intestazione del filtro + + + Attiva/disattiva riquadro doppio + + + Attiva/disattiva la modalità riquadro doppio Anteprima non disponibile @@ -1238,41 +1265,47 @@ Apri Sensore Memoria + + Aprire la pagina Sensore di memoria nelle impostazioni di Windows + + + Pulizia + Copia - Copia {0, plural, one {item} other {items}} + Copia {0, plural, one {elemento} other {elementi}} - Elimina {0, plural, one {item} other {items}} + Elimina {0, plural, one {elemento} other {elementi}} Sposta - {0, plural, one {Un oggetto sarà spostato} other {# elementi saranno spostati}} + {0, plural, one {Un elemento sarà spostato} other {# elementi saranno spostati}} - Muovi {0, plural, one {item} other {items}} + Muovi {0, plural, one {elemento} other {elementi}} - {0, plural, one {Un oggetto sarà eliminato} other {# elementi saranno eliminati}} + {0, plural, one {Un elemento sarà eliminato} other {# elementi saranno eliminati}} Continua - {0, plural, one {C'è un nome file in conflitto} other {Ci sono # nomi file in conflitto}} + {0, plural, one {C'è un nome di file in conflitto} other {Ci sono # nomi di file in conflitto}} - {0, plural, one {C'è un nome file in conflitto} other {Ci sono # nomi file in conflitto}}, e {1, plural, one {un elemento in uscita} other {# elementi in uscita}} + {0, plural, one {C'è un nome di file in conflitto} other {Ci sono # nomi di file in conflitto}}, e {1, plural, one {un elemento in uscita} other {# elementi in uscita}} Conflitto {0, plural, one {nome del file} other {nomi dei file}} - {0, plural, one {Un oggetto verrà copiato} other {# elementi verranno copiati}} + {0, plural, one {Un elemento verrà copiato} other {# elementi verranno copiati}} Elimina definitivamente @@ -1380,7 +1413,7 @@ {0} elementi - Riapri scheda chiusa + Riapri scheda Rinomina @@ -1587,7 +1620,7 @@ Sostituisci tutte le voci delle autorizzazioni degli oggetti figlio con le voci delle autorizzazioni ereditabili da questo oggetto - Chiudi pannello attivo + Chiudi pannello Entra in modalità Overlay Compatto @@ -1646,15 +1679,6 @@ Home - - Ctrl+Maiusc+1 - - - Ctrl+Maiusc+2 - - - Ctrl+Maiusc+6 - Estrazione archivio completata con successo. @@ -1712,8 +1736,8 @@ Colonne - - Riquadri + + Schede Apri le cartelle in una nuova scheda @@ -1749,7 +1773,7 @@ Etichetta - Apri gli elementi con un solo click + Apri i file con un solo click Percorso cartella @@ -1826,6 +1850,18 @@ Registra questo programma per il riavvio + + Finestra iniziale + + + Finestra normale + + + Minimizzato + + + Massimizzato + Esegui come amministratore @@ -1853,6 +1889,9 @@ Questa impostazione modifica il registro di sistema e può causare effetti indesiderati sul tuo dispositivo. Continuando con questa opzione si accettano i rischi legati ad essa. + + Le operazioni di appiattimento sono permanenti e non consigliate. Continua a tuo rischio e pericolo. + Crea Raccolta @@ -1931,6 +1970,9 @@ Chiudi le altre schede + + Chiudi tutte le schede + Musica @@ -1985,11 +2027,23 @@ Comportamenti - - Recensisci Files + + Ciao! - - Vuoi lasciare una recensione su Files? + + Ti piace Files? Considera di valutarci sul Microsoft Store. + + + Ti piace Files? Considera di sostenere il progetto su GitHub. + + + Supportaci + + + Votaci + + + Rimuovi Imposta come sfondo @@ -2018,8 +2072,8 @@ Schermata di blocco - - Apri cartelle con un singolo clic nel Layout Colonne + + Apri le cartelle con un solo click Apertura elementi @@ -2027,6 +2081,21 @@ Comprimi + + Muovi tutti i contenuti delle sottocartelle nella posizione selezionata + + + Appiattisci cartella + + + Appiattisci + + + Appiattendo una cartella, tutti i contenuti delle sue sottocartelle verranno spostati nella posizione selezionata. Questa operazione è permanente e non reversibile. Usando questa funzionalità sperimentale, accetti il rischio che ne consegue e accetti di non ritenere il team di Files responsabile per qualunque perdita di dati. + + + Mostra le opzioni di appiattimento + Seleziona i file e le cartelle quando si passa con il mouse @@ -2037,7 +2106,7 @@ Ripristina tutti gli elementi - Vuoi ripristinare {0} file selezionato/i? + Vuoi ripristinare {0, plural, one {l'elemento selezionato} other {i {0} elementi selezionati}}? Ripristina selezione @@ -2060,6 +2129,12 @@ Password dell'archivio + + Codifica + + + {0} (rilevato) + Percorso @@ -2102,9 +2177,24 @@ Dimensioni divisione - + Non dividere + + Auto + + + Dimensione dizionario + + + Dimensione parola + + + Uso stimato della memoria: {0} + + + Memoria disponibile: {0} + CD @@ -2159,8 +2249,14 @@ Modifica il file delle impostazioni - - Novità + + Apri il file delle impostazioni nel tuo editor predefinito + + + Note di Rilascio + + + Apri Note di Rilascio Creare un collegamento in questa posizione richiede i privilegi di amministratore @@ -2241,25 +2337,22 @@ Opzioni del menu contestuale - Mostra le estensioni dei file - - - Mostra elementi nascosti + Estensioni file - Formatta... + Formato Aiuto - Attiva o disattiva schermo intero + Schermo intero Sei sicuro/a di voler eliminare questo tag? - Riproduci tutto + Riproduci Altezza @@ -2319,7 +2412,7 @@ Aumenta dimensione - Inverti direzione di ordinamento + Direzione ordinamento Nome non valido @@ -2331,7 +2424,7 @@ Video - Avvia popup anteprima + Popup anteprima Attiva o disattiva modalità Overlay Compatto @@ -2340,25 +2433,28 @@ Apri la pagina di aiuto nel browser - Attiva o disattiva schermo intero + Attiva/Disattiva schermo intero Entra in modalità Overlay Compatto - Esci dalla modalità Compact Overlay + Esci dalla modalità Overlay Compatto - Attiva o disattiva modalità Overlay Compatto + Attiva/Disattiva modalità Overlay Compatto - Vai alla ricerca + Inizia la ricerca nella OmniBar - Attiva o disattiva la visualizzazione degli elementi nascosti + Mostra/Nascondi gli elementi nascosti + + + Mostra/Nascondi i file che iniziano con un punto - Attiva o disattiva la visualizzazione delle estensioni dei file + Mostra/Nascondi le estensioni dei file Attiva o disattiva il riquadro di anteprima per visualizzare le anteprime dei file @@ -2367,52 +2463,64 @@ Attiva o disattiva la visualizzazione della barra laterale - Copia gli elementi negli appunti + Copia {0, plural, one {elemento selezionato} other {elementi selezionati}} - Copia il percorso degli elementi selezionati negli appunti + Copia il percorso della directory corrente + + + Copia il percorso degli elementi selezionati + + + Copia il percorso degli elementi selezionati con virgolette - Copia il percorso degli elementi selezionati con virgolette negli appunti + Copia il percorso della cartella corrente con virgolette - Taglia gli elementi negli appunti + Taglia {0, plural, one {elemento selezionato} other {elementi selezionati}} - Incolla gli elementi dagli appunti alla cartella corrente + Incolla gli elementi nella cartella corrente + + + Incolla gli elementi nella cartella corrente come collegamenti - Incolla gli elementi dagli appunti alla cartella selezionata + Incolla gli elementi nella cartella selezionata + + + Incolla nella cartella selezionata - Elimina elemento/i + Elimina {0, plural, one {elemento selezionato} other {elementi selezionati}} Crea nuova cartella - Crea una nuovo collegamento agli elementi selezionati + Crea {0, plural, one {nuova scorciatoia} other {nuove scorciatoie}} per {0, plural, one {l'elemento selezionato} other {gli elementi selezionati}} Crea un nuovo collegamento a qualsiasi elemento - Svuota il cestino + Svuota il contenuto del Cestino Apri il menù di formattazione dell'elemento selezionato - Ripristina gli elementi selezionati dal cestino + Ripristina {0, plural, one {elemento selezionato} other {elementi selezionati}} dal cestino Ripristina tutti gli elementi nel cestino - Apri elemento/i + Apri {0, plural, one {elemento} other {elementi}} - Apri elemento/i con l'applicazione selezionata + Apri {0, plural, one {elemento} other {elementi}} con l'applicazione selezionata Apri la cartella superiore dell'elemento cercato @@ -2427,7 +2535,7 @@ Seleziona tutti gli elementi - Inverti selezione + Inverti elementi selezionati Annulla selezione @@ -2436,19 +2544,19 @@ Inverti la selezione degli elementi - Condividi i file selezionati con altri + Condividi {0, plural, one {il file selezionato} other {i file selezionati}} con altri - Aggiungi elemento/i al menu Start + Fissa {0, plural, one {elemento} other {elementi}} al Menu Start - Rimuovi elemento/i dal menu Start + Rimuovi {0, plural, one {elemento} other {elementi}} dal Menu Start - Aggiungi cartella/e alla barra laterale + Fissa {0, plural, one {cartella} other {cartelle}} alla barra laterale - Rimuovi cartella/e dalla barra laterale + Rimuovi {0, plural, one {cartella} other {cartelle}} dalla barra laterale Imposta l'immagine selezionata come sfondo del desktop @@ -2462,14 +2570,23 @@ Imposta l'immagine selezionata come sfondo dell'app + + Installa font + + + Installa driver + + + Installa certificato + - Installa font selezionato/i + Installa font {0, plural, one {selezionato} other {selezionati}} - Installa driver usando i file inf selezionati + Installa {0, plural, one {il} other {i}} driver usando {0, plural, one {il file inf selezionato} other {i file inf selezionati}} - Installa i certificati selezionati + Installa {0, plural, one {il certificato selezionato} other {i certificati selezionati}} Esegui l'applicazione selezionata come amministratore @@ -2484,28 +2601,28 @@ Avvia l'anteprima in una finestra popup - Crea un archivio con gli elementi selezionati + Crea archivio con {0, plural, one {l'elemento selezionato} other {gli elementi selezionati}} - Crea un archivio 7z con gli elementi selezionati + Crea archivio 7z con {0, plural, one {l'elemento selezionato} other {gli elementi selezionati}} - Crea un archivio zip con gli elementi selezionati + Crea archivio zip con {0, plural, one {l'elemento selezionato} other {gli elementi selezionati}} - Estrai gli elementi dagli archivi selezionati in una cartella + Estrai {0, plural, one {archivio selezionato} other {archivi selezionati}} in una cartella qualsiasi - Estrai gli elementi dagli archivi selezionati nella cartella corrente + Estrai {0, plural, one {archivio selezionato} other {archivi selezionati}} nella cartella corrente - Estrai gli elementi dagli archivi selezionati in una nuova cartella + Estrai {0, plural, one {archivio selezionato} other {archivi selezionati}} in una nuova cartella - Ruota le immagini selezionate a sinistra + Ruota {0, plural, one {immagine selezionata} other {immagini selezionate}} a sinistra - Ruota le immagini selezionate a destra + Ruota {0, plural, one {immagine selezionata} other {immagini selezionate}} a destra Apri la pagina delle impostazioni @@ -2525,8 +2642,8 @@ Passa alla visualizzazione dettagli - - Passa alla visualizzazione riquadri + + Passa alla visualizzazione a schede Passa alla visualizzazione elenco @@ -2628,10 +2745,10 @@ Apri una nuova scheda - Naviga indietro nella cronologia di navigazione + Vai indietro - Naviga avanti nella cronologia di navigazione + Vai avanti Naviga alla cartella suporiore @@ -2660,8 +2777,11 @@ Chiudi tutte le schede tranne quella selezionata + + Chiudi tutte le schede, inclusa la scheda corrente + - Riapri l'ultima scheda chiusa + Riapri la scheda chiusa di recente Passa alla scheda precedente @@ -2673,13 +2793,13 @@ Chiudi la scheda attuale - Chiudi pannello attivo + Chiudi il pannello attivo Imposta il focus sull'altro pannello - Cambia il focus al pannello non attivo + Cambia il focus all'altro pannello Attiva o disattiva la barra laterale @@ -2887,13 +3007,13 @@ Senza etichetta - Passa alla scheda precedente + Scheda precedente - Passa alla scheda successiva + Scheda successiva - Chiudi scheda corrente + Chiudi scheda Modifica percorso @@ -2923,7 +3043,7 @@ Mostra caselle di spunta quando si selezionano gli elementi - Apri la barra del percorso + Elimina percorso nella OmniBar Crea un nuovo elemento @@ -2938,7 +3058,7 @@ Elimina permanentemente - Elimina gli elementi permanentemente + Elimina {0, plural, one {elemento selezionato} other {elementi selezionati}} permanentemente Riproduci i file multimediali selezionati @@ -3027,11 +3147,20 @@ Hai dei cambiamenti non commitati su questo branch. Cosa vorresti farci? + + Hai una fusione in corso con conflitti non risolti. Si prega di risolvere i conflitti o interrompere la fusione per cambiare ramo. + + + Interrompi la fusione e passa a '{0}' + + + Rimani su '{0}' e risolvi i conflitti + Cambia branch - Branches + Rami Cambia @@ -3055,14 +3184,20 @@ Passa al nuovo branch - Crea una cartella con gli elementi attualmente selezionati + Crea cartella con {0, plural, one {l'elemento attualmente selezionato} other {gli elementi attualmente selezionati}} - Apri le proprietà + Proprietà + + + Apri proprietà di Esplora file Apri la finestra delle proprietà + + Apri la finestra delle proprietà di Esplora File + Locali @@ -3081,6 +3216,9 @@ Esegui git fetch + + Clona un repo git + Esegui git pull @@ -3125,7 +3263,7 @@ Mica Alt - Materiale Di Sfondo + Sfondo Pieno @@ -3157,17 +3295,17 @@ Files non può accedere a GitHub in questo momento. - - Apri cartella in VS Code + + Apri cartella in {0} - - Apri la directory attuale in Visual Studio Code + + Apri la cartella corrente in {0} - - Apri repository in VS Code + + Apri repo in {0} - - Apri la cartella base della repository di Git in Visual Studio Code + + Apri la root del repo Git in {0} Copia il codice @@ -3194,7 +3332,7 @@ Inizializza repo - Inizializza una repository Git + Inizializza la cartella corrente come repository git Nome Repository Remoto @@ -3209,13 +3347,13 @@ Ordina gli elementi per percorso - Apri la directory in un nuovo pannello + Apri la directory selezionata in un nuovo pannello - Apri la directory in una nuova scheda + Apri la directory selezionata in una nuova scheda - Apri la directory in una nuova finestra + Apri la directory selezionata in una nuova finestra Apri tutto @@ -3240,10 +3378,10 @@ Il comando '{0}' non è pronto per essere eseguito. - Palette dei comandi + Palette dei Comandi - Apri la palette dei comandi + Apri Palette dei Comandi nella OmniBar Avvia in: @@ -3267,7 +3405,7 @@ Files è in esecuzione come amministratore - A causa di una limitazione con Windows e WinAppSdk, il drag and drop non è disponibile quando si esegue FIles come amministratore. Se si desidera utilizzare il drag and drop, è possibile aggirare questa limitazione aprendo UAC (User Account Control) dal menu Start, selezionando il terzo livello e riavviando Windows. In caso contrario, è possibile continuare a utilizzare i file senza drag and drop. + A causa di una limitazione di Windows e WinAppSdk, il drag and drop non è disponibile quando si esegue FIles come amministratore. Se si desidera utilizzare il drag and drop, è possibile aggirare questa limitazione aprendo UAC (User Account Control) dal menu Start, selezionando il terzo livello e riavviando Windows. Altrimenti, è possibile continuare a usare Files senza drag and drop. Lasciare l'app in esecuzione in background quando la finestra è chiusa @@ -3373,73 +3511,144 @@ Elaborazione degli elementi... + + Trovare gli elementi... + Velocità: - Annullata compressione di {0} elementi in "{1}" + Compressione di {0} elementi in "{1}" annullata Shown in a StatusCenter card. - Annullata compressione di {0} elementi da "{1}" a "{2}" + Compressione di {0} elementi da "{1}" a "{2}" annullata Shown in a StatusCenter card. - Compressi {0} elemento/i in "{1}" + {0, plural, one {# elemento compresso} other {# elementi compressi}} in "{1}" Shown in a StatusCenter card. - Compressi {0} elemento/i da "{1}" in "{2}" + {0, plural, one {# elemento compresso} other {# elementi compressi}} da "{1}" in "{2}" Shown in a StatusCenter card. - Errore nella compressione di {0} elemento/i in "{1}" + Errore nella compressione di {0, plural, one {# elemento} other {# elementi}} in "{1}" Shown in a StatusCenter card. - Errore nella compressione di {0} elemento/i da "{1}" in "{2}" + Impossibile comprimere {0, plural, one {# elemento} other {# elementi}} da "{1}" in "{2}" Shown in a StatusCenter card. - Compressione di {0} elemento/i in "{1}" in corso + Comprimendo {0, plural, one {# elemento} other {# elementi}} in "{1}" Shown in a StatusCenter card. - Compressione di {0} elemento/i da "{1}" in "{2}" in corso + Comprimendo {0, plural, one {# elemento} other {# elementi}} da "{1}" in "{2}" + Shown in a StatusCenter card. + + + Clonazione di {0} in "{1}" annullata + Shown in a StatusCenter card. + + + Clonazione di {0} da "{1}" in "{2}" annullata + Shown in a StatusCenter card. + + + "{0}" clonato in "{1}" + Shown in a StatusCenter card. + + + {0} clonato da "{1}" in "{2}" + Shown in a StatusCenter card. + + + Errore nella clonazione di "{0}" in "{1}" + Shown in a StatusCenter card. + + + Errore nella clonazione di {0} da "{1}" in "{2}" + Shown in a StatusCenter card. + + + Clonazione di "{0}" in "{1}" + Shown in a StatusCenter card. + + + Clonazione di {0} da "{1}" in "{2}" + Shown in a StatusCenter card. + + + Installazione dei caratteri {0} annullata + Shown in a StatusCenter card. + + + Installazione di {0, plural, one {#} other {#}} font da "{1}" annullata + Shown in a StatusCenter card. + + + {0, plural, one {Installato #} other {Installati #}} font + Shown in a StatusCenter card. + + + {0, plural, one {Installato #} other {Installati #}} font da "{1}" + Shown in a StatusCenter card. + + + Errore nell'installazione di {0, plural, one {#} other {#}} font + Shown in a StatusCenter card. + + + Impossibile installare {0, plural, one {#} other {#}} font da "{1}" + Shown in a StatusCenter card. + + + Installando {0, plural, one {#} other {#}} font + Shown in a StatusCenter card. + + + Installando {0, plural, one {#} other {#}} font da "{1}" Shown in a StatusCenter card. - Annullata copia di {0} elemento/i in "{1}" + Copia di {0, plural, one {# elemento} other {# elementi}} in "{1}" annullata Shown in a StatusCenter card. - Annullata copia di {0} elemento/i da "{1}" a "{2}" + Copia di {0, plural, one {# elemento} other {# elementi}} da "{1}" in "{2}" annullata Shown in a StatusCenter card. - Copiati {0} elemento/i in "{1}" + {0, plural, one {Elemento copiato} other {Elementi copiati}}" in "{1}" Shown in a StatusCenter card. - Copiati {0} elemento/i da "{1}" a "{2}" + {0, plural, one {Elemento copiato} other {Elementi copiati}}" da "{1}" in "{2}" Shown in a StatusCenter card. - Errore nella copia di {0} elemento/i in "{1}" + Errore nella copia {0, plural, one {di # elemento} other {di # elementi}} in "{1}" Shown in a StatusCenter card. - Errore nella copia di {0} elemento/i da "{1}" a "{2}" + Impossibile copiare {0, plural, one {# elemento} other {# elementi}} da "{1}" in "{2}" Shown in a StatusCenter card. - Copia di {0} elemento/i in "{1}" in corso + Copiando {0, plural, one {# elemento} other {# elementi}} in "{1}" Shown in a StatusCenter card. - Copia di {0} elemento/i da "{1}" a "{2}" in corso + Copiando {0, plural, one {# elemento} other {# elementi}} da "{1}" in "{2}" Shown in a StatusCenter card. + + Trovat{0, plural, one {o # elemento} other {i # elementi}} + Shown in a StatusCenter card during file discovery phase. + Annullata estrazione di "{0}" in "{1}" Shown in a StatusCenter card. @@ -3473,59 +3682,59 @@ Shown in a StatusCenter card. - Annullata eliminazione di {0} elemento/i da "{1}" + Eliminazione di {0, plural, one {# elemento} other {# elementi}} da "{1}" annullata Shown in a StatusCenter card. - Eliminati {0} elemento/i da "{1}" + {0, plural, one {Eliminato # elemento} other {Eliminati # elementi}} da "{1}" Shown in a StatusCenter card. - Errore nell'eliminazione di {0} elemento/i da "{1}" + Errore nell'eliminazione di {0, plural, one {# elemento} other {# elementi}} da "{1}" Shown in a StatusCenter card. - Eliminazione di {0} elementi da "{1}" non riuscita + Impossibile eliminare {0, plural, one {# elemento} other {# elementi}} da "{1}" Shown in a StatusCenter card. - Eliminazione di {0} elemento/i da "{1}" in corso + Eliminando {0, plural, one {# elemento} other {# elementi}} da "{1}" Shown in a StatusCenter card. - Annullato spostamento di {0} elemento/i in "{1}" + Spostamento di {0, plural, one {# elemento} other {# elementi}} in "{1}" annullato Shown in a StatusCenter card. - Annullato spostamento di {0} elemento/i da "{1}" in "{2}" + Spostamento di {0, plural, one {# elemento} other {# elementi}} da "{1}" in "{2}" annullato Shown in a StatusCenter card. - Spostati {0} elemento/i in "{1}" + {0, plural, one {# elemento spostato} other {# elementi spostati}} in "{1}" Shown in a StatusCenter card. - Spostati {0} elemento/i da "{1}" in "{2}" + {0, plural, one {# elemento spostato} other {# elementi spostati}} da "{1}" in "{2}" Shown in a StatusCenter card. - Spostamento di {0} elemento/i in "{1}" in corso + Spostando {0, plural, one {# elemento} other {# elementi}} in "{1}" Shown in a StatusCenter card. - Spostamento di {0} elemento/i da "{1}" in "{2}" in corso + Spostando {0, plural, one {# elemento} other {# elementi}} da "{1}" in "{2}" Shown in a StatusCenter card. - Errore nello spostamento di {0} elemento/i in "{1}" + Errore nello spostamento di {0, plural, one {# elemento} other {# elementi}} in "{1}" Shown in a StatusCenter card. - Errore nello spostamento di {0} elemento/i da "{1}" in "{2}" + Impossibile spostare {0, plural, one {# elemento} other {# elementi}} da "{1}" in "{2}" Shown in a StatusCenter card. - Annullato svuotamento del Cestino + Svuotamento del Cestino annullato Shown in a StatusCenter card. @@ -3549,7 +3758,7 @@ Shown in a StatusCenter card. - {0}/{1} elemento/i elaborati + {0}/{1, plural, one {# elemento elaborato} other {# elementi elaborati}} Shown in a StatusCenter card. Used as "8/20 items processed" @@ -3567,6 +3776,9 @@ Impossibile impostare lo sfondo + + Impossibile aprire il file delle impostazioni + Elimina il branch di Git @@ -3592,7 +3804,7 @@ Si è verificato un errore durante l'applicazione di quest'etichetta - Estrae gli elementi dall'archivio/dagli archivi selezionato/i nella cartella corrente per gli archivi con un singolo elemento o in una nuova cartella per quelli con più elementi + Estrai {0, plural, one {l'archivio selezionato} other {gli archivi selezionati}} qui per un singolo elemento o in una nuova cartella per un oggetto multiplo Estrai qui (Smart) @@ -3601,7 +3813,7 @@ Ordina file e cartelle insieme - Ordina file e cartelle insieme + Ordina file e cartelle nella stessa lista Ordina prima i file @@ -3657,9 +3869,6 @@ Domande e discussioni - - Le dimensioni aggiuntive non sono ancora disponibili per la visualizzazione Riquadri. - Compatta Used to describe layout sizes @@ -3821,6 +4030,10 @@ File ICO This is the friendly name for ICO files. + + File ICL + This is the friendly name for ICL files. + File Zip This is the friendly name for ZIP files. @@ -3829,6 +4042,10 @@ File Bitmap This is the friendly name for bitmap files. + + File Immagine + This is the friendly name for image files. + Num @@ -3855,22 +4072,22 @@ Mostra barra degli strumenti - Setting that controls if the Toolbar is shown in the main view + Setting that controls if the toolbar is shown in the main view Menu azioni scheda - - Aggiungi pannello verticale + + Dividi verticalmente - Aggiungi un pannello verticale + Dividi il riquadro verticalmente - - Aggiungi pannello orizzontale + + Dividi orizzontalmente - - Aggiungi un pannello orizzontale + + Dividi il riquadro orizzontalmente Disponi verticalmente @@ -3884,8 +4101,8 @@ Disponi i riquadri orizzontalmente - - Aggiungi pannello + + Riquadro diviso Mostra il pulsante delle azioni nella barra del titolo @@ -3893,8 +4110,11 @@ Disponi riquadri - - Disposizione predefinita pannello + + Direzione predefinita divisione riquadro doppio + + + Modalità riquadro doppio Orizzontale @@ -3902,4 +4122,259 @@ Verticale + + Mostra l'icona di Files nella barra delle applicazioni + + + Thread CPU + + + Vai alla pagina iniziale + + + Barre degli strumenti + + + ID utente + + + Rinomina in blocco + + + Comprimi contenuti + + + Mostra l'opzione per creare un flusso dati alternativo + + + Crea flusso dati alternativo + + + Crea {0, plural, one {flusso di dati alternativo per l'elemento selezionato} other {flussi di dati alternativi per gli elementi selezionati}} + + + Inserisci il nome del flusso dati + + + Si è verificato un errore durante la creazione del flusso di dati alternativo + + + Si prega di notare che i flussi di dati alternativi funzionano solo su unità formattate come NTFS. + + + I flussi di dati alternativi sono attualmente nascosti + + + Vuoi visualizzare flussi di dati alternativi? Puoi modificare questa impostazione in qualsiasi momento dalla pagina delle impostazioni dei file e delle cartelle. + + + Gestisci etichette + + + Disponibile + + + Totale + + + Cambia sempre il focus alla nuova scheda creata + + + Attiva/Disattiva il pannello dello scaffale + + + Mostra/Nascondi il pannello dello scaffale + + + Mostra Attiva/Disattiva pannello dello scaffale nella barra dell'indirizzo + + + Scaffale + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + Cancella elementi + + + Rimuovi dallo scaffale + + + Aggiungi allo scaffale + Tooltip that displays when dragging items to the Shelf Pane + + + Inserisci un hash da confrontare + Placeholder that appears in the compare hash text box + + + Corrisponde a {0} + Appears when two compared hashes match, e.g. "Matches SHA256" + + + Nessuna corrispondenza trovata + Appears when two compared hashes don't match + + + Percorso o alias + + + Percorso non valido + + + Prova integrazione + + + {0} non è stato trovato. Controlla le tue impostazioni e riprova. + + + L'IDE configurato non è stato trovato + + + Apri impostazioni + + + Visual Studio Code + + + Inserisci un percorso o un alias di avvio + + + Inserisci un nome per l'IDE + + + Clona repo + Clone repo dialog title + + + Clona + Primary action button in the clone repo dialog + + + URL della repository + URL textbox header in the clone repo dialog + + + Impossibile clonare la repository + Cannot clone repo dialog title + + + Confronta un file + Button that appears in file hash properties that allows the user to compare two files + + + Formato dimensione + + + Binario + + + Decimale + + + È possibile aggiungere sezioni alla barra laterale facendo clic con il tasto destro del mouse e selezionando le sezioni che si desidera aggiungere + + + Trascina qui i file o le cartelle per interagire con loro attraverso schede diverse + + + Inserisci un percorso per navigare a... + + + Trova funzionalità e comandi... + + + Cerca per file e cartelle... + + + Durante le operazioni dei file + + + Mostra il pulsante centro di stato + + + Anello di avanzamento del centro di stato + Screen reader name for the status center progress ring + + + File icone + This is the friendly name for a variety of different icon files. + + + Mostra componenti nascoste del percorso + + + Mostra cartelle in {0} + + + Mostra cartelle in Home + + + Non ci sono comandi contenenti {0} + + + Mostra altro + + + Filtra per + + + Nome file + + + Firme + + + Elenco firme + + + Rilasciato da: + + + Rilasciato a: + + + Valido da: + + + Valido fino a: + + + Nessuna firma trovata. + + + Mostra l'opzione per aprire le cartelle nel terminale di Windows + + + Apri il file di registro + + + Apri il file di registro nell'editor predefinito + + + Apri la posizione del file di registro nel file manager predefinito + + + Impossibile aprire il file di registro + + + Mostra la barra di stato + + + Solo nella vista a colonne + + + Nuovo {0} + + + Abilita scorrimento fluido + + + Scorrimento + + + Show option to Pin to Sidebar + + + Show option to Pin to the Start Menu + \ No newline at end of file diff --git a/src/Files.App/Strings/ja-JP/Resources.resw b/src/Files.App/Strings/ja-JP/Resources.resw index 8d49dd44609f..d310b5d01f9d 100644 --- a/src/Files.App/Strings/ja-JP/Resources.resw +++ b/src/Files.App/Strings/ja-JP/Resources.resw @@ -123,9 +123,15 @@ パスのコピー + + アイテムのパスをコピー + 引用符付きでパスのコピー + + 選択した項目のパスを引用符付きでコピー + 参照 @@ -249,6 +255,9 @@ 貼り付け + + ショートカットを貼り付け + 新規作成 @@ -291,8 +300,8 @@ 名前を入力 - - 名前を設定 + + 新しい{0}を作成 ライト @@ -300,6 +309,9 @@ ダーク + + システム設定を使用 + アプリ @@ -515,9 +527,6 @@ 状態 - - Windows の既定 - 結果なし @@ -530,6 +539,9 @@ {0} にコピー + + {0} にクローン + ショートカットの作成 @@ -633,7 +645,7 @@ キャンセル - 新しいファイルの作成 + 新しい項目を作成 以下から新しい項目の種類を選択してください @@ -998,6 +1010,9 @@ {0} の {1} での検索結果を表示: + + 「{0}」の検索結果 + 詳細 @@ -1052,9 +1067,6 @@ 詳細 (Ctrl+Shift+1) - - タイル (Ctrl+Shift+2) - 削除した日時 @@ -1080,11 +1092,26 @@ 情報ウィンドウの表示を切り替える - 詳細およびプレビューのための情報ウィンドウを表示するかどうかを切り替える + 詳細/プレビュー ペインの表示/非表示を切り替え ツールバーの表示を切り替える + + ツールバーの表示/非表示 + + + 絞り込みヘッダーを切り替える + + + 絞り込みヘッダーの表示/非表示を切り替える + + + デュアルペインの切り替える + + + デュアルペインモードを切り替える + プレビューを利用できません @@ -1238,6 +1265,12 @@ ストレージ センサーを開く + + Windows 設定のストレージ センサー ページを開く + + + クリーンアップ + コピー @@ -1461,7 +1494,7 @@ {0} へ展開 - 新しいライブラリの作成 + 新しいライブラリを作成 既定のライブラリを復元 @@ -1587,7 +1620,7 @@ すべての子オブジェクトのアクセス許可エントリを、このオブジェクトから継承可能なアクセス許可エントリに置き換えます - 現在のペインを閉じる + ペインを閉じる コンパクト オーバーレイを開始 @@ -1646,15 +1679,6 @@ ホーム - - Ctrl+Shift+1 - - - Ctrl+Shift+2 - - - Ctrl+Shift+6 - アーカイブの展開が正常に完了しました。 @@ -1712,8 +1736,8 @@ カラム - - タイル + + カード 新しいタブでフォルダーを開く @@ -1749,7 +1773,7 @@ タグ - シングル クリックで項目を開く + シングルクリックでファイルを開く フォルダー パス @@ -1826,6 +1850,18 @@ このプログラムを再起動するために登録する + + 実行時の大きさ + + + 通常のウィンドウ + + + 最小化 + + + 最大化 + 管理者として実行 @@ -1853,6 +1889,9 @@ この設定はシステムのレジストリを変更し、デバイスに思わぬ結果をもたらす可能性があります。問題が発生しても開発者は責任を負いません。 + + フラット化の操作は恒久的で推奨されません。問題が発生しても開発者は責任を負いません。 + ライブラリを作成 @@ -1866,7 +1905,7 @@ 最近使用したファイル - WIndows 起動時に Files を開く + Windows 起動時に Files を開く 属性 @@ -1931,6 +1970,9 @@ 他のタブを閉じる + + すべてのタブを閉じる + ミュージック @@ -1985,11 +2027,23 @@ 動作 - - Files を評価 + + こんにちは! + + + Files をお楽しみいただけましたか? Microsoft Store でのレビューをご検討ください。 + + + Files をお楽しみいただけましたか? GitHub でプロジェクトをサポートすることをご検討ください。 - - Files を評価していただけますか? + + スポンサー + + + 評価する + + + 閉じる 背景に設定 @@ -2018,8 +2072,8 @@ ロック画面 - - カラム レイアウトでシングル クリックでフォルダーを開く + + シングルクリックでフォルダを開く 項目を開くとき @@ -2027,6 +2081,21 @@ 圧縮 + + すべての項目をサブフォルダーから選択した場所に移動する + + + フォルダーをフラット化 + + + フラット化 + + + フォルダーをフラット化すると、そのサブフォルダーから選択した場所にすべての項目を移動します。この操作は恒久的で、元に戻すことはできません。この実験的機能を使用する場合は、リスクを認識し、Files 開発チームはあらゆるデータ損失に対する責任を負わないことに同意してください。 + + + フラット化のオプションを表示 + カーソルを合わせたファイルとフォルダーを選択する @@ -2037,7 +2106,7 @@ すべての項目を復元 - 選択した {0} 項目を復元しますか? + {0, plural, other {{0} 項目}}を元に戻しますか? 選択した項目を復元 @@ -2060,6 +2129,12 @@ アーカイブのパスワード + + エンコーディング + + + {0} (検出済み) + パス @@ -2102,9 +2177,24 @@ 分割サイズ - + 分割しない + + Auto + + + Dictionary size + + + Word size + + + Estimated memory usage: {0} + + + Available memory: {0} + CD @@ -2159,8 +2249,14 @@ 設定ファイルを編集する - - 更新情報 + + 既定のエディターで設定ファイルを開く + + + 更新履歴 + + + 更新履歴を開く この場所にショートカットを作成するには管理者権限が必要です @@ -2241,25 +2337,22 @@ コンテキスト メニューの設定 - ファイル名拡張子を表示 - - - 隠しファイルを表示 + ファイルの拡張子 - フォーマット… + フォーマット ヘルプ - 全画面表示に切り替える + 全画面表示 このタグを削除しますか? - すべて再生 + 再生 高さ @@ -2319,7 +2412,7 @@ サイズを大きくする - 昇順と降順の切り替え + 並べ替え方向 無効な名前です @@ -2331,34 +2424,37 @@ ビデオ - プレビュー ポップアップを開く + プレビューポップアップ コンパクト オーバーレイを切り替える - ブラウザーでオンライン ヘルプを開く + ブラウザでオンラインヘルプページを開く - 全画面表示に切り替える + 全画面モードの切り替え - コンパクト オーバーレイを開始する + コンパクト オーバーレイを開始 - コンパクト オーバーレイを終了する + コンパクト オーバーレイを終了 - コンパクト オーバーレイを切り替える + コンパクト オーバーレイモードを切り替える - 検索ボックスへ移動する + OmniBarで検索を開始 - 隠しファイルを表示するかどうかを切り替える + 非表示項目の表示を切り替える + + + ドットファイルの表示を切り替える - ファイル名拡張子を表示するかどうかを切り替える + ファイル拡張子の表示を切り替える ファイルをプレビューするためのプレビュー ウィンドウを表示する @@ -2367,31 +2463,43 @@ サイドバーを表示するかどうかを切り替える - 項目をクリップボードにコピーする + 選択した{0, plural, other {項目}}をコピー - 選択した項目のパスをクリップボードにコピーする + カレントディレクトリのパスをコピー + + + 選択した項目のパスをコピー + + + 選択した項目のパスを引用符付きでコピー - 選択した項目のパスを引用符付きでクリップボードにコピーする + 現在のディレクトリのパスを引用符付きでコピーする - 項目をクリップボードに切り取る + 選択した{0, plural, other {項目}}をカット - 項目をクリップボードから現在のフォルダーに貼り付ける + 項目を現在のフォルダーに貼り付け + + + 項目を現在のフォルダーにショートカットとして貼り付ける - 項目をクリップボードから選択したフォルダーに貼り付ける + 選択したフォルダーに項目を貼り付け + + + 選択したフォルダーに貼り付け - ファイルを削除する + 選択した{0, plural, other {項目}}を削除 新しいフォルダーを作成する - 選択した項目へのショートカットを作成する + 選択した {0, plural, other {項目}} に {0, plural, other {項目のショートカット}}を新規作成する 任意の項目へのショートカットを作成する @@ -2403,16 +2511,16 @@ 選択した項目の [フォーマット] メニューを開く - ごみ箱から選択した項目を復元 + ゴミ箱から {0, plural, other {項目}} を元に戻す ごみ箱からすべての項目を復元する - 項目を開く + {0, plural, other {項目}}を開く - 選択したアプリケーションで項目を開く + 選択したアプリケーションで {0, plural, other {項目}}を開く 検索した項目の親フォルダーを開く @@ -2430,25 +2538,25 @@ 項目の選択を反転する - 項目の選択をクリアする + 選択した項目をクリア 項目の選択を切り替える - 選択したファイルを他の人と共有する + 選択した {0, plural, other {ファイル}}を他の人と共有 - スタート メニューに項目をピン留めする + スタート メニューに {0, plural, other {項目}}をピン留めする - スタート メニューから項目のピン留めを外す + スタート メニューから {0, plural, other {項目}}のピン留めを外す - サイドバーにフォルダーをピン留めする + サイドバーに {0, plural, other {項目}}をピン留めする - サイドバーからフォルダーのピン留めを外す + サイドバーから {0, plural, other {項目}}のピン留めを外す 選択した画像をデスクトップの背景に設定する @@ -2462,14 +2570,23 @@ 選択した画像をアプリの背景に設定する + + フォントをインストール + + + ドライバーをインストール + + + 証明書をインストール + - 選択したフォントをインストールする + 選択した {0, plural, other {フォント}}をインストールする - 選択した inf ファイルを使用してドライバーをインストールする + 選択した {0, plural, other {INF ファイル}}から {0, plural, other {ドライバー}}をインストールする - 選択した証明書をインストールする + 選択した {0, plural, other {証明書}}をインストールする 選択したアプリケーションを管理者として実行する @@ -2484,28 +2601,28 @@ ポップアップ ウィンドウでプレビューを開く - 選択した項目でアーカイブを作成する + 選択した{0, plural, other {項目}}でアーカイブを作成 - 選択した項目で即座に 7z アーカイブを作成する + 選択した{0, plural, other {項目}}で7zアーカイブを作成 - 選択した項目で即座に zip アーカイブを作成する + 選択した{0, plural, other {項目}}でzipアーカイブを作成 - 選択したアーカイブから任意のフォルダーに項目を抽出する + 選択した {0, plural, other {アーカイブ}} を任意のフォルダに展開 - 選択したアーカイブから現在のフォルダーに項目を抽出する + 選択した {0, plural, other {アーカイブ}} を現在のフォルダに展開 - 選択したアーカイブから新しいフォルダーに項目を抽出する + 選択した {0, plural, other {アーカイブ}} を新規フォルダに展開 - 選択した画像を左に回転する + 選択した {0, plural, other {画像}} を左に回転 - 選択した画像を右に回転する + 選択した {0, plural, other {画像}} を右に回転 設定画面を開く @@ -2525,8 +2642,8 @@ 詳細ビューに切り替える - - タイル ビューに切り替える + + カード ビューに切り替える 一覧ビューに切り替える @@ -2625,13 +2742,13 @@ 項目のグループ化順を切り替える - 新しいタブを開く + 新しいタブ - ナビゲーション履歴を戻る + 前に戻る - ナビゲーション履歴を進む + 次に進む 1つ上のフォルダーに移動する @@ -2660,8 +2777,11 @@ 選択したタブ以外のタブを閉じる + + 現在のタブを含むすべてのタブを閉じる + - 最後に閉じたタブを再度開く + 最近閉じたタブを再度開く 前のタブに移動する @@ -2679,7 +2799,7 @@ 別のペインに移動 - 現在のペインでないペインに移動する + 他のペインに移動する サイドバーの表示を切り替える @@ -2887,13 +3007,13 @@ タグなし - 前のタブに移動 + 前のタブ - 次のタブに移動 + 次のタブ - 現在のタブを閉じる + タブを閉じる パスを編集 @@ -2923,7 +3043,7 @@ 項目選択時にチェック ボックスを表示する - パス バーに移動する + OmniBarでパスを編集 新しい項目を作成する @@ -2938,7 +3058,7 @@ 完全に削除 - 項目を完全に削除する + 選択した{0, plural, other {項目}}を完全に削除 選択したメディア ファイルを再生する @@ -3027,6 +3147,15 @@ このブランチにはコミットされていない変更があります。どうしますか? + + 未解決の競合を含む統合が進行中です。競合を解決するか、統合を中止してからブランチを切り替えてください。 + + + 統合を中止し、「{0}」に切り替え + + + 「{0}」に留まり競合を解決 + ブランチを切り替え @@ -3055,14 +3184,20 @@ 新しいブランチに切り替える - 選択した項目でフォルダーを作成する + 現在選択している{0, plural, other {項目}}でアーカイブを作成 - プロパティを開く + プロパティ + + + エクスプローラーのプロパティを開く プロパティ ウィンドウを開く + + エクスプローラーのプロパティ ウィンドウを開く + ローカル @@ -3081,6 +3216,9 @@ git fetch を実行する + + Git リポジトリをクローンする + git pull を実行する @@ -3125,7 +3263,7 @@ マイカ Alt - 背景の素材 + 背景 ソリッド @@ -3157,17 +3295,17 @@ Files は現在 GitHub にアクセスできません。 - - VS Code でフォルダーを開く + + {0} でフォルダを開く - - 現在のフォルダーを Visual Studio Code で開く + + 現在のディレクトリを {0} で開く - - VS Code でリポジトリを開く + + {0} でリポジトリを開く - - Git リポジトリのルートを Visual Studio Code で開く + + Git リポジトリのルートを {0} で開く コードをコピー @@ -3194,7 +3332,7 @@ リポジトリの初期化 - Git リポジトリを初期化する + 現在のフォルダを git リポジトリとして初期化 リモート リポジトリ名 @@ -3209,10 +3347,10 @@ 項目をパスで並べ替える - 新しいペインでフォルダーを開く + 新しいペインで選択したフォルダーを開く - 新しいタブでフォルダーを開く + 新しいタブで選択したフォルダーを開く 新しいウィンドウでフォルダーを開く @@ -3243,7 +3381,7 @@ コマンド パレット - コマンド パレットを開く + コマンド パレットをOmniBarで開く 作業フォルダー: @@ -3373,6 +3511,9 @@ 項目を処理しています... + + 項目を検出しています… + 速度: @@ -3385,61 +3526,129 @@ Shown in a StatusCenter card. - {0} 項目を "{1}" へ圧縮しました + {0, plural, other {# 項目}}を "{1}" に圧縮しました Shown in a StatusCenter card. - {0} 項目を "{1}" から "{2}" へ圧縮しました + ​"{1}" から "{2}" に {0, plural, other {# 項目}}を圧縮しました Shown in a StatusCenter card. - {0} 項目を "{1}" へ圧縮中にエラーが発生しました + ​{0, plural, other {# 項目}}を "{1}" に圧縮する際にエラーが発生しました Shown in a StatusCenter card. - {0} 項目を "{1}" から "{2}" へ圧縮できませんでした + "{1}" から "{2}" への {0, plural, other {# 項目}}の圧縮に失敗しました Shown in a StatusCenter card. - {0} 項目を "{1}" へ圧縮しています + {0, plural, other {# 項目}}を "{1}" に圧縮しています Shown in a StatusCenter card. - {0} 項目を "{1}" から "{2}" へ圧縮しています + "{1}" から "{2}" へ{0, plural, other {# 項目}}を圧縮中 + Shown in a StatusCenter card. + + + {0} の "{1}" へのクローンをキャンセルしました + Shown in a StatusCenter card. + + + {0} の "{1}" から "{2}" へのクローンをキャンセルしました + Shown in a StatusCenter card. + + + "{0}" を "{1}" へクローンしました + Shown in a StatusCenter card. + + + {0} を "{1}" から "{2}" へクローンしました + Shown in a StatusCenter card. + + + "{0}" を "{1}" へクローン中にエラーが発生しました + Shown in a StatusCenter card. + + + {0} を "{1}" から "{2}" へクローンできませんでした + Shown in a StatusCenter card. + + + "{0}" を "{1}" へクローンしています + Shown in a StatusCenter card. + + + {0} を "{1}" から "{2}" へクローンしています + Shown in a StatusCenter card. + + + {0} 個のフォントのインストールをキャンセルしました + Shown in a StatusCenter card. + + + ​"{1}" からの {0, plural, other {# 個のフォント}}のインストールがキャンセルしました + Shown in a StatusCenter card. + + + {0, plural, other {# 個のフォント}}をインストールしました + Shown in a StatusCenter card. + + + "{1}" から {0, plural, other {# 個のフォント}}をインストールしました + Shown in a StatusCenter card. + + + {0, plural, other {# 個のフォント}}のインストール中にエラーが発生しました + Shown in a StatusCenter card. + + + "{1}" から {0, plural, other {# 個のフォント}}のインストールに失敗しました + Shown in a StatusCenter card. + + + {0, plural, other {# 個のフォント}}をインストールしています + Shown in a StatusCenter card. + + + ​"{1}" から {0, plural, other {# 個のフォント}}をインストールしています Shown in a StatusCenter card. - {0} 項目の "{1}" へのコピーをキャンセルしました + ​"{1}" への {0, plural, other {# 項目}}のコピーをキャンセルしました Shown in a StatusCenter card. - {0} 項目の "{1}" から "{2}" へのコピーをキャンセルしました + ​"{1}" から "{2}" への {0, plural, other {# 項目}}のコピーをキャンセルしました Shown in a StatusCenter card. - {0} 項目を "{1}" へコピーしました + "{1}" へ {0, plural, other {# 項目}}をコピーしました Shown in a StatusCenter card. - {0} 項目を "{1}" から "{2}" へコピーしました + "{1}" から "{2}" へ {0, plural, other {# 項目}}をコピーしました Shown in a StatusCenter card. - {0} 項目を "{1}" へコピー中にエラーが発生しました + ​"{1}" への {0, plural, other {# 項目}}のコピー中にエラーが発生しました Shown in a StatusCenter card. - {0} 項目を "{1}" から "{2}" へコピーできませんでした + "{1}" から "{2}" への {0, plural, other {# 項目}}のコピーに失敗しました Shown in a StatusCenter card. - {0} 項目を "{1}" へコピーしています + ​"{1}" へ {0, plural, other {# 項目}}をコピー中 Shown in a StatusCenter card. - {0} 項目を "{1}" から "{2}" へコピーしています + "{1}" から "{2}" へ {0, plural, other {# 項目}}をコピー中 Shown in a StatusCenter card. + + {0, plural, one {# 件} other {# 件}}の項目を検出しました + Shown in a StatusCenter card during file discovery phase. + "{0}" の "{1}" への展開をキャンセルしました Shown in a StatusCenter card. @@ -3473,55 +3682,56 @@ Shown in a StatusCenter card. - {0} 項目の "{1}" からの削除をキャンセルしました + 「{1}」から {0, plural, one {# 件} other {# 件}} の削除をキャンセルしました。 + Shown in a StatusCenter card. - {0} 項目を "{1}" から削除しました + 「{1}」 から {0, plural, one {# 件} other {# 件}} を削除しました Shown in a StatusCenter card. - {0} 項目を "{1}" から削除中にエラーが発生しました + 「{1}」から {0, plural, one {# 件} other {# 件}} を削除中にエラーが発生しました Shown in a StatusCenter card. - {0} 項目を "{1}" から削除できませんでした + 「{1}」から {0, plural, one {# 件} other {# 件}} の削除に失敗しました Shown in a StatusCenter card. - {0} 項目を "{1}" から削除しています + 「{1}」から {0, plural, one {# 件} other {# 件}} を削除中 Shown in a StatusCenter card. - {0} 項目の "{1}" への移動をキャンセルしました + 「{1}」から {0, plural, one {# 件} other {# 件}} の移動をキャンセルしました Shown in a StatusCenter card. - {0} 項目の "{1}" から "{2}" への移動をキャンセルしました + 「{1}」から「{2}」への {0, plural, one {# 件} other {# 件}} の移動をキャンセルしました Shown in a StatusCenter card. - {0} 項目を "{1}" へ移動しました + 「{1}」に {0, plural, one {# 件} other {# 件}} を移動しましました Shown in a StatusCenter card. - {0} 項目を "{1}" から "{2}" へ移動しました + 「{1}」から「{2}」へ {0, plural, one {# 件} other {# 件}} を移動しましました Shown in a StatusCenter card. - {0} 項目を "{1}" へ移動しています + 「{1}」から {0, plural, one {# 件} other {# 件}} を移動中 Shown in a StatusCenter card. - {0} 項目を "{1}" から "{2}" へ移動しています + 「{1}」から「{2}」へ {0, plural, one {# 件} other {# 件}} を移動中 Shown in a StatusCenter card. - {0} 項目を "{1}" へ移動中にエラーが発生しました + 「{1}」から {0, plural, one {# 件} other {# 件}} を移動中にエラーが発生しました Shown in a StatusCenter card. - {0} 項目を "{1}" から "{2}" へ移動できませんでした + 「{1}」から「{2}」へ {0, plural, one {# 件} other {# 件}} の移動に失敗しました Shown in a StatusCenter card. @@ -3549,7 +3759,7 @@ Shown in a StatusCenter card. - {0}/{1} 項目が処理されました + 処理済み:{0}/{1, plural, one {# 件} other {# 件}} Shown in a StatusCenter card. Used as "8/20 items processed" @@ -3567,6 +3777,9 @@ 背景を設定できませんでした + + 設定ファイルを開けませんでした + Git ブランチの削除 @@ -3592,7 +3805,7 @@ このタグを適用中にエラーが発生しました - 選択したアーカイブから単一項目の場合は現在のフォルダー、複数項目の場合は新しいフォルダーに項目を抽出する + 選択した {0, plural, one {アーカイブ} other {アーカイブ}} を1 件の場合はここに、複数の場合は新しいフォルダーに展開します ここに展開 (スマート) @@ -3601,7 +3814,7 @@ ファイルとフォルダーを一緒に並べ替える - ファイルとフォルダーを一緒に並べ替える + ファイルとフォルダーを混ぜて並べ替える ファイルを先に並べ替える @@ -3657,9 +3870,6 @@ 質問と議論 - - タイル ビューではサイズの変更はまだ利用できません。 - コンパクト Used to describe layout sizes @@ -3821,6 +4031,10 @@ ICO ファイル This is the friendly name for ICO files. + + ICL ファイル + This is the friendly name for ICL files. + Zip ファイル This is the friendly name for ZIP files. @@ -3829,6 +4043,10 @@ ビットマップ ファイル This is the friendly name for bitmap files. + + 画像ファイル + This is the friendly name for image files. + Num @@ -3855,22 +4073,22 @@ ツールバーを表示する - Setting that controls if the Toolbar is shown in the main view + Setting that controls if the toolbar is shown in the main view タブ操作メニュー - - 縦にペインを追加 + + 縦に分割 - 縦にペインを追加する + ペインを垂直に分割 - - 横にペインを追加 + + 横に分割 - - 横にペインを追加する + + ペインを水平に分割 縦に配置 @@ -3884,8 +4102,8 @@ ペインを横に配置する - - ペインを追加 + + ペインを分割 タイトル バーにタブ操作ボタンを表示する @@ -3893,8 +4111,11 @@ ペインの配置 - - デフォルトのペイン配置 + + 既定のデュアルペインの分割方向 + + + デュアルペインモード @@ -3902,4 +4123,259 @@ + + システム トレイに Files のアイコンを表示する + + + CPU スレッド数 + + + ホーム ページに移動する + + + ツールバー + + + ユーザー ID + + + 名前を一括して変更 + + + 内容を圧縮 + + + [代替データ ストリームの作成] オプションを表示する + + + 代替データ ストリームの作成 + + + 選択した {0, plural, other {# 項目}}の代替データストリームを作成する + + + データ ストリーム名を入力 + + + 代替データ ストリームの作成中にエラーが発生しました + + + 代替データ ストリームはNTFSでフォーマットされたドライブでのみ動作することに注意してください。 + + + 代替データ ストリームは現在表示されていません + + + 代替データ ストリームを表示しますか? ファイルとフォルダーの設定ページからいつでもこの設定を変更できます。 + + + タグの管理 + + + 利用可能 + + + 合計 + + + 常に新しく作成したタブに表示を切り替える + + + シェルフペインを切り替え + + + シェルフペインの表示を切り替え + + + アドレス バーにシェルフ ウィンドウの切り替えボタンを表示する + + + シェルフ + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + 項目をクリア + + + シェルフから削除 + + + シェルフに追加 + Tooltip that displays when dragging items to the Shelf Pane + + + 比較するハッシュ値を入力 + Placeholder that appears in the compare hash text box + + + {0} と一致 + Appears when two compared hashes match, e.g. "Matches SHA256" + + + 一致なし + Appears when two compared hashes don't match + + + パスまたはエイリアス + + + 無効なパスです + + + 連携のテスト + + + {0} が見つかりませんでした。設定を確認して、もう一度やり直してください。 + + + 設定された IDE が見つかりません + + + 設定を開く + + + Visual Studio Code + + + パスまたはアプリ実行エイリアスを入力してください + + + IDE の名前を入力してください + + + リポジトリのクローン + Clone repo dialog title + + + クローン + Primary action button in the clone repo dialog + + + リポジトリの URL + URL textbox header in the clone repo dialog + + + リポジトリをクローンできません + Cannot clone repo dialog title + + + ファイルの比較 + Button that appears in file hash properties that allows the user to compare two files + + + サイズの形式 + + + 2進 + + + 10進 + + + サイドバーを右クリックして、追加したいセクションを選ぶとサイドバーに追加することができます + + + ここにファイルまたはフォルダをドラッグしてタブの間でファイルを操作します + + + 移動先のパスを入力… + + + 機能とコマンドを検索… + + + ファイルとフォルダーを検索… + + + ファイル操作中 + + + ステータスセンターボタンを表示 + + + ステータスセンターの進行状況表示リング + Screen reader name for the status center progress ring + + + アイコンファイル + This is the friendly name for a variety of different icon files. + + + 折りたたんだパンくずリストを表示する + + + {0} 内のフォルダを表示 + + + フォルダ内のフォルダを表示 + + + {0}を含むコマンドは存在しません + + + もっと見る + + + 絞り込む + + + ファイル名 + + + 署名 + + + 署名リスト + + + 発行者: + + + 発行先: + + + 有効期間の開始: + + + 有効期間の終了: + + + 署名が見つかりませんでした。 + + + Windows Terminal でフォルダーを開くオプションを表示する + + + ログファイルを開く + + + 既定のエディターでログファイルを開く + + + 既定のファイルマネージャーでログファイルの場所を開く + + + ログファイルを開けません + + + ステータスバーを表示 + + + カラム表示でのみ + + + 新規 {0} + + + スムーズスクロールを有効にする + + + スクロール + + + [サイドバーにピン留め] オプションを表示する + + + [スタートメニューにピン留め] オプションを表示する + \ No newline at end of file diff --git a/src/Files.App/Strings/ka/Resources.resw b/src/Files.App/Strings/ka/Resources.resw index d1c559b0e194..9c3f0360b22e 100644 --- a/src/Files.App/Strings/ka/Resources.resw +++ b/src/Files.App/Strings/ka/Resources.resw @@ -123,9 +123,15 @@ Copy path + + Copy item path + Copy path with quotes + + Copy selected item path with quotes + გადათვალიერება @@ -160,7 +166,7 @@ გამოტოვება - Select All + Select all Invert selection @@ -211,7 +217,7 @@ დამალული ფაილების და საქაღალდეების ჩვენება - წერტილოვანი ფაილების ჩვენება + Show dot files შესახედაობა @@ -249,6 +255,9 @@ Paste + + Paste shortcut + ახალი @@ -291,8 +300,8 @@ Enter an item name - - Set name + + Create new {0} ნათელი @@ -300,6 +309,9 @@ ბნელი + + Use system setting + აპლიკაცია @@ -515,9 +527,6 @@ Status - - ვინდოუსის ნაგულისხმევი - შედეგები არ არის @@ -530,6 +539,9 @@ ასლის გადატანა {0} -ში + + Clone to {0} + Create shortcut @@ -998,6 +1010,9 @@ ძიების შედეგები {0} -(ს)თვის {1} -ში + + Search results for `{0}` + დეტალები @@ -1052,9 +1067,6 @@ დეტალების ხედი (Ctrl+Shift+1) - - ფილები (Ctrl+Shift+2) - წაშლის თარიღი @@ -1080,10 +1092,25 @@ Toggle the info pane - Toggle the info pane to view the detail/preview panes + Toggle visibility of the detail/preview panes - Toggle the Toolbar + Toggle toolbar + + + Toggle visibility of the toolbar + + + Toggle filter header + + + Toggle visibility of the filter header + + + Toggle dual pane + + + Toggle dual pane mode გადახედვა არ არის შესაძლებელი @@ -1238,6 +1265,12 @@ Open Storage Sense + + Open the Storage Sense page in Windows Settings + + + Cleanup + ასლის შექმნა @@ -1380,7 +1413,7 @@ {0} ელემენტი - დახურული ჩანართების ხელახლა გახსნა + Reopen tab Rename @@ -1587,7 +1620,7 @@ ყველა ბავშვის ობიექტის ნებართვის ჩანაწერის ჩანაცვლება ამ ობიექტის მემკვიდრეობითი ნებართვის ჩანაწერიდან - Close active pane + Close pane შეერთებული გადაფარვის ჩართვა @@ -1646,15 +1679,6 @@ მთავარი - - Ctrl+Shift+1 - - - Ctrl+Shift+2 - - - Ctrl+Shift+6 - არქივის ამოღება წარმატებით დასრულდა. @@ -1712,8 +1736,8 @@ სვეტები - - მოზაიკა + + Cards Open folders in new tab @@ -1749,7 +1773,7 @@ ფაილის იარლიყი - Open items with a single click + Open files with a single click Folder path @@ -1826,6 +1850,18 @@ Register this program for restart + + Start window + + + Normal window + + + Minimized + + + Maximized + ადმინისტრატორის სახელით გაშვება @@ -1853,6 +1889,9 @@ This option modifies the system registry and can have unexpected side effects on your device. Continue at your own risk. + + The flatten operations are permanent and not recommended. Continue at your own risk. + Create Library @@ -1931,6 +1970,9 @@ სხვა ჩანართების დახურვა + + Close all tabs + მუსიკა @@ -1985,11 +2027,23 @@ Behaviors - - Review Files + + Hello! - - Would you like to review Files? + + Enjoying Files? Please consider reviewing in the Microsoft Store. + + + Enjoying Files? Please consider supporting the project on GitHub. + + + Sponsor + + + Rate us + + + Dismiss Set as background @@ -2018,8 +2072,8 @@ Lockscreen - - Open folders with a single click in the Columns Layout + + Open folders with a single click Opening items @@ -2027,6 +2081,21 @@ Compress + + Move all contents from subfolders into the selected location + + + Flatten folder + + + Flatten + + + Flattening a folder will move all contents from its subfolders to the selected location. This operation is permanent and cannot be undone. By using this experimental feature, you acknowledge the risk and agree not to hold the Files team responsible for any data loss. + + + Show flatten options + Select files and folders when hovering over them @@ -2037,7 +2106,7 @@ Restore all items - Do you want to restore the {0} selected item(s)? + Do you want to restore the {0, plural, one {selected item} other {{0} selected items}}? Restore selection @@ -2060,6 +2129,12 @@ Archive password + + Encoding + + + {0} (detected) + Path @@ -2102,9 +2177,24 @@ Splitting size - + Do not split + + Auto + + + Dictionary size + + + Word size + + + Estimated memory usage: {0} + + + Available memory: {0} + CD @@ -2159,8 +2249,14 @@ Edit settings file - - What's new + + Open settings file in your default editor + + + Release Notes + + + Open Release Notes Creating a shortcut in this location requires administrator privileges @@ -2241,25 +2337,22 @@ Context menu options - Show file extensions - - - Show hidden items + File extensions - Format... + ფორმატი Help - Toggle full screen + Full screen Are you sure you want to delete this tag? - Play all + Play Height @@ -2319,7 +2412,7 @@ Increase size - Toggle sort direction + Sort direction Invalid name @@ -2331,34 +2424,37 @@ ვიდეოები - Launch preview popup + Preview popup Toggle compact overlay - Open online help page in browser + Open the online help page in your browser - Toggle full screen + Toggle full screen mode - შეერთებული გადაფარვის ჩართვა + Enter compact overlay mode - შეერთებული გადაფარვის გამორთვა + Exit compact overlay mode - Toggle compact overlay + Toggle compact overlay mode - Go to search box + Start search in the OmniBar - Toggle whether to show hidden items + Toggle visibility of hidden items + + + Toggle visibility of dot files - Toggle whether to show file extensions + Toggle visibility of file extensions Toggle the preview pane to view file previews @@ -2367,52 +2463,64 @@ Toggle whether to show sidebar - Copy item(s) to clipboard + Copy selected {0, plural, one {item} other {items}} - Copy path of selected items to the clipboard + Copy path of the current directory + + + Copy path of selected items + + + Copy path of selected items with quotes - Copy path of selected items with quotes to the clipboard + Copy path of the current directory with quotes - Cut item(s) to clipboard + Cut selected {0, plural, one {item} other {items}} - Paste item(s) from clipboard to current folder + Paste items to the current folder + + + Paste items to the current folder as shortcuts - Paste item(s) from clipboard to selected folder + Paste items to the selected folder + + + Paste to selected folder - ელემენტ(ებ)ის წაშლა + Delete selected {0, plural, one {item} other {items}} Create new folder - Create new shortcut(s) to selected item(s) + Create new {0, plural, one {shortcut} other {shortcuts}} to selected {0, plural, one {item} other {items}} Create new shortcut to any item - სანაგვე ყუთის გასუფთავება + Empty the contents of Recycle Bin Open "Format Drive" menu for selected item - Restore selected item(s) from recycle bin + Restore selected {0, plural, one {item} other {items}} from recycle bin Restore all items from recycle bin - Open item(s) + Open {0, plural, one {item} other {items}} - Open item(s) with selected application + Open {0, plural, one {item} other {items}} with selected application Open parent folder of searched item @@ -2427,28 +2535,28 @@ Select all items - Invert item selection + Invert selected items - Clear item selection + Clear selected items Toggle item selection - Share selected file(s) with others + Share selected {0, plural, one {file} other {files}} with others - Pin item(s) to the Start Menu + Pin {0, plural, one {item} other {items}} to the Start Menu - Unpin item(s) from the Start Menu + Unpin {0, plural, one {item} other {items}} from the Start Menu - Pin folder(s) to Sidebar + Pin {0, plural, one {folder} other {folders}} to Sidebar - Unpin folder(s) from Sidebar + Unpin {0, plural, one {folder} other {folders}} from Sidebar Set selected picture as desktop background @@ -2462,14 +2570,23 @@ Set selected picture as the app background + + Install font + + + Install driver + + + Install certificate + - Install selected font(s) + Install selected {0, plural, one {font} other {fonts}} - Install driver(s) using selected inf file(s) + Install {0, plural, one {driver} other {drivers}} using selected inf {0, plural, one {file} other {files}} - Install selected certificate(s) + Install selected {0, plural, one {certificate} other {certificates}} Run selected application as administrator @@ -2484,28 +2601,28 @@ Launch preview in popup window - Create archive with selected item(s) + Create archive with selected {0, plural, one {item} other {items}} - Create 7z archive instantly with selected item(s) + Create 7z archive with selected {0, plural, one {item} other {items}} - Create zip archive instantly with selected item(s) + Create zip archive with selected {0, plural, one {item} other {items}} - Extract items from selected archive(s) to any folder + Extract selected {0, plural, one {archive} other {archives}} to any folder - Extract items from selected archive(s) to current folder + Extract selected {0, plural, one {archive} other {archives}} to the current folder - Extract items from selected archive(s) to new folder + Extract selected {0, plural, one {archive} other {archives}} to new folder - Rotate selected image(s) to the left + Rotate selected {0, plural, one {image} other {images}} to the left - Rotate selected image(s) to the right + Rotate selected {0, plural, one {image} other {images}} to the right Open settings page @@ -2525,8 +2642,8 @@ Switch to details view - - Switch to tiles view + + Switch to cards view Switch to list view @@ -2625,13 +2742,13 @@ Toggle group sort direction - Open new tab + ახალი ჩანართის გახსნა - Navigate backward in navigation history + Navigate backward - Navigate forward in navigation history + Navigate forward Navigate up one directory @@ -2660,8 +2777,11 @@ Close tabs other than selected tab + + Close all tabs including the current tab + - Reopen last closed tab + Reopen recently closed tab Move to the previous tab @@ -2673,13 +2793,13 @@ Close current tab - Close active pane + Close the active pane Focus other pane - Switch focus to the non active pane + Switch focus to the other pane Toggle the sidebar @@ -2887,13 +3007,13 @@ Untagged - Moves to the previous tab + Previous tab - Moves to the next tab + Next tab - Closes current tab + Close tab Edit path @@ -2923,7 +3043,7 @@ Show checkboxes when selecting items - Focus path bar + Edit path in the OmniBar Create new item @@ -2938,7 +3058,7 @@ Delete permanently - Delete item(s) permanently + Delete selected {0, plural, one {item} other {items}} permanently Play the selected media files @@ -3027,6 +3147,15 @@ You have uncommitted changes on this branch. What would you like to do with them? + + You have an ongoing merge with unresolved conflicts. Please resolve the conflicts or abort the merge to switch branches. + + + Abort merge and switch to '{0}' + + + Stay on '{0}' and resolve conflicts + Switch branch @@ -3055,14 +3184,20 @@ Switch to new branch - Create a folder with the currently selected item(s) + Create a folder with the currently selected {0, plural, one {item} other {items}} - Open properties + Properties + + + Open File Explorer properties Open properties window + + Open File Explorer properties window + Locals @@ -3081,6 +3216,9 @@ Run git fetch + + Clone a git repo + Run git pull @@ -3125,7 +3263,7 @@ Mica Alt - Backdrop Material + Backdrop Solid @@ -3157,17 +3295,17 @@ Files cannot access GitHub right now. - - Open folder in VS Code + + Open folder in {0} - - Open the current directory in Visual Studio Code + + Open the current directory in {0} - - Open repo in VS Code + + Open repo in {0} - - Open the root of the Git repo in Visual Studio Code + + Open the root of the Git repo in {0} Copy code @@ -3194,7 +3332,7 @@ Initialize repo - Initialize a Git repository + Initialize current folder as a git repository Remote Repository Name @@ -3209,13 +3347,13 @@ Sort items by path - Open directory in new pane + Open selected directory in a new pane - Open directory in new tab + Open selected directory in a new tab - Open directory in new window + Open selected directory in a new window Open all @@ -3240,10 +3378,10 @@ The '{0}' command is not ready to be executed. - Command palette + Command Palette - Open command palette + Open Command Palette in the OmniBar Start in: @@ -3373,6 +3511,9 @@ Processing items... + + Discovering items... + Speed: @@ -3385,61 +3526,129 @@ Shown in a StatusCenter card. - Compressed {0} item(s) to "{1}" + Compressed {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Compressed {0} item(s) from "{1}" to "{2}" + Compressed {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error compressing {0} item(s) to "{1}" + Error compressing {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to compress {0} item(s) from "{1}" to "{2}" + Failed to compress {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Compressing {0} item(s) to "{1}" + Compressing {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Compressing {0} item(s) from "{1}" to "{2}" + Compressing {0, plural, one {# item} other {# items}} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Canceled cloning {0} to "{1}" + Shown in a StatusCenter card. + + + Canceled cloning {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Cloned "{0}" to "{1}" + Shown in a StatusCenter card. + + + Cloned {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Error cloning "{0}" to "{1}" + Shown in a StatusCenter card. + + + Failed to clone {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Cloning "{0}" to "{1}" + Shown in a StatusCenter card. + + + Cloning {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Canceled installing {0} fonts + Shown in a StatusCenter card. + + + Canceled installing {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Error installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Failed to install {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} from "{1}" Shown in a StatusCenter card. - Canceled copying {0} item(s) to "{1}" + Canceled copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Canceled copying {0} item(s) from "{1}" to "{2}" + Canceled copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Copied {0} item(s) to "{1}" + Copied {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Copied {0} item(s) from "{1}" to "{2}" + Copied {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error copying {0} item(s) to "{1}" + Error copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to copy {0} item(s) from "{1}" to "{2}" + Failed to copy {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Copying {0} item(s) to "{1}" + Copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Copying {0} item(s) from "{1}" to "{2}" + Copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. + + Discovered {0, plural, one {# item} other {# items}} + Shown in a StatusCenter card during file discovery phase. + Canceled extracting "{0}" to "{1}" Shown in a StatusCenter card. @@ -3473,55 +3682,55 @@ Shown in a StatusCenter card. - Canceled deleting {0} item(s) from "{1}" + Canceled deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Deleted {0} item(s) from "{1}" + Deleted {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Error deleting {0} item(s) from "{1}" + Error deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Failed to delete {0} item(s) from "{1}" + Failed to delete {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Deleting {0} item(s) from "{1}" + Deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Canceled moving {0} item(s) to "{1}" + Canceled moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Canceled moving {0} item(s) from "{1}" to "{2}" + Canceled moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Moved {0} item(s) to "{1}" + Moved {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Moved {0} item(s) from "{1}" to "{2}" + Moved {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Moving {0} item(s) to "{1}" + Moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Moving {0} item(s) from "{1}" to "{2}" + Moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error moving {0} item(s) to "{1}" + Error moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to move {0} item(s) from "{1}" to "{2}" + Failed to move {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. @@ -3549,7 +3758,7 @@ Shown in a StatusCenter card. - {0}/{1} item(s) processed + {0}/{1, plural, one {# item} other {# items}} processed Shown in a StatusCenter card. Used as "8/20 items processed" @@ -3567,6 +3776,9 @@ Failed to set the background wallpaper + + Failed to open the settings file + Delete Git branch @@ -3592,7 +3804,7 @@ There was an error applying this tag - Extract items from selected archive(s) to current folder for single-item archive, or to new folder for multi-item archive + Extract selected {0, plural, one {archive} other {archives}} here for single-item or to new folder for multi-item Extract here (Smart) @@ -3601,7 +3813,7 @@ Sort files and folders together - Sort files and folders together + Sort files and folders in the same list Sort files first @@ -3657,9 +3869,6 @@ Questions & discussions - - Additional sizes are not yet available for the Tiles View. - Compact Used to describe layout sizes @@ -3821,6 +4030,10 @@ ICO File This is the friendly name for ICO files. + + ICL File + This is the friendly name for ICL files. + Zip File This is the friendly name for ZIP files. @@ -3829,6 +4042,10 @@ Bitmap Files This is the friendly name for bitmap files. + + Image Files + This is the friendly name for image files. + Num @@ -3854,23 +4071,23 @@ Status: - Show Toolbar - Setting that controls if the Toolbar is shown in the main view + Show toolbar + Setting that controls if the toolbar is shown in the main view Tab actions menu - - Add vertical pane + + Split vertically - Add a vertical pane + Split pane vertically - - Add horizontal pane + + Split horizontally - - Add a horizontal pane + + Split pane horizontally Arrange vertically @@ -3884,8 +4101,8 @@ Arrange panes horizontally - - Add pane + + Split pane Show tab actions button in the title bar @@ -3893,8 +4110,11 @@ Arrange panes - - Default pane arrangement + + Default dual pane split direction + + + Dual pane mode Horizontal @@ -3902,4 +4122,259 @@ Vertical + + Show Files icon in the System Tray + + + CPU threads + + + Navigate to the home page + + + Toolbars + + + User ID + + + Bulk rename + + + Compress contents + + + Show option to create alternate data stream + + + Create alternate data stream + + + Create alternate data stream for the selected {0, plural, one {item} other {items}} + + + Enter data stream name + + + There was an error creating the alternate data stream + + + Please note that alternate data streams only work on drives formatted as NTFS. + + + Alternate data streams are currently hidden + + + Would you like to display alternate data streams? You can modify this setting anytime from the files and folders settings page. + + + Manage tags + + + Available + + + Total + + + Always switch focus to newly created tab + + + Toggle shelf pane + + + Toggle the visibility of the shelf pane + + + Show shelf pane toggle in address bar + + + Shelf + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + Clear items + + + Remove from shelf + + + Add to shelf + Tooltip that displays when dragging items to the Shelf Pane + + + Enter a hash to compare + Placeholder that appears in the compare hash text box + + + Matches {0} + Appears when two compared hashes match, e.g. "Matches SHA256" + + + No matches found + Appears when two compared hashes don't match + + + Path or alias + + + Invalid path + + + Test integration + + + {0} could not be located. Please check your settings and try again. + + + The configured IDE could not be located + + + Open settings + + + Visual Studio Code + + + Enter a path or launch alias + + + Please enter a name for the IDE + + + Clone repo + Clone repo dialog title + + + Clone + Primary action button in the clone repo dialog + + + Repository URL + URL textbox header in the clone repo dialog + + + Cannot clone repo + Cannot clone repo dialog title + + + Compare a file + Button that appears in file hash properties that allows the user to compare two files + + + Size format + + + Binary + + + Decimal + + + You can add sections to the sidebar by right-clicking and selecting the sections you want to add + + + Drag files or folders here to interact with them across different tabs + + + Enter a path to navigate to... + + + Find features and commands... + + + Search for files and folders... + + + During file operations + + + Show status center button + + + Status center progress ring + Screen reader name for the status center progress ring + + + Icon files + This is the friendly name for a variety of different icon files. + + + Show collapsed path breadcrumbs + + + Show folders in {0} + + + Show folders in Home + + + There are no commands containing {0} + + + See more + + + Filtering for + + + Filename + + + Signatures + + + Signature list + + + Issued by: + + + Issued to: + + + Valid from: + + + Valid to: + + + No signature was found. + + + Show option to open folders in Windows Terminal + + + Open log file + + + Open log file in your default editor + + + Open log file location in your default file manager + + + Unable to open the log file + + + Show status bar + + + Only in Columns View + + + New {0} + + + Enable smooth scrolling + + + Scrolling + + + Show option to Pin to Sidebar + + + Show option to Pin to the Start Menu + \ No newline at end of file diff --git a/src/Files.App/Strings/km-KH/Resources.resw b/src/Files.App/Strings/km-KH/Resources.resw index 68caef0ec676..86cdc7da941e 100644 --- a/src/Files.App/Strings/km-KH/Resources.resw +++ b/src/Files.App/Strings/km-KH/Resources.resw @@ -123,9 +123,15 @@ ចម្លងទីតាំង + + Copy item path + ចម្លងផ្លូវទីតាំង + + Copy selected item path with quotes + រកមើល @@ -154,7 +160,7 @@ គោលដៅថតឯកសារ - is a subfolder of the source folder + គឺជាថតរងនៃថតប្រភព រំលង @@ -184,13 +190,13 @@ ស្វែងរក - Files you've previously accessed will show up here + ឯកសារដែលអ្នកបានអាក់សេសពីមុននឹងបង្ហាញនៅទីនេះ លុបធាតុមួយនេះ - GitHub repository + រីប៉ូហ្គីតហាប់ អំពី @@ -202,13 +208,13 @@ ទ្រង់ទ្រាយ​​​កាល​បរិច្ឆេទ - ស្បែក + ផ្ទៃស្បែក - Show extensions for known file types + បង្ហាញប្រភេទឯកសារដែលគេស្គាល់ - Show hidden files and folders + បង្ហាញឯកសារ និងថតដែលលាក់ Show dot files @@ -217,13 +223,13 @@ រូបរាង - Background color + ពណ៌ផ្ទៃខាងក្រោយ កម្រិតខ្ពស់ - បន្តវិញកន្លែងចាកចេញ + បន្តត្រង់កន្លែងដែលអ្នកចេញ បើកទំព័រថេបថ្មី @@ -232,7 +238,7 @@ បើកទំហំជាក់លាក់ - Desktop + ដេសថប ឯកសារ @@ -249,11 +255,14 @@ បិទភ្ជាប់ + + បិទភ្ជាប់ផ្លូវកាត់ + ថ្មី - Properties + លក្ខណៈសម្បត្តិ បើក @@ -262,7 +271,7 @@ បើកទំព័រថេបថ្មី - Open in new window + បើកផ្ទាំងថ្មី ចែក​រំលែក​ @@ -274,7 +283,7 @@ លុប - Pin to Sidebar + ខ្ទាស់ទៅរបារចំហៀង សូមស្វាគមន៏មកកាន់កម្មវីធីឯកសារ @@ -283,7 +292,7 @@ ផ្តល់ការអនុញ្ញាតិ - To get started, you'll need to grant us permission to display your files. This will open a Settings page where you can grant us this permission. You'll need to reopen the app once you've completed this step. + ដើម្បីចាប់ផ្តើម អ្នកនឹងត្រូវផ្តល់ការអនុញ្ញាតឱ្យយើងបង្ហាញឯកសាររបស់អ្នក នឹងបើកទំព័រការកំណត់ដែលអ្នកអាចផ្តល់ការអនុញ្ញាតនេះដល់ពួកយើង អ្នកនឹងត្រូវបើកកម្មវិធីឡើងវិញនៅពេលដែលអ្នកបានបញ្ចប់ជំហាននេះ។ បង្ហាញ @@ -291,8 +300,8 @@ បញ្ចូលឈ្មោះ - - កំណត់ឈ្មោះ + + បង្កើតថ្មី {0} ភ្លឺ @@ -300,6 +309,9 @@ ងងឹត + + ប្រើ​​​​​ការ​​​​កំណត់​របស់​​​​​ប្រព័ន្ធ ​ + កម្មវិធី @@ -313,7 +325,7 @@ ឯកសារ​ថ្មី​ - This folder is empty. + ថតទទេរ ត្រលប់ @@ -322,58 +334,58 @@ បញ្ចូនបន្ត - Up + លើ ធ្វើឱ្យស្អាត - An item with this name already exists in this directory. + ធាតុដែលមានឈ្មោះនេះមានរួចហើយនៅក្នុងថតនេះ - Replace existing item + ជំនួសធាតុដែលមានស្រាប់ - Item already exists + ធាតុមានរួចហើយ - Set as lockscreen background + កំណត់ផ្ទៃខាងក្រោយផ្ទាំងចាក់សោរ - Set as app background + កំណត់ជាផ្ទៃខាងក្រោយកម្មវិធី - We weren't able to delete this item + យើងមិនអាចលុបធាតុនេះបានទេ - Please insert the necessary drive to access this item. + សូមបញ្ចូលដ្រាយចាំបាច់ដើម្បីអាក់សេសធាតុនេះ - Drive unplugged + ដ្រាយមិនបានភ្ជាប់ - The file you are attempting to access may have been moved or deleted. + ឯកសារដែលអ្នកកំពុងព្យាយាមចូលប្រើអាចត្រូវបានផ្លាស់ទី ឬលុប - Cannot open properties for this file + មិនអាចបើកលក្ខណសម្បត្តិសម្រាប់ឯកសារនេះបានទេ - The file you are attempting to access may have been moved or deleted. + ឯកសារដែលអ្នកកំពុងព្យាយាមចូលប្រើអាចត្រូវបានផ្លាស់ទី ឬលុប រក​មិន​ឃើញ​ឯកសារទេ - The folder you are attempting to access may have been moved or deleted. + ថតឯកសារដែលអ្នកកំពុងព្យាយាមចូលប្រើអាចត្រូវបានផ្លាស់ទី ឬលុប - Did you delete this folder? + តើអ្នកបានលុបថតឯកសារនេះទេ? - The file you are attempting to access is currently being used by {0} + ឯកសារដែលអ្នកកំពុងព្យាយាមចូលប្រើបច្ចុប្បន្នកំពុងត្រូវបានប្រើប្រាស់ដោយ {0} - The file you are attempting to access is currently being used by another application + ឯកសារដែលអ្នកកំពុងព្យាយាមចូលប្រើបច្ចុប្បន្នកំពុងត្រូវបានប្រើប្រាស់ដោយកម្មវិធីផ្សេងទៀត ឯកសារកំពុងប្រើប្រាស់ @@ -382,40 +394,40 @@ ប្លង់ - Read-only + ត្រឹមតែអាន {0, plural, one {# day ago} other {# days ago}} - 1 day ago + 1 ថ្ងៃមុន - {0} days ago + {0} ថ្ងៃមុន {0, plural, one {# hour ago} other {# hours ago}} - 1 hour ago + 1 ម៉ោងមុន - {0} hours ago + {0} ម៉ោងមុន {0, plural, one {# minute ago} other {# minutes ago}} - 1 minute ago + 1 នាទីមុន - {0} minutes ago + {0} នាទីមុន {0, plural, one {# second ago} other {# seconds ago}} - ១ វិនាទី​មុន + 1 វិនាទី​មុន {0} វិនាទី​មុន @@ -427,7 +439,7 @@ ប្រតិបត្តិការស្នើសុំត្រូវបានបដិសេដ - {0} free of {1} + {0} នៅទំនេរនៃ {1} ផ្ញើ​ទៅ៖ @@ -450,10 +462,10 @@ បាទ/ចាស - Are you sure you want to permanently delete all these items? + តើអ្នកប្រាកដថាចង់លុបធាតុទាំងអស់នេះជាអចិន្ត្រៃយ៍ទេ? - Empty recycle bin + ទទេរធុងសំរាម បៃ @@ -474,7 +486,7 @@ ប៉េរ៉ាបៃ - B + បៃ {0, number} {0, plural, one {file} other {files}}, {1, number} {1, plural, one {folder} other {folders}} @@ -483,16 +495,16 @@ {0, number} {0, plural, one {file} other {files}}, {1, number} {1, plural, one {folder} other {folders}} from {2, number} {2, plural, one {location} other {locations}} - Run as another user + ដំណើរការជាអ្នកប្រើប្រាស់ផ្សេង - All type of {0} + គ្រប់ប្រភេទនៃ {0} - Different types + ប្រភេទផ្សេងគ្នា - All in {0} + ទាំងអស់ក្នុង {0} ទំហំ​ប្រើប្រាស់៖ @@ -515,14 +527,11 @@ ស្ថានភាព - - Windows default - គ្មានលទ្ធផល - Can't access any items to display + មិនអាចចូលប្រើធាតុណាមួយដើម្បីបង្ហាញបានទេ ប្តូរទៅ​ {0} @@ -530,6 +539,9 @@ ចម្លងទៅ​ {0} + + Clone to {0} + បង្កើតផ្លូវកាត់ @@ -540,148 +552,148 @@ Arguments: - Destination: + ទិសដៅ - Shortcut type: + ប្រភេទផ្លូវកាត់៖ - Web link + បញ្ជាប់វេប - ទូទៅ + ជាទូទៅ - Shortcut + ផ្លូវកាត់ - Internet shortcut + ផ្លូវកាត់អុីនធើណែត - {0} - shortcut + {0} ផ្លូវកាត់ - Files ran into a problem that the developers didn't prepare for yet. + ឯកសារមានកំហុសដែលអ្នកអភិវឌ្ឍន៍មិនទាន់រៀបចំនៅឡើយ - Something went wrong! + មានកំហុសកើតឡើង! - Report this issue + រាយការណ៍ពីបញ្ហានេះ - The item referenced is either invalid or inaccessible.{0}Error message:{0}{1} + ធាតុដែលបានយោងគឺមិនត្រឹមត្រូវ ឬមិនអាចចូលដំណើរការបាន។{0}សារកំហុស៖{0}{1} - Invalid item + ធាតុមិនត្រឹមត្រូវ ជំនាន់: - There's nothing to share right now... + មិនមានអ្វីត្រូវចែករំលែកពេលនេះ ... - The items you've selected will be shared + ធាតុដែលអ្នកបានជ្រើសរើសនឹងត្រូវបានចែករំលែក - The selected item will be shared + ធាតុដែលបានជ្រើសរើសនឹងត្រូវបានចែករំលែក - Sharing {0} + កំពុងចែករំលែក {0} - Sharing {0} {1} + កំពុងចែករំលែក {0} {1} - The item you're attempting to rename no longer exists. Please verify the correct location of the item you need to rename. + ធាតុដែលអ្នកកំពុងព្យាយាមប្តូរឈ្មោះលែងមានទៀតហើយ សូមផ្ទៀងផ្ទាត់ទីតាំងត្រឹមត្រូវនៃធាតុដែលអ្នកត្រូវប្តូរឈ្មោះ - Item no longer exists + ធាតុលែងមានទៀតហើយ - The name specified was invalid. Please check the desired item name and try again. + ឈ្មោះដែលបានបញ្ជាក់គឺមិនត្រឹមត្រូវទេ សូមពិនិត្យមើលឈ្មោះធាតុដែលចង់បាន ហើយព្យាយាមម្តងទៀត - Invalid item name + ឈ្មោះធាតុមិនត្រឹមត្រូវ - The length of the item name specified exceeds the maximum limit. Please check the desired name and try again. + ប្រវែងនៃឈ្មោះធាតុដែលបានបញ្ជាក់លើសពីកំណត់អតិបរមា សូមពិនិត្យមើលឈ្មោះដែលចង់បាន ហើយព្យាយាមម្តងទៀត - Item name was too long + ឈ្មោះធាតុគឺវែងពេក - Show more options + បង្ហាញជម្រើសច្រើនទៀត - The application needs to be restarted in order to apply these settings, would you like to restart the app? + កម្មវិធីត្រូវចាប់ផ្តើមឡើងវិញ ដើម្បីអនុវត្តការកំណត់ទាំងនេះ តើអ្នកចង់ចាប់ផ្តើមកម្មវិធីឡើងវិញទេ? ឧត្ថម្ភយើងតាមរយៈហ្គិតហាប់ - We weren't able to create this item + យើង​មិន​អាច​បង្កើត​ធាតុ​នេះ​បាន​ទេ - Access Denied + អាក់សេសបដិសេធ - Set as + កំណត់ជា បោះបង់ - Create a new item + បង្កើតធាតុថ្មី - Choose a type for this new item below + ជ្រើសរើសប្រភេទសម្រាប់ធាតុថ្មីនេះខាងក្រោម - File + ឯកសារ - Creates an empty file + បង្កើតឯកសារទទេ - ថត + ថតឯកសារ បង្កើតថតទំនេរ - Refresh the directory + ផ្ទុកឡើងវិញថត ភាសា - Move shell extensions into a sub menu + ប្តូរផ្នែកបន្ថែមសែលទៅក្នុងម៉ឺនុយរង - Show confirmation dialog when deleting items + បង្ហាញប្រអប់បញ្ជាក់នៅពេលលុបធាតុ - There was an issue saving some properties. + មានបញ្ហាក្នុងការរក្សាទុកលក្ខណៈសម្បត្តិមួយចំនួន - Error + កំហុស - Close anyway + បិទយ៉ាងណាក៏ដោយ - Rating + ការវាយតម្លៃ - Item Path + ផ្លូវធាតុ - Item Type + ប្រភេទធាតុ - Title + ចំណងជើង ប្រធាន​បទ @@ -699,7 +711,7 @@ ជម្រៅ​ប៊ីត ៖ - Dimensions + វិមាត្រ Horizontal Size @@ -723,100 +735,100 @@ មិនបានបញ្ជាក់ - Longitude Decimal + រយៈទទឹងទសភាគ - Latitude Decimal + រយៈបណ្តោយទសភាគ - Altitude + រយៈកម្ពស់ - Date Taken + កាលបរិច្ឆេទ​ថត - Camera Manufacturer + ក្រុមហ៊ុនផលិតកាមេរ៉ា - Camera Model + ម៉ូដែលកាមេរ៉ា - Exposure Time + ពេលវេលាបញ្ចេញពន្លឺ Focal Length - Aperture + ជម្រៅ​រូបភាព - People Names + ឈ្មោះមនុស្ស - Channel Count + ចំនួនកាណាល់ - Format + ទ្រង់ទ្រាយ Sample Rate - Album Artist + អាល់ប៊ុមសិល្បករ - Album Title + ចំណង​ជើង​អាល់ប៊ុម - Artist + សិល្បករ - Beats Per Minute + ចង្វាក់ក្នុងមួយនាទី - Composer + អ្នក​និពន្ធ Conductor - Disc Number + លេខឌីស - Genre + ប្រភេទ - Track Number + លេខ​បទ - Duration + រយៈពេល - Frame Count + ចំនួនស៊ុម Protection Type - Author Url + អ្នកនិពន្ធ Url - Content Distributor + អ្នកចែកចាយមាតិកា - Date Released + កាលបរិច្ឆេទចេញផ្សាយ - Series Name + ឈ្មោះស៊េរី - Season Number + លេខរដូវកាល - Episode Number + លេខភាគ - Producer + ផលិតករ Promotion Url @@ -840,67 +852,67 @@ Thumbnail Small Uri - User Web Url + វេបយូអអិលអ្នកប្រើប្រាស់ - Writer + អ្នកនិពន្ធ - Year + ឆ្នាំ - Contributor + អ្នក​ចូលរួម - Last Author + អ្នកនិពន្ធចុងក្រោយ - Revision Number + ចំនួន​ពិនិត្យ​ឡើង​វិញ ជំនាន់: - Date Created + កាលបរិច្ឆេទបង្កើត - Total Editing Time + ពេលវេលាកែសម្រួលសរុប - Template + ពុម្ព - Word Count + ចំនួនពាក្យ - Character Count + ចំនួនតួអក្សរ - Line Count + ចំនួន​​​បន្ទាត់​ - Paragraph Count + ចំនួនកថាខណ្ឌ - Page Count + ចំនួន​ទំព័រ - Slide Count + ចំនួនស្លាយ Frame Rate - Encoding Bitrate + អត្រាប៊ីតការអ៊ិនកូដ - Audio Encoding Bitrate + អត្រាប៊ីតអ៊ិនកូដអូឌីយ៉ូ - Video Encoding Bitrate + អត្រាប៊ីតអ៊ិនកូដវីដេអូ - Compression + ការបង្ហាប់ Frame Width @@ -909,100 +921,103 @@ Frame Height - Orientation + ទិស - Core + ស្នូល - Image + រូបភាព - Photo + រូបថត - GPS + ជីភីអេស - Media + មេឌៀ - Audio + អូឌីយ៉ូ - Music + តន្ត្រី - Video + វីដេអូ​ - Document + ឯកសារ - Address + អាសយដ្ឋាន - Selection options + ជម្រើសជ្រើសរើស - Select + ជ្រើសរើស - Recycle bin item + ធាតុក្នុងធុងសំរាម - Shortcut item + ប្រភេទផ្លូវកាត់៖ - Drives + ដ្រាយ - Move here + ផ្លាស់ទីនៅទីនេះ - Safe to remove hardware + មានសុវត្ថិភាពក្នុងការដកហាតវែរចេញ - The device can now be safely removed from the computer. + ឧបករណ៍នេះអាចត្រូវបានយកចេញដោយសុវត្ថិភាពពីកុំព្យូទ័រពេលនេះ - Problem Ejecting Device + មានបញ្ហាក្នុងច្រានឧបករណ៍ចេញ - This device is currently in use. Close any programs, windows or tabs that might be using the device, and then try again. + ឧបករណ៍នេះកំពុងប្រើប្រាស់ បិទកម្មវិធី វិនដូ ឬផ្ទាំងណាមួយដែលអាចនឹងកំពុងប្រើឧបករណ៍ ហើយបន្ទាប់មកព្យាយាមម្តងទៀត - Eject + ច្រានចេញ - Duplicate tab + បើកផ្ទាំងស្ទួន - Move tab to new window + ផ្លាស់ទីថេបទៅផ្ទាំងថ្មី ថេបថ្មី - Some properties may contain personal information. + លក្ខណៈសម្បត្តិមួយចំនួនអាចផ្ទុកព័ត៌មានផ្ទាល់ខ្លួន - Clear All Properties + លុបលក្ខណៈសម្បត្តិទាំងអស់ - Check the status of file operations here + ពិនិត្យស្ថានភាពប្រតិបត្តិការឯកសារនៅទីនេះ - Status center + មណ្ឌលស្ថានភាព - Search results in {1} for {0} + លទ្ធផលស្វែងរកក្នុង {1} សម្រាប់ {0} + + + លទ្ធផលស្វែងរកសម្រាប់ `{0}` - Details + ពត៌មាន​លម្អិត - No + ទេ Show protected system files @@ -1011,190 +1026,202 @@ Sync layout and sorting preferences across directories - Customize the right click context menu + ប្តូរម៉ឺនុយបរិបទចុចខាងស្តាំ - Original path + ផ្លូវដើម - Add + បន្ថែម - Edit + កែសម្រួល - Remove + ដកចេញ - Open tabs in dual pane mode + បើកផ្ទាំងផែនបែបផ្ទួន - Show option to open folders in a new pane + បង្ហាញជម្រើសដើម្បីបើកថតនៅក្នុងផ្ទាំងផែនថ្មី - Show option to copy path + បង្ហាញជម្រើសដើម្បីចម្លងផ្លូវ - Show option to create folder with selection + បង្ហាញជម្រើសដើម្បីបង្កើតថតជាមួយជម្រើស - Show option to create shortcut + បង្ហាញជម្រើសដើម្បីបង្កើតផ្លូវកាត់ - New pane + ផេនថ្មី - Open in new pane + បើកក្នុងផែនថ្មី ឯកសារ និងថត - Details (Ctrl+Shift+1) - - - Tiles (Ctrl+Shift+2) + លម្អិត (Ctrl+Shift+1) - Date deleted + កាលបរិច្ឆេទត្រូវបានលុប - Cloud Drives + ថាសពពក - Confirm + បញ្ជាក់ - Desired name + ឈ្មោះដែលចង់បាន - Toggle the preview pane + បិទបើកផ្ទាំងមើលជាមុន - Toggle the details pane + បិទបើកផ្ទាំងលម្អិត - Toggle the details pane to view basic file properties + បិទបើកផ្ទាំងលម្អិត ដើម្បីមើលមូលដ្ឋានព័ត៌មានឯកសារ - Toggle the info pane + បិទបើកផ្ទាំងព័ត៌មាន - Toggle the info pane to view the detail/preview panes + Toggle visibility of the detail/preview panes - Toggle the Toolbar + Toggle toolbar + + + Toggle visibility of the toolbar + + + បិទ/បើកក្បាលតម្រង + + + Toggle visibility of the filter header + + + Toggle dual pane + + + Toggle dual pane mode - No preview available + មិនមានការមើលជាមុនទេ - Item Name + ឈ្មោះធាតុ - Unpin from Sidebar + ដកខ្ទាស់ពីរបារចំហៀង - Network + បណ្តាញ - File details + ព័ត៌មានលម្អិតអំពីឯកសារ - File preview + មើល​ឯកសារ​ជា​មុន - Selected file preview pane + ផែនជ្រើសរើសឯកសារមើលមុន ផ្តល់យោបល់ - Available when online + អាចប្រើបានអហ្វឡាញ ឯកសារ - Available offline + លទ្ធភាពអហ្វឡាញ - Partially available offline + លទ្ធភាពអហ្វឡាញដោយផ្នែក - Syncing + កំពុងធ្វើសមកាលកម្ម - Excluded from sync + ដកចេញពីការធ្វើសមកាលកម្ម - Not calculated + មិនបានគណនា - Unknown + មិនស្គាល់ - If you change a file extension, the file might become unusable. Are you sure you want to change it? + ប្រសិនបើអ្នកផ្លាស់ប្តូរផ្នែកបន្ថែមឯកសារ ឯកសារអាចនឹងមិនអាចប្រើបាន តើអ្នកប្រាកដថាចង់ផ្លាស់ប្តូរវាទេ? - CD ROM Drive + ដ្រាយស៊ីឌីរ៉ូម - Cloud Drive + ដ្រាយពពក Fixed Disk Drive - Floppy Disk Drive + ថាសទន់ - Network Drive + ថាសបណ្តាញ - Unmounted Drive + ថាស​ដែល​មិន​បាន​ភ្ជាប់ - RAM Disk Drive + ដ្រាយថាសរ៉េម - Removable Storage Device + ឧបករណ៍ផ្ទុកដែលអាចដកចេញបាន - Unknown + មិនស្គាល់ - Virtual Drive + ថាសនិម្មិត - Date created + កាលបរិច្ឆេទបង្កើត - More options... + បង្ហាញជម្រើសច្រើនទៀត... Map network drive - Pinned + បានខ្ទាស់ - Libraries + បណ្ណាល័យ - No item selected + មិនមានធាតុដែលត្រូវបានជ្រើសរើស - Target + គោលដៅ Arguments - Default + បុរេជម្រើស - Item count + ចំនួនធាតុ This action requires administrator rights @@ -1206,19 +1233,19 @@ Columns (Ctrl+Shift+6) - Pin to the Start Menu + ខ្ទាស់​ទៅ​បញ្ជី​ចាប់ផ្ដើម​ - Unpin from the Start Menu + ដក​ខ្ទាស់​ចេញ​ពី​បញ្ជី​ចាប់ផ្ដើម​ - Library + បណ្ណាល័យ​ - Locations: + ទីតាំង៖ - Set as default save path + កំណត់ជាទីតាំងរក្សាលំនាំដើម No locations @@ -1238,6 +1265,12 @@ Open Storage Sense + + Open the Storage Sense page in Windows Settings + + + Cleanup + ចម្លង @@ -1290,88 +1323,88 @@ Create folder with selection - Type: + ប្រភេទ៖ - Date modified: + កាលបរិច្ឆេទកែប្រែ - Open with + បើកជាមួយ - File icon + អាយខនឯកសារ Original path column - Item type column + ជួរឈរប្រភេទធាតុ - Date modified column + ជួរឈរកាលបរិច្ឆេទកែប្រែ - Date deleted column + ជួរឈរកាលបរិច្ឆេទលុប - Sort + តម្រៀប​ - Date modified + កាលបរិច្ឆេទកែប្រែ Original folder - Descending + ចុះ - Huge + ធំសម្បើម - Very large + ធំខ្លាំង - Large + ធំ - Medium + មធ្យម - Small + តូច - Tiny + តូច​ល្អិត - Future + អនាគត - Today + ថ្ងៃនេះ - Yesterday + ម្សិលមិញ - Earlier this week + កាលពីដើមសប្តាហ៍នេះ - Last week + សប្តាហ៍មុន - Earlier this month + កាលពីដើមខែនេះ - Last month + ខែ​មុន - Earlier this year + កាលពីដើមឆ្នាំនេះ - Last year + ឆ្នាំមុន - Year {0} + ឆ្នាំ {0} {0} item @@ -1380,52 +1413,52 @@ {0} items - Reopen closed tab + បើកថេបសាថ្មី - Rename + ប្តូរឈ្មោះ - Privacy + ឯកជនភាព - the Recycle Bin + ធុងសម្រាម - Canceling + កំពុងបោះបង់ - Clear + សម្អាត - Widgets + ធាតុក្រាហ្វិក - Sync status + ស្ថានភាពសមកាល - Security + សុវត្ថិភាព - Advanced permissions + ការអនុញ្ញាតកម្រិតខ្ពស់ - Allow + អនុញ្ញាត - Deny + បដិសេធ - Full control + សិទ្ធិពេញលេញ List directory contents - Modify + កែសម្រួល - Permissions for {0} + សិទ្ធិសម្រាប់ {0} Read and execute @@ -1434,82 +1467,82 @@ Read - Group or user names + ឈ្មោះក្រុម ឬអ្នកប្រើប្រាស់ Write - Unknown account + គណនីមិនស្គាល់ - You do not have permissions to view the security properties of this object. Click "Advanced permissions" to proceed. + អ្នកមិនមានសិទ្ធិមើលលក្ខណៈសម្បត្តិសុវត្ថិភាពរបស់វត្ថុនេះទេ។ ចុច "ការអនុញ្ញាតកម្រិតខ្ពស់" ដើម្បីបន្ត - Owner: + ម្ចាស់៖ - Unknown owner + មិនស្គាល់ម្ចាស់ Extract archive - Open destination folder when complete + បើកថតទិសដៅនៅពេលបញ្ចប់ Extract to {0}\ - Create new library + បង្កើតបណ្ណាល័យថ្មី - Restore default libraries + ស្តារបណ្ណាល័យលំនាំដើម - Are you sure you want to restore the default libraries? All your files will remain on your storage. + តើអ្នកប្រាកដថាចង់ស្ដារបណ្ណាល័យលំនាំដើមទេ? ឯកសារទាំងអស់របស់អ្នកនឹងនៅតែមាននៅលើទំហំផ្ទុករបស់អ្នក - Restore Libraries + ស្តារបណ្ណាល័យ - Owner + ម្ចាស់ - Special + ពិសេស - Access + អាក់សេស - Applies to + អនុវត្តចំពោះ - Principal + ប្រាក់ដើម - files + ឯកសារ - this folder + ថតនេះ - subfolders + ថតរង Inherited - Permissions + សិទ្ធិ You do not have permissions to view the security properties of this object. You can try to take ownership of this object. - This folder and files + ថត និងឯកសារនេះ - This folder, subfolders and files + ថត ថតរង និងឯកសារនេះ Only files @@ -1587,19 +1620,19 @@ Replace all child object permissions entries with inheritable permission entries from this object - បិទផ្ទាំងសកម្ម + Close pane - Enter compact overlay + ចូលត្រួលើគ្នាដែល Exit compact overlay - File description + ពិពណ៌នាឯកសារ - Company + ឈ្មោះក្រុមហ៊ុន ភាសា @@ -1608,7 +1641,7 @@ Trademarks - File version + ជំនាន់ឯកសារ Load full preview @@ -1620,40 +1653,31 @@ {0, plural, one {# item} other {# items}} ({1, plural, one {# file} other {# files}}, {2, plural, one {# folder} other {# folders}}) - Uncompressed size + ទំហំមិនទាន់បង្ហាប់ WSL - Hide {0} section + លាក់ផ្នែក {0} - Add file + បន្ថែមឯកសារ - Updates available + មានលទ្ធភាពអាប់ដេត - Updates are ready to install + ការអាប់ដេតរួចរាល់ក្នុងការដំឡើង - Close + បិទ - Update + អាប់ដេត - Home - - - Ctrl+Shift+1 - - - Ctrl+Shift+2 - - - Ctrl+Shift+6 + ទំព័រដើម The archive extraction completed successfully. @@ -1668,70 +1692,70 @@ Open parent folder - Unknown + មិនស្គាល់ - Edit tags + កែប្រែថេគ Part of set - Open drive + បើកដ្រាយ - Please insert a disc into drive {0} + សូមបញ្ចូលឌីសទៅក្នុងដ្រាយ {0} - Insert a disc + បញ្ចូលឌីស - OK + អូខេ Credential required - Anonymous + អនាមិក Provide your credential: - UserName + អត្ថនាម - No items found + រកមិនឃើញធាតុទេ បើកទៅកាន់ទីតាំងឯកសារ - Create link in {0} + បង្កើតតំណភ្ជាប់ ជួរឈរ - - Tiles + + Cards - Open folders in new tab + បើកថតក្នុងថែបថ្មី - Status center + មណ្ឌលស្ថានភាព - Date created column + ជួរឈរកាលបរិច្ឆេទបង្កើត - Item size column + ជួរឈរទំហំធាតុ - Experimental feature flags + ទង់មុខងារពិសោធន៍ - ជំនួយ + ជំនួយ និងគាំទ្រ ដាក់ស្នើសុំមុខងារ @@ -1746,31 +1770,31 @@ Use Files as Open File Dialog - Tag + ស្លាក - Open items with a single click + Open files with a single click - Folder path + ទីតាំង​ថត - Export settings + ការកំណត់នាំចេញ - Import settings + ការកំណត់នាំចូល - Couldn't import settings. The settings file is corrupted. + មិនអាចនាំចូលការកំណត់បានទេ ឯកសារការកំណត់ត្រូវបានខូច - Error importing settings + កំហុសនាំចូលការកំណត់ - Empty Recycle Bin + ទទេរធុងសំរាម - Sidebar + បារចំហៀង Choose a custom folder icon @@ -1782,10 +1806,10 @@ Restore default - Open tab in existing instance when opening Files from another app + បើកផ្ទាំងនៅក្នុងឧទាហរណ៍ដែលមានស្រាប់ នៅពេលបើកឯកសារពីកម្មវិធីផ្សេងទៀត - Startup settings + ការកំណត់ពេលចាប់ផ្តើម Compatibility @@ -1826,6 +1850,18 @@ Register this program for restart + + Start window + + + Normal window + + + Minimized + + + Maximized + Run as administrator @@ -1853,6 +1889,9 @@ This option modifies the system registry and can have unexpected side effects on your device. Continue at your own risk. + + The flatten operations are permanent and not recommended. Continue at your own risk. + Create Library @@ -1863,7 +1902,7 @@ Calculate folder sizes - Recent files + ឯកសារថ្មី Open Files on Windows startup @@ -1872,10 +1911,10 @@ Attributes - Hidden + លាក់ - More details + លម្អិតបន្ថែមពីឯកសារ Read only @@ -1884,10 +1923,10 @@ Cleanup your drive contents - Type + ប្រភេទ - Bytes + បៃ Extract @@ -1920,22 +1959,25 @@ Rotate right - Install + តម្លើង - Close tabs to the left + បិទថេបខាងឆេ្វង - Close tabs to the right + បិទថេបខាងស្ដាំ - Close other tabs + បិទថេបដទៃ + + + បិទផ្ទាំងទាំងអស់ - Music + តន្ត្រី - Pictures + រូបភាព Update Files @@ -1944,7 +1986,7 @@ ស្លាក - Universal + ម៉ោងសកល ex: {0}, {1} @@ -1971,7 +2013,7 @@ Size all columns to fit - Adaptive layout + ប្លង់​បត់បែន Adaptive layout (Ctrl+Shift+7) @@ -1985,26 +2027,38 @@ Behaviors - - Review Files + + Hello! + + + Enjoying Files? Please consider reviewing in the Microsoft Store. + + + Enjoying Files? Please consider supporting the project on GitHub. + + + Sponsor - - Would you like to review Files? + + Rate us + + + Dismiss Set as background - Set as desktop background + កំណត់ជាផ្ទាំងបង្ហាញអេក្រង់ - Set as default + កំណត់ជាបុរេជម្រើស - Date column + ជួរឈរកាលបរិច្ឆេទ - Date created column + ជួរឈរកាលបរិច្ឆេទបង្កើត Size column @@ -2018,8 +2072,8 @@ Lockscreen - - Open folders with a single click in the Columns Layout + + Open folders with a single click Opening items @@ -2027,6 +2081,21 @@ Compress + + Move all contents from subfolders into the selected location + + + Flatten folder + + + Flatten + + + Flattening a folder will move all contents from its subfolders to the selected location. This operation is permanent and cannot be undone. By using this experimental feature, you acknowledge the risk and agree not to hold the Files team responsible for any data loss. + + + Show flatten options + Select files and folders when hovering over them @@ -2037,7 +2106,7 @@ Restore all items - Do you want to restore the {0} selected item(s)? + Do you want to restore the {0, plural, one {selected item} other {{0} selected items}}? Restore selection @@ -2060,6 +2129,12 @@ Archive password + + Encoding + + + {0} (detected) + Path @@ -2102,9 +2177,24 @@ Splitting size - + Do not split + + Auto + + + Dictionary size + + + Word size + + + Estimated memory usage: {0} + + + Available memory: {0} + CD @@ -2124,7 +2214,7 @@ Password - Format + ទ្រង់ទ្រាយ Sync status column @@ -2133,7 +2223,7 @@ Group by - Sort by + តម្រៀបតាម Group in descending order @@ -2159,8 +2249,14 @@ Edit settings file - - What's new + + Open settings file in your default editor + + + Release Notes + + + Open Release Notes Creating a shortcut in this location requires administrator privileges @@ -2181,22 +2277,22 @@ View more - Show edit tags flyout + បង្ហាញការកែសម្រួលស្លាក - Show compression options + បង្ហាញជម្រើសបង្ហាប់ - Show send to menu + បង្ហាញម៉ឺនុយការផ្ញើទៅកាន់ - Show option to open folders in a new tab + បង្ហាញជម្រើសដើម្បីបើកថតនៅក្នុងផ្ទាំងផែនថ្មី - Show option to open folders in a new window + បង្ហាញជម្រើសដើម្បីបើកថតនៅក្នុងផ្ទាំងផែនថ្មី - Quick access + ចូលប្រើរហ័ស Enter your credentials to connect to: {0} @@ -2229,7 +2325,7 @@ Tag color - New tag + ស្លាកថ្មី Create new tag @@ -2238,28 +2334,25 @@ Loading... - Context menu options + ជម្រើសមុឺនុយបរិបទ - Show file extensions - - - Show hidden items + File extensions - Format... + ទ្រង់ទ្រាយ - Help + ជំនួយ - Toggle full screen + Full screen Are you sure you want to delete this tag? - Play all + Play Height @@ -2316,10 +2409,10 @@ Decrease size - Increase size + បង្កើនទំហំ - Toggle sort direction + Sort direction Invalid name @@ -2328,91 +2421,106 @@ Name must not be empty or start or end with a period. - Videos + វីដេអូ - Launch preview popup + Preview popup Toggle compact overlay - Open online help page in browser + Open the online help page in your browser - Toggle full screen + Toggle full screen mode - Enter compact overlay + Enter compact overlay mode - Exit compact overlay + Exit compact overlay mode - Toggle compact overlay + Toggle compact overlay mode - Go to search box + Start search in the OmniBar - Toggle whether to show hidden items + Toggle visibility of hidden items + + + Toggle visibility of dot files - Toggle whether to show file extensions + Toggle visibility of file extensions - Toggle the preview pane to view file previews + បិទបើកផ្ទាំងមើលជាមុន ដើម្បីមើលការមើលឯកសារជាមុន Toggle whether to show sidebar - Copy item(s) to clipboard + Copy selected {0, plural, one {item} other {items}} - Copy path of selected items to the clipboard + Copy path of the current directory + + + Copy path of selected items + + + Copy path of selected items with quotes - Copy path of selected items with quotes to the clipboard + Copy path of the current directory with quotes - Cut item(s) to clipboard + Cut selected {0, plural, one {item} other {items}} - Paste item(s) from clipboard to current folder + Paste items to the current folder + + + Paste items to the current folder as shortcuts - Paste item(s) from clipboard to selected folder + Paste items to the selected folder + + + Paste to selected folder - Delete item(s) + Delete selected {0, plural, one {item} other {items}} Create new folder - Create new shortcut(s) to selected item(s) + Create new {0, plural, one {shortcut} other {shortcuts}} to selected {0, plural, one {item} other {items}} Create new shortcut to any item - Empty recycle bin + Empty the contents of Recycle Bin Open "Format Drive" menu for selected item - Restore selected item(s) from recycle bin + Restore selected {0, plural, one {item} other {items}} from recycle bin Restore all items from recycle bin - Open item(s) + Open {0, plural, one {item} other {items}} - Open item(s) with selected application + Open {0, plural, one {item} other {items}} with selected application Open parent folder of searched item @@ -2427,28 +2535,28 @@ Select all items - Invert item selection + Invert selected items - Clear item selection + Clear selected items Toggle item selection - Share selected file(s) with others + Share selected {0, plural, one {file} other {files}} with others - Pin item(s) to the Start Menu + Pin {0, plural, one {item} other {items}} to the Start Menu - Unpin item(s) from the Start Menu + Unpin {0, plural, one {item} other {items}} from the Start Menu - Pin folder(s) to Sidebar + Pin {0, plural, one {folder} other {folders}} to Sidebar - Unpin folder(s) from Sidebar + Unpin {0, plural, one {folder} other {folders}} from Sidebar Set selected picture as desktop background @@ -2462,14 +2570,23 @@ Set selected picture as the app background + + Install font + + + Install driver + + + Install certificate + - Install selected font(s) + Install selected {0, plural, one {font} other {fonts}} - Install driver(s) using selected inf file(s) + Install {0, plural, one {driver} other {drivers}} using selected inf {0, plural, one {file} other {files}} - Install selected certificate(s) + Install selected {0, plural, one {certificate} other {certificates}} Run selected application as administrator @@ -2484,28 +2601,28 @@ Launch preview in popup window - Create archive with selected item(s) + Create archive with selected {0, plural, one {item} other {items}} - Create 7z archive instantly with selected item(s) + Create 7z archive with selected {0, plural, one {item} other {items}} - Create zip archive instantly with selected item(s) + Create zip archive with selected {0, plural, one {item} other {items}} - Extract items from selected archive(s) to any folder + Extract selected {0, plural, one {archive} other {archives}} to any folder - Extract items from selected archive(s) to current folder + Extract selected {0, plural, one {archive} other {archives}} to the current folder - Extract items from selected archive(s) to new folder + Extract selected {0, plural, one {archive} other {archives}} to new folder - Rotate selected image(s) to the left + Rotate selected {0, plural, one {image} other {images}} to the left - Rotate selected image(s) to the right + Rotate selected {0, plural, one {image} other {images}} to the right Open settings page @@ -2520,19 +2637,19 @@ Decrease item size in the current view - Increase item size in the current view + បង្កើនទំហំធាតុក្នុងទម្រង់បច្ចុប្បន្ន - Switch to details view + ប្តូរទៅពត៌មាន​លម្អិត - - Switch to tiles view + + Switch to cards view - Switch to list view + ប្តូរទៅទម្រង់បញ្ជី - List + បញ្ជី ក្រឡាចត្រង្គ @@ -2544,7 +2661,7 @@ ប្តូរទៅទម្រង់ជួរឈរ - Switch views adaptively + ប្តូរទៅប្លង់​បត់បែន Sort items by name @@ -2628,13 +2745,13 @@ បើកទំព័រថេបថ្មី - Navigate backward in navigation history + Navigate backward - Navigate forward in navigation history + Navigate forward - Navigate up one directory + ត្រលប់ទៅមួយថតខាងលើ Duplicate current tab @@ -2660,8 +2777,11 @@ Close tabs other than selected tab + + បិទផ្ទាំងទាំងអស់រួមទាំងផ្ទាំងបច្ចុប្បន្ន + - Reopen last closed tab + Reopen recently closed tab Move to the previous tab @@ -2673,13 +2793,13 @@ Close current tab - បិទផ្ទាំងសកម្ម + Close the active pane Focus other pane - Switch focus to the non active pane + Switch focus to the other pane Toggle the sidebar @@ -2741,11 +2861,11 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Up + លើ Key name for hotkeys in menus. Use abbreviation if possible. - Home + ទំព័រដើម Key name for hotkeys in menus. Use abbreviation if possible. @@ -2773,7 +2893,7 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Clear + សម្អាត Key name for hotkeys in menus. Use abbreviation if possible. @@ -2781,7 +2901,7 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Help + ជំនួយ Key name for hotkeys in menus. Use abbreviation if possible. @@ -2881,28 +3001,28 @@ This PC - Recycle Bin + ធុងសម្រាម Untagged - Moves to the previous tab + Previous tab - Moves to the next tab + Next tab - Closes current tab + Close tab - Edit path + កែប្រែផ្លូវ - Redo + ធ្វើទៀត - Undo + មិនធ្វើវិញ Invalid location @@ -2923,7 +3043,7 @@ Show checkboxes when selecting items - Focus path bar + Edit path in the OmniBar Create new item @@ -2938,16 +3058,16 @@ Delete permanently - Delete item(s) permanently + Delete selected {0, plural, one {item} other {items}} permanently Play the selected media files - Undo the last file operation + មិនធ្វើប្រតិបត្តិការឯកសារចុងក្រោយ - Redo the last file operation + ធ្វើប្រតិបត្តិការឯកសារចុងក្រោយឡើងវិញ Location: @@ -2971,7 +3091,7 @@ Toggle grouping unit - Year + ឆ្នាំ Group by date unit @@ -3027,6 +3147,15 @@ You have uncommitted changes on this branch. What would you like to do with them? + + You have an ongoing merge with unresolved conflicts. Please resolve the conflicts or abort the merge to switch branches. + + + Abort merge and switch to '{0}' + + + Stay on '{0}' and resolve conflicts + Switch branch @@ -3055,14 +3184,20 @@ Switch to new branch - Create a folder with the currently selected item(s) + Create a folder with the currently selected {0, plural, one {item} other {items}} - Open properties + លក្ខណៈសម្បត្តិ + + + Open File Explorer properties Open properties window + + Open File Explorer properties window + Locals @@ -3081,6 +3216,9 @@ Run git fetch + + Clone a git repo + Run git pull @@ -3116,16 +3254,16 @@ ហ្គិត - Acrylic + អាគ្រីលីក Mica - Mica Alt + ឆ្លាស់មីកា - Backdrop Material + Backdrop Solid @@ -3157,17 +3295,17 @@ Files cannot access GitHub right now. - - Open folder in VS Code + + Open folder in {0} - - Open the current directory in Visual Studio Code + + Open the current directory in {0} - - Open repo in VS Code + + Open repo in {0} - - Open the root of the Git repo in Visual Studio Code + + Open the root of the Git repo in {0} Copy code @@ -3194,7 +3332,7 @@ Initialize repo - Initialize a Git repository + Initialize current folder as a git repository Remote Repository Name @@ -3209,13 +3347,13 @@ Sort items by path - Open directory in new pane + Open selected directory in a new pane - Open directory in new tab + Open selected directory in a new tab - Open directory in new window + Open selected directory in a new window Open all @@ -3240,10 +3378,10 @@ The '{0}' command is not ready to be executed. - Command palette + Command Palette - Open command palette + Open Command Palette in the OmniBar Start in: @@ -3293,7 +3431,7 @@ One of the custom color themes - Gray + ប្រផេស One of the custom color themes @@ -3349,7 +3487,7 @@ One of the custom color themes - Clear completed + សម្អាតទាំងស្រុង Name: @@ -3359,7 +3497,7 @@ Shown in a StatusCenter card. Used as "64 MB of 1 GB processed" - items + ធាតុ Files can't initialize this directory as a Git repository. @@ -3373,6 +3511,9 @@ Processing items... + + Discovering items... + Speed: @@ -3385,61 +3526,129 @@ Shown in a StatusCenter card. - Compressed {0} item(s) to "{1}" + Compressed {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Compressed {0} item(s) from "{1}" to "{2}" + Compressed {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error compressing {0} item(s) to "{1}" + Error compressing {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to compress {0} item(s) from "{1}" to "{2}" + Failed to compress {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Compressing {0} item(s) to "{1}" + Compressing {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Compressing {0} item(s) from "{1}" to "{2}" + Compressing {0, plural, one {# item} other {# items}} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Canceled cloning {0} to "{1}" + Shown in a StatusCenter card. + + + Canceled cloning {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Cloned "{0}" to "{1}" + Shown in a StatusCenter card. + + + Cloned {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Error cloning "{0}" to "{1}" + Shown in a StatusCenter card. + + + Failed to clone {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Cloning "{0}" to "{1}" + Shown in a StatusCenter card. + + + Cloning {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Canceled installing {0} fonts + Shown in a StatusCenter card. + + + Canceled installing {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Error installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Failed to install {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} from "{1}" Shown in a StatusCenter card. - Canceled copying {0} item(s) to "{1}" + Canceled copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Canceled copying {0} item(s) from "{1}" to "{2}" + Canceled copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Copied {0} item(s) to "{1}" + Copied {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Copied {0} item(s) from "{1}" to "{2}" + Copied {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error copying {0} item(s) to "{1}" + Error copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to copy {0} item(s) from "{1}" to "{2}" + Failed to copy {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Copying {0} item(s) to "{1}" + Copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Copying {0} item(s) from "{1}" to "{2}" + Copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. + + Discovered {0, plural, one {# item} other {# items}} + Shown in a StatusCenter card during file discovery phase. + Canceled extracting "{0}" to "{1}" Shown in a StatusCenter card. @@ -3473,55 +3682,55 @@ Shown in a StatusCenter card. - Canceled deleting {0} item(s) from "{1}" + Canceled deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Deleted {0} item(s) from "{1}" + Deleted {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Error deleting {0} item(s) from "{1}" + Error deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Failed to delete {0} item(s) from "{1}" + Failed to delete {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Deleting {0} item(s) from "{1}" + Deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Canceled moving {0} item(s) to "{1}" + Canceled moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Canceled moving {0} item(s) from "{1}" to "{2}" + Canceled moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Moved {0} item(s) to "{1}" + Moved {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Moved {0} item(s) from "{1}" to "{2}" + Moved {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Moving {0} item(s) to "{1}" + Moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Moving {0} item(s) from "{1}" to "{2}" + Moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error moving {0} item(s) to "{1}" + Error moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to move {0} item(s) from "{1}" to "{2}" + Failed to move {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. @@ -3549,14 +3758,14 @@ Shown in a StatusCenter card. - {0}/{1} item(s) processed + {0}/{1, plural, one {# item} other {# items}} processed Shown in a StatusCenter card. Used as "8/20 items processed" Unblock downloaded file - No ongoing file operations + មិនមានប្រតិបត្តិការឯកសារដែលកំពុងដំណើរការទេ The option to open Files on Windows Startup is not available due to your system settings or group policy. To re-enable, open the startup page in Task Manager. @@ -3567,6 +3776,9 @@ Failed to set the background wallpaper + + Failed to open the settings file + Delete Git branch @@ -3592,31 +3804,31 @@ There was an error applying this tag - Extract items from selected archive(s) to current folder for single-item archive, or to new folder for multi-item archive + Extract selected {0, plural, one {archive} other {archives}} here for single-item or to new folder for multi-item Extract here (Smart) - Sort files and folders together + តម្រៀបឯកសារនិងថតឯកសារជាមួយគ្នា - Sort files and folders together + Sort files and folders in the same list - Sort files first + តម្រៀបឯកសារជាមុន Sort files first then folders - Sort folders first + តម្រៀបថតឯកសារជាមុន Sort folders first then files - Sort priority + តម្រៀបអាទិភាព Failed to rotate the image @@ -3637,7 +3849,7 @@ បើកផ្ទាំងថ្មី - Change album cover + ប្តូរគម្របអាល់ប៊ុម Failed to rename item @@ -3657,19 +3869,16 @@ សំណួរ​និងពិភាក្សា - - Additional sizes are not yet available for the Tiles View. - Compact Used to describe layout sizes - Small + តូច Used to describe layout sizes - Medium + មធ្យម Used to describe layout sizes @@ -3693,7 +3902,7 @@ Used to describe layout sizes - Large + ធំ Used to describe layout sizes @@ -3731,7 +3940,7 @@ Restore defaults - Choose an action + ជ្រើសរើសសកម្មភាព Are you sure you want to restore the default key bindings? This action cannot be undone. @@ -3749,14 +3958,14 @@ Adaptive layout is not available when preferences are synced, the default layout has been changed to Details. - Background image + រូបភាពផ្ទៃខាងក្រោយ - Bottom + ក្រោម Image alignment type - Center + កណ្តាល Image alignment type @@ -3764,24 +3973,24 @@ Image stretch type - Horizontal alignment + ​តម្រឹម​ផ្តេក - Image fit + សម្របរូបភាព - Left + ឆ្វេង Image alignment type - Opacity + កម្រិតបង្ហាញ - Right + ស្តាំ Image alignment type - Top + លើ Image alignment type @@ -3793,7 +4002,7 @@ Image stretch type - Vertical alignment + តម្រឹម​បញ្ឈរ Thin Acrylic @@ -3821,6 +4030,10 @@ ICO File This is the friendly name for ICO files. + + ICL File + This is the friendly name for ICL files. + Zip File This is the friendly name for ZIP files. @@ -3829,6 +4042,10 @@ Bitmap Files This is the friendly name for bitmap files. + + Image Files + This is the friendly name for image files. + Num @@ -3854,23 +4071,23 @@ Status: - Show Toolbar - Setting that controls if the Toolbar is shown in the main view + Show toolbar + Setting that controls if the toolbar is shown in the main view Tab actions menu - - Add vertical pane + + ពុះបញ្ឈរ - Add a vertical pane + Split pane vertically - - Add horizontal pane + + ពុះផ្ដេក - - Add a horizontal pane + + Split pane horizontally Arrange vertically @@ -3884,8 +4101,8 @@ Arrange panes horizontally - - Add pane + + ពុះបន្ទះផ្ទាំង Show tab actions button in the title bar @@ -3893,8 +4110,11 @@ Arrange panes - - Default pane arrangement + + Default dual pane split direction + + + Dual pane mode Horizontal @@ -3902,4 +4122,259 @@ Vertical + + Show Files icon in the System Tray + + + CPU threads + + + Navigate to the home page + + + Toolbars + + + User ID + + + Bulk rename + + + Compress contents + + + Show option to create alternate data stream + + + Create alternate data stream + + + Create alternate data stream for the selected {0, plural, one {item} other {items}} + + + Enter data stream name + + + There was an error creating the alternate data stream + + + Please note that alternate data streams only work on drives formatted as NTFS. + + + Alternate data streams are currently hidden + + + Would you like to display alternate data streams? You can modify this setting anytime from the files and folders settings page. + + + Manage tags + + + Available + + + Total + + + Always switch focus to newly created tab + + + Toggle shelf pane + + + Toggle the visibility of the shelf pane + + + Show shelf pane toggle in address bar + + + Shelf + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + Clear items + + + Remove from shelf + + + Add to shelf + Tooltip that displays when dragging items to the Shelf Pane + + + Enter a hash to compare + Placeholder that appears in the compare hash text box + + + Matches {0} + Appears when two compared hashes match, e.g. "Matches SHA256" + + + No matches found + Appears when two compared hashes don't match + + + Path or alias + + + Invalid path + + + Test integration + + + {0} could not be located. Please check your settings and try again. + + + The configured IDE could not be located + + + Open settings + + + Visual Studio Code + + + Enter a path or launch alias + + + Please enter a name for the IDE + + + Clone repo + Clone repo dialog title + + + Clone + Primary action button in the clone repo dialog + + + Repository URL + URL textbox header in the clone repo dialog + + + Cannot clone repo + Cannot clone repo dialog title + + + Compare a file + Button that appears in file hash properties that allows the user to compare two files + + + Size format + + + Binary + + + Decimal + + + You can add sections to the sidebar by right-clicking and selecting the sections you want to add + + + Drag files or folders here to interact with them across different tabs + + + Enter a path to navigate to... + + + Find features and commands... + + + Search for files and folders... + + + During file operations + + + Show status center button + + + Status center progress ring + Screen reader name for the status center progress ring + + + Icon files + This is the friendly name for a variety of different icon files. + + + Show collapsed path breadcrumbs + + + Show folders in {0} + + + Show folders in Home + + + There are no commands containing {0} + + + See more + + + Filtering for + + + Filename + + + Signatures + + + Signature list + + + Issued by: + + + Issued to: + + + Valid from: + + + Valid to: + + + No signature was found. + + + Show option to open folders in Windows Terminal + + + Open log file + + + Open log file in your default editor + + + Open log file location in your default file manager + + + Unable to open the log file + + + Show status bar + + + Only in Columns View + + + New {0} + + + Enable smooth scrolling + + + Scrolling + + + Show option to Pin to Sidebar + + + Show option to Pin to the Start Menu + \ No newline at end of file diff --git a/src/Files.App/Strings/ko-KR/Resources.resw b/src/Files.App/Strings/ko-KR/Resources.resw index aaab6f8de01f..fec7cd1b1990 100644 --- a/src/Files.App/Strings/ko-KR/Resources.resw +++ b/src/Files.App/Strings/ko-KR/Resources.resw @@ -123,8 +123,14 @@ 경로 복사 + + 항목 위치 복사 + - 홑 따옴표를 포함하여 경로 복사 + 따옴표로 경로 복사 + + + 선택한 항목 경로를 따옴표로 복사 찾아보기 @@ -142,7 +148,7 @@ 크기: - 디스크 할당 크기 + 디스크 할당 크기: 압축 해제 후 크기: @@ -154,7 +160,7 @@ 대상 폴더 - 은(는) 원본 폴더의 하위 폴더입니다 + 은 소스 폴더의 하위 폴더입니다 넘어가기 @@ -178,7 +184,7 @@ 모든 항목 비우기 - 탐색할 경로를 입력하거나 ">"를 입력하여 명령 팔레트 열기 + 탐색할 경로를 입력하거나 “>” 입력하여 명령 팔레트를 엽니다 검색 @@ -190,13 +196,13 @@ 이 항목 삭제 - GitHub 리포지토리 열기 + GitHub 저장소 정보 - 오픈소스 + 오픈 소스 날짜 형식 @@ -244,10 +250,13 @@ 이름 - 오름차순 + 상승하는 - 붙여넣기 + 붙여 넣기 + + + 바로 가기 붙여 넣기 새로 만들기 @@ -283,7 +292,7 @@ 권한 부여 - 시작하기 위해서는 파일 표시 권한이 필요합니다. 권한을 얻기 위한 설정 페이지가 열리고, 권한 부여 후 앱을 재시작 하면 됩니다. + 시작하려면 먼저 파일을 표시할 수 있는 권한을 부여해야 합니다. 그러면 이 권한을 부여할 수 있는 설정 페이지가 열립니다. 이 단계를 완료한 후 Files을 다시 열어야 합니다. 표시 @@ -291,8 +300,8 @@ 항목 이름을 입력하세요 - - 이름 설정 + + 새로운 {0} 만들기 밝게 @@ -300,8 +309,11 @@ 어둡게 + + 시스템 설정 사용 + - + 프로그램 시스템 @@ -340,43 +352,43 @@ 잠금 화면 배경으로 설정 - 앱 배경으로 설정 + 프로그램 배경 화면을 이것으로 설정 항목을 삭제할 수 없습니다. - 파일에 접근하려면 드라이브를 삽입하세요. + 이 항목에 액세스하려면 필요한 드라이브를 삽입하세요. 드라이브 분리됨 - 접근하려는 파일이 이동되거나 삭제되었을 수 있습니다. + 액세스하려는 파일이 이동 및 삭제되었을 수 있습니다. 이 파일의 속성을 열 수 없습니다. - 접근하려는 파일이 이동되거나 삭제되었을 수 있습니다. + 액세스하려는 파일이 이동 및 삭제되었을 수 있습니다. 파일을 찾을 수 없음 - 접근하려는 폴더가 이동되거나 삭제되었을 수 있습니다. + 액세스하려는 폴더가 이동 및 삭제되었을 수 있습니다. - 폴더를 삭제하셨나요? + 이 폴더를 삭제하셨습니까? - 접근하려는 파일이 현재 {0}에서 사용 중입니다 + 액세스하려는 파일은 현재 {0}에서 사용 중입니다 - 접근하려는 파일이 현재 다른 앱에서 사용 중입니다 + 액세스하려는 파일이 현재 다른 애플리케이션에서 사용 중입니다 - 사용 중인 파일 + 파일이 사용 중입니다 레이아웃 @@ -391,7 +403,7 @@ 1일 전 - {0}일 전 + {0} 일 전 {0, plural, other {#시간 전}} @@ -400,7 +412,7 @@ 1시간 전 - {0}시간 전 + {0} 시간 전 {0, plural, other {#분 전}} @@ -409,7 +421,7 @@ 1분 전 - {0}분 전 + {0} 분 전 {0, plural, other {#초 전}} @@ -418,13 +430,13 @@ 1초 전 - {0}초 전 + {0} 초 전 지금 - 해당 기능은 지원되지 않습니다. + 해당 기능은 지원되지 않습니다 {1} 중 {0} 사용 가능 @@ -433,17 +445,17 @@ 보내기 - 파일명에는 다음 문자들을 사용할 수 없습니다: \ / : * ? " < > | + 항목 이름에는 다음 문자가 포함할 수 없습니다. : \ / : * ? “ < > | 지정한 항목 이름이 잘못되었습니다 - {0, plural, other {# items selected}} + {0, plural, other {#개 항목 선택함}} ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural - {0, plural, other {items}} + {0, plural, other {항목}} ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural @@ -456,7 +468,7 @@ 휴지통 비우기 - 바이트 + bytes KB @@ -492,7 +504,7 @@ 다른 종류 - {0}의 모든 항목 + {0} 의 모든 항목 사용 중인 공간: @@ -515,9 +527,6 @@ 상태 - - Windows 기본값 - 결과 없음 @@ -525,10 +534,13 @@ 표시할 항목에 접근할 수 없음 - {0}(으)로 이동 + {0} 위치에 옮김 - {0}(으)로 복사 + {0} 위치에 복사 + + + {0} 위치에 복제 바로 가기 만들기 @@ -561,7 +573,7 @@ {0} - 바로 가기 - 개발자가 아직 대비하지 못한 문제가 발생했습니다. + 파일 프로그램에 개발자가 미처 준비하지 못한 문제가 발생했습니다. 문제가 발생했습니다! @@ -603,31 +615,31 @@ 지정한 이름이 유효하지 않습니다. 다시 시도해 주세요. - 유효하지 않은 항목명 + 잘못된 항목 이름 - 지정한 이름 길이가 최대 제한을 초과했습니다. 다시 시도해 주세요. + 지정한 이름 길이가 최대 제한을 초과했습니다. 다시 시도해 주십시오. - 이름이 너무 김 + 항목 이름이 너무 깁니다 추가 옵션 표시 - 설정을 적용하려면 앱을 다시 시작해야합니다. 앱을 다시 시작할까요? + 이 설정을 적용하려면 프로그램을 다시 시작해야 합니다. 이 프로그램을 다시 시작해도 괜찮습니까? - GitHub에서 후원하기 + 후원하기 항목을 만들 수 없습니다 - 접근이 거부되었습니다. + 액세스가 거부되었습니다 - 배경으로 설정 + 다음으로 설정 취소 @@ -642,22 +654,22 @@ 파일 - 빈 파일을 생성합니다 + 빈 파일을 만듭니다 폴더 - 빈 폴더를 생성합니다 + 빈 폴더를 만듭니다 - 경로 새로고침 + 경로 새로 고침 언어 - 셸 확장을 '추가 옵션 표시'로 이동 + 인터페이스 확장을 하위 메뉴로 이동 항목을 삭제할 때 한 번 더 확인 @@ -717,7 +729,7 @@ 색 영역 - sRGB + s rgb 지정되지 않음 @@ -747,7 +759,7 @@ 초점 거리 - 조리개 + 렌즈를 통해 들어오는 빛의 양을 조절하는 장치 사람들 이름 @@ -759,19 +771,19 @@ 포맷 - 샘플 레이트 + 샘플 속도 앨범 아티스트 - 앨범명 + 앨범 이름 아티스트 - BPM + Bpm 작곡가 @@ -798,7 +810,7 @@ 보호 유형 - 저자 URL + 작성자 링크 콘텐츠 배포자 @@ -807,7 +819,7 @@ 발매일 - 시리즈명 + 시리즈 이름 시즌 번호 @@ -819,28 +831,28 @@ 제작자 - 홍보 URL + 프로모션 링크 제공 방식 - 퍼블리셔 + 발행자 - 대형 썸네일 경로 + 큰 표지 경로 - 대형 썸네일 URI + 대형 표지 링크 - 소형 썸네일 경로 + 소형 표지 링크 - 소형 썸네일 URI + 소형 표지 링크 - 사용자 웹 URL + 사용자 웹 링크 작성자 @@ -867,7 +879,7 @@ 총 편집 시간 - 템플릿 + 서식 단어 수 @@ -888,16 +900,16 @@ 슬라이드 수 - 프레임레이트 + 프레임 옮김 확률 - 비트레이트 + 비트 옮김 확률 - 오디오 비트레이트 + 오디오 비트 옮김 확률 - 비디오 비트레이트 + 비디오 비트 옮김 확률 압축 @@ -996,7 +1008,10 @@ 작업 현황 - {0}에 대한 {1}의 검색 결과 + {0} 에 대한 {1} 의 검색 결과 + + + `{0}`에 대한 검색 결과 자세히 @@ -1008,10 +1023,10 @@ 보호된 시스템 파일 표시 - 디렉토리 간 레이아웃 및 정렬 설정 동기화 + 위치 레이아웃 및 정렬 환경 설정 동기화 - 마우스 우클릭 메뉴 설정 + 마우스 오른쪽 버튼 옵션 설정 원본 경로 @@ -1052,14 +1067,11 @@ 자세히 (Ctrl+Shift+1) - - 타일 (Ctrl+Shift+2) - 삭제된 날짜 - 클라우드 드라이브 + 인터넷 서버 드라이브 확인 @@ -1068,31 +1080,46 @@ 원하는 이름 - 미리보기 화면 + 미리 보기 패널 보기 - 세부정보 보기 + 자세한 정보 보기 - 기본 파일 속성을 보려면 세부정보 보기로 전환하세요. + 기본 파일 속성을 보려면 자세한 정보 보기로 전환하세요 정보 보기 - 세부정보/미리보기 패널을 보려면 정보 보기로 전환하세요 + 자세히/미리보기 창 표시 - 도구 모음 전환 + 툴바 사용 + + + 툴바 표시 + + + 필터 헤더 사용 + + + 필터 헤더 표시 + + + 듀얼 창 사용 + + + 듀얼 창 모드 - 가능한 미리보기가 없습니다 + 확인 가능한 미리 보기가 없습니다 - 이름 + 항목 이름 - 사이드바에서 제거 + 양옆 선반에서 제거 네트워크 @@ -1101,10 +1128,10 @@ 파일의 자세한 정보 - 파일 미리보기 + 파일 미리 보기 - 선택한 파일 미리보기 패널 + 선택한 파일 미리 보기 패널 피드백 보내기 @@ -1140,19 +1167,19 @@ CD-ROM 드라이브 - 클라우드 드라이브 + 인터넷 서버 드라이브 하드 디스크 드라이브 - 플로피 디스크 드라이브 + 자기 저장 매체 드라이브 네트워크 드라이브 - 마운트 해제된 드라이브 + 경로 연결 해제된 드라이브 램 디스크 드라이브 @@ -1191,7 +1218,7 @@ 고급 실행 인수: - 기본값 + 기본 값 항목 수 @@ -1224,7 +1251,7 @@ 위치 없음 - 입력란은 비워 둘 수 없습니다! + 입력 위치는 비워 둘 수 없습니다! 이름에는 다음 기호가 포함될 수 없습니다: \ / : * ? " < > | @@ -1238,6 +1265,12 @@ 저장소 센스 열기 + + Windows 설정에서 Storage Sense 페이지를 엽니다 + + + 정리하기 + 복사 @@ -1251,7 +1284,7 @@ 이동 - {0, plural, other {항목 #개가 이동됩니다}} + {0, plural, other {항목 #개가 옮김}} {0, plural, other {항목}} 이동 @@ -1272,13 +1305,13 @@ {0, plural, other {파일 이름}} 중복 - {0, plural, other {항목 #개가 복사됩니다}} + {0, plural, other {항목 #개가 복사}} - 영구 삭제 + 영구적 삭제 - ISO 감도 + ISO 속도 기존 항목 대체 @@ -1380,7 +1413,7 @@ 항목 {0}개 - 닫은 탭 다시 열기 + 최근 탭 다시 열기 이름 바꾸기 @@ -1398,7 +1431,7 @@ 제거 - 위젯 + 미니 화면 동기화 상태 @@ -1419,13 +1452,13 @@ 모든 권한 - 경로 내 내용 나열 + 특정 권한 수정하기 - {0}의 권한 + {0} 의 권한 읽기 및 실행 @@ -1452,13 +1485,13 @@ 알 수 없는 소유자 - 압축풀기 + 압축 풀기 완료 후 압축을 해제한 폴더 표시 - {0}에 압축 해제 + {0} 에 압축 해제 새 라이브러리 생성 @@ -1539,7 +1572,7 @@ 파일 생성 - 하위 디렉토리 및 파일 삭제 + 하위 경로 및 파일 삭제 실행 파일 @@ -1587,13 +1620,13 @@ 모든 하위 객체의 사용 권한 항목을 이 객체의 상속 가능한 사용 권한 항목으로 변경 - 활성 패널 닫기 + 창 닫기 - 컴팩트 오버레이 시작 + 작은 오버레이 모드 시작 - 컴팩트 오버레이 종료 + 화면 크기 자동 조절 기능 종료 파일 설명 @@ -1611,10 +1644,10 @@ 파일 버전 - 전체 미리보기 불러오기 + 파일 미리 보기 - 클라우드에서 항목을 내려받고 미리보기를 불러옵니다. + 클라우드에서 항목을 내려받고 미리보기를 불러옵니다 {0, plural, other {항목 #}} ({1, plural, other {파일 #}}, {2, plural, other {폴더 #}}) @@ -1626,7 +1659,7 @@ WSL - {0} 섹션 숨기기 + {0} 섹 션 숨기기 파일 추가 @@ -1646,15 +1679,6 @@ - - Ctrl+Shift+1 - - - Ctrl+Shift+2 - - - Ctrl+Shift+6 - 압축 해제가 성공적으로 완료되었습니다. @@ -1680,7 +1704,7 @@ 드라이브 열기 - 드라이브 {0}에 디스크를 삽입하세요. + 드라이브 {0} 에 디스크를 삽입하세요 디스크 삽입 @@ -1698,7 +1722,7 @@ 신원 증명: - 사용자명 + 사용자 명 항목 없음 @@ -1712,8 +1736,8 @@ - - 타일 + + 카드 새 탭에서 폴더 열기 @@ -1749,7 +1773,7 @@ 파일 태그 - 한 번 클릭하여 항목 열기 + 한 번 클릭하여 파일 열기 폴더 경로 @@ -1770,7 +1794,7 @@ 휴지통 비우기 - 사이드바 + 측면 바 사용자 지정 폴더 아이콘 선택 @@ -1779,13 +1803,13 @@ 사용자 지정 - 기본값 복원 + 기본 설정 복원 - 다른 앱에서 Files 열 때 이미 있던 창에서 탭 열기 + 다른 프로그램에서 Files을 열 때 기존 창에서 열기 - 프로그램 시작 시 + 시작 설정 호환성 @@ -1826,6 +1850,18 @@ 다시 시작을 위해 이 프로그램을 등록 + + 창 실행 + + + 일반 창 실행 + + + 최소화 + + + 최대화 + 관리자 권한으로 실행 @@ -1839,7 +1875,7 @@ DPI 조정하지 않기 - DPI를 재정의하지 않음 + DPI 설정을 변경하지 마십시오 호환성 모드 @@ -1851,7 +1887,10 @@ 제3자 라이브러리 - 이 설정은 시스템 레지스트리를 수정하며 예기치 않은 부작용을 낳을 수 있습니다. 계속 진행할 경우 책임은 사용자에게 있습니다. + 이 옵션은 시스템 레지스트리를 수정하기 때문에 시스템에 어떠한 손상이 발생할 수 있습니다. 계속 진행할 경우 발생할 수도 있는 손상에 대한 책임은 사용자에게 있음을 확인 후 진행하십시오. + + + 평면 작업은 영구적이며 권장하지 않습니다. 본인 책임 하에 계속 진행하세요. 라이브러리 생성 @@ -1866,7 +1905,7 @@ 최근 항목 - Files를 Windows 시작 앱으로 등록 + Files을 Windows 시작 프로그램으로 지정 특성 @@ -1902,16 +1941,16 @@ Ctrl+E - {0}에 압축 해제 + {0} 에 압축 해제 스크립트 실행 - PowerShell로 실행 + PowerShell 으로 실행 - 폴더 크기 계산은 리소스를 많이 사용하므로 CPU 사용량이 증가할 수 있습니다. + 폴더 크기를 계산하는 것은 많은 것를 사용하므로 CPU 사용량이 증가할 수 있습니다. 왼쪽으로 회전 @@ -1931,6 +1970,9 @@ 다른 탭 닫기 + + 모든 탭 닫기 + 음악 @@ -1944,13 +1986,13 @@ 태그 - 유니버설 + 일반적인 - 예시: {0}, {1} + 예: {0}, {1} - 이미지 썸네일 표시 + 이미지 표지 표시 다시 시도 @@ -1965,16 +2007,16 @@ 사용자 지정 - 바탕화면의 슬라이드 쇼로 지정하기 + 바탕 화면 슬라이드 쇼로 설정 모든 열의 크기 맞추기 - 적응형 보기 + 적응 레이아웃 - 적응형 (Ctrl+Shift+7) + 적응 레이아웃(Ctrl+Shift+7) Ctrl+Shift+7 @@ -1985,11 +2027,23 @@ 동작 - - 파일 검토 + + 안녕하세요! + + + Files를 유용하게 사용하고 계신다면 Microsoft Store에 리뷰를 남겨 주세요. + + + Files를 유용하게 사용하고 계신다면 GitHub에서 프로젝트를 후원해 주세요. + + + 후원 - - 파일을 검토하시겠어요? + + 리뷰하기 + + + 닫기 배경으로 설정 @@ -1998,7 +2052,7 @@ 바탕 화면 배경으로 설정 - 기본값으로 설정 + 기본으로 지정 날짜 열 @@ -2016,10 +2070,10 @@ 유형 열 - 잠금화면 + 잠금 화면 - - 열 레이아웃에서 한 번 클릭해 폴더 열기 + + 한 번 클릭하여 폴더 열기 항목 여는 중 @@ -2027,6 +2081,21 @@ 압축 + + 하위 폴더의 모든 콘텐츠가 선택한 위치로 이동합니다 + + + 폴더 평면 설정 + + + 평면 + + + 폴더를 평면으로 하면 해당 하위 폴더의 모든 콘텐츠가 선택한 위치로 이동합니다. 이 작업은 영구적이며 실행 취소할 수 없습니다. 이 실험적 기능을 사용함으로써 귀하는 위험을 감수하고 데이터 손실이 발생한 경우에도 Files 팀에게는 어떠한 책임이 없음을 동의하므로 간주합니다. + + + 평면 옵션 표시 + 파일과 폴더에 마우스를 올려 선택하기 @@ -2037,7 +2106,7 @@ 모든 항목 복원하기 - 선택된 {0}개의 항목을 복원하시겠습니까? + {0, plural, other {{0} 개 항목}}을 복구합니까? 선택 목록 복원하기 @@ -2049,10 +2118,10 @@ 복원 - 바로가기를 열 수 없음 + 바로 가기를 열 수 없음 - 이 항목의 원본 {0}을 찾을 수 없습니다. 이 바로가기를 삭제하시겠습니까? + 이 항목의 원본 {0} 찾을 수 없습니다. 이 바로 가기를 삭제하시겠습니까? 이 폴더에 접근할 수 있는 권한이 없습니다. @@ -2060,6 +2129,12 @@ 압축 파일 비밀번호 + + 인코딩 + + + {0} (확인됨) + 경로 @@ -2102,9 +2177,24 @@ 분할 크기 - + 분할하지 않음 + + Auto + + + Dictionary size + + + Word size + + + Estimated memory usage: {0} + + + Available memory: {0} + CD @@ -2115,7 +2205,7 @@ FAT - 블루레이 + 블루 레이 암호화 @@ -2145,7 +2235,7 @@ 바로 가기 만들기 - 로컬 또는 네트워크상에 있는 프로그램, 파일, 폴더, 컴퓨터, 인터넷 주소로 연결되는 바로 가기를 만들 수 있습니다. + 로컬 또는 네트워크 프로그램, 파일, 폴더, 컴퓨터 또는 인터넷 주소에 대한 바로 가기를 만듭니다. 항목 위치 입력: @@ -2159,14 +2249,20 @@ Files 설정 파일 수정 - - 새로운 기능 + + 설정 파일을 기본 편집기에서 엽니다 + + + 릴리즈 노트 + + + 릴리즈 노트 열기 이 위치에 바로 가기를 만들려면 관리자 권한이 필요합니다 - 대신 바탕 화면에 바로가기를 만드시겠습니까? + 대신 바탕 화면에 바로 가기를 만드시겠습니까? 지정된 항목의 이름이 잘못되었습니다 @@ -2175,7 +2271,7 @@ 설정 - 빈 공간을 두 번 클릭해 상위 디렉토리로 이동 + 빈 공간을 더블 클릭하여 한 디렉터리 위로 이동 더 보기 @@ -2199,7 +2295,7 @@ 바로 가기 - {0} 로 연결 할 자격 증명을 입력하세요 + {0} 연결할 자격 증명을 입력하세요 네트워크 자격 증명을 입력하세요 @@ -2223,7 +2319,7 @@ 완전히 삭제할 때만 - 안함 + 하지 않음 태그 색상 @@ -2241,10 +2337,7 @@ 컨텍스트 메뉴 옵션 - 파일 확장자 표시 - - - 숨긴 항목 표시 + 파일 확장 프로그램 포맷 @@ -2253,13 +2346,13 @@ 도움말 - 전체 화면 전환 + 전체화면 정말 이 태그를 삭제 하시겠습니까? - 모두 재생 + 재생 높이 @@ -2283,16 +2376,16 @@ 다중 선택 - 사이드바 항목 재정렬 + 측면 바 항목 다시 정렬 - 해시 + 해시 진행 계산 중 오류가 발생했습니다 - 해시를 계산하지 못했습니다. 파일을 닫고 다시 시도해주십시오. + 해시 계산에 실패했습니다. 파일을 닫고 다시 시도하십시오. 온라인 파일인 경우 해시값을 계산할 수 없습니다. @@ -2319,7 +2412,7 @@ 크기 확대 - 정렬 방향 전환 + 정렬 방향 잘못된 이름 @@ -2331,34 +2424,37 @@ 동영상 - 미리 보기 팝업 표시 + 미리보기 팝업 컴팩트 오버레이 전환 - 온라인 지원 페이지 열기 + 브라우저에서 지원 페이지 열기 전체 화면 전환 - 컴팩트 오버레이를 시작합니다 + 컴팩트 오버레이 채우기 - 컴팩트 오버레이를 종료합니다 + 컴팩트 오버레이 모드 종료 - 컴팩트 오버레이를 전환합니다 + 컴팩트 오버레이 모드 - 검색으로 이동 + 경로 막대에서 검색 - 숨겨진 항목 표시 여부를 전환합니다 + 숨겨진 항목 표시 + + + 온점(.)으로 시작하는 파일 표시 켜기/끄기 - 파일 확장자 표시 여부를 전환합니다 + 파일 확장자 표시 켜기/끄기 파일을 미리 보려면 미리보기 패널로 전환하세요 @@ -2367,31 +2463,43 @@ 사이드바 표시 전환 - 클립보드에 복사 + 선택한 {0, plural, other { 개 항목}} 복사 - 선택된 요소들의 경로를 클립보드에 복사하기 + 현재 경로 복사 + + + 선택한 항목의 위치 복사 + + + 선택한 항목의 위치를 따옴표를 붙여 복사 - 선택된 요소들의 경로를 홑따옴표를 포함하여 클립보드에 복사하기 + 현재 경로를 따옴표를 붙여 복사 - 클립보드에 잘라내기 + 선택한 {0, plural, other { 개 항목}} 잘라내기 - 클립보드에서 현재 폴더에 붙여넣기 + 현재 폴더에 붙여넣기 + + + 현재 폴더에 바로가기로 붙여넣기 - 클립보드에서 선택된 폴더에 붙여넣기 + 선택한 폴더에 붙여넣기 + + + 선택한 폴더에 붙여넣기 - 항목 삭제 + 선택한 항목 삭제 {0, plural, one {item} other {items}} 새 폴더 만들기 - 선택한 항목에 대한 새 바로 가기 만들기 + 선택한 {0, plural, other { 개 항목}}에 {0, plural, other { 개 항목의 바로가기}} 새로 만들기 새 바로 가기 만들기 @@ -2403,16 +2511,16 @@ 선택한 드라이브 포맷 - 복원 + 휴지통에서 {0, plural, other { 개 항목}} 복구 모든 항목 복원 - 열기 + {0, plural, other { 개 항목}} 열기 - 선택한 앱으로 항목 열기 + 선택한 프로그램으로 {0, plural, other { 개 항목}} 열기 검색된 항목의 상위 폴더 열기 @@ -2427,28 +2535,28 @@ 모든 항목을 선택합니다 - 선택 영역 반전 + 선택한 항목 반전 - 선택 취소 + 선택한 항목 삭제 항목 선택 여부를 전환합니다 - 공유 + 선택한 {0, plural, other { 개 파일}} 공유 - 시작 메뉴에 고정 + 시작 화면에 {0, plural, other { 개 항목}} 고정 - 시작 메뉴에서 제거 + 시작 메뉴에서 {0, plural, other { 개 항목}} 고정 해제 - 사이드바에 폴더 고정 + 사이드바에 {0, plural, other { 개 항목}} 고정 - 사이드바에서 폴더 제거 + 사이드바에서 {0, plural, other { 개 항목}} 고정 해제 선택한 사진을 바탕 화면 배경으로 설정 @@ -2462,14 +2570,23 @@ 선택한 사진을 앱의 배경으로 설정 + + 글꼴 설치 + + + 드라이버 설치 + + + 인증서 설치 + - 선택한 폰트 설치 + 선택한 {0, plural, other { 개 폰트}} 설치 - 설치 정보를 이용해 드라이버 설치 + 선택한 {0, plural, other { 개 INF 파일}}로 {0, plural, other { 개 드라이버}} 설치 - 인증서 설치 + 선택한 {0, plural, other { 개 인증서}} 설치 선택한 앱을 관리자 권한으로 실행 @@ -2484,28 +2601,28 @@ 팝업 창에서 미리 보기 - 선택한 항목으로 아카이브 생성 + 선택한 {0, plural, other { 개 항목}} 압축 - 선택한 항목으로 7z 아카이브 생성 + 선택한 {0, plural, other { 개 항목}}으로 7z 압축 파일 만들기 - 선택한 항목으로 zip 아카이브 생성 + 선택한 {0, plural, other { 개 항목}}으로 zip 압축 파일 만들기 - 선택한 아카이브를 임의의 폴더에 압축 해제 + 선택한 {0, plural, other { 개 압축 파일}} 임의의 폴더에 압축 해제 - 선택한 아카이브를 현재 폴더에 압축 해제 + 선택한 {0, plural, other { 개 압축 파일}} 현재 폴더에 압축 해제 - 선택한 아카이브를 새 폴더에 압축 해제 + 선택한 {0, plural, other { 개 압축 파일}} 새 폴더에 압축 해제 - 이미지를 왼쪽으로 회전 + 선택한 {0, plural, other { 개 사진}} 왼쪽으로 회전 - 이미지를 오른쪽으로 회전 + 선택한 {0, plural, other { 개 사진}} 오른쪽으로 회전 설정 열기 @@ -2525,8 +2642,8 @@ 자세히 보기로 전환 - - 타일 보기로 전환 + + 카드 보기로 전환 목록 보기로 전환 @@ -2535,16 +2652,16 @@ 목록 - 그리드 + 격자 - 그리드 보기로 전환 + 격자 보기로 전환 열 보기로 전환 - 적응형 보기로 전환 + 적응 보기 전환 이름으로 정렬 @@ -2628,13 +2745,13 @@ 새 탭 열기 - 탐색 기록에서 뒤로 이동 + 뒤로 가기 - 탐색 기록에서 앞으로 이동 + 앞으로 가기 - 상위 디렉토리로 이동 + 상위 경로로 이동 현재 탭 복제 @@ -2660,6 +2777,9 @@ 선택한 탭을 제외한 모든 탭 닫기 + + 현재 탭을 포함한 모든탭을 닫습니다 + 마지막으로 닫은 탭 다시 열기 @@ -2673,16 +2793,16 @@ 현재 탭 닫기 - 활성 패널 닫기 + 현재 창 닫기 - Focus other pane + 다른 패널에 포커스 - Switch focus to the non active pane + 다른 창에 집중하기 - 사이드 바 전환 + 측면 바 전환 Alt @@ -2697,7 +2817,7 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Win + Win 키 Key name for hotkeys in menus. Use abbreviation if possible. @@ -2765,7 +2885,7 @@ Key name for hotkeys in menus. Use abbreviation if possible. - 일시정지 + 일시 정지 Key name for hotkeys in menus. Use abbreviation if possible. @@ -2887,13 +3007,13 @@ 태그 지정 안함 - 이전 탭으로 이동 + 이전 탭 - 다음 탭으로 이동 + 다음 탭 - 현재 탭 닫기 + 탭 닫기 경로 수정 @@ -2923,7 +3043,7 @@ 항목 확인란 표시 - 주소 표시줄 포커스 + 경로 막대 편집 새 항목 만들기 @@ -2938,7 +3058,7 @@ 영구적으로 삭제 - 항목을 영구적으로 삭제 + 선택한 {0, plural, other { 개 항목}} 영구 삭제 선택한 미디어 파일 재생하기 @@ -3027,6 +3147,15 @@ 이 분기에서 변경 사항을 커밋하지 않았습니다. 어떻게 하시겠습니까? + + 진행 중인 merge에 미해결된 conflict가 있습니다. conflict를 해결하거나 merge를 취소하여 브랜치를 변경하세요. + + + merge 취소하고 '{0}'으로 변경하기 + + + '{0}'에 잔류하고 conflict 해결하기 + 브랜치 바꾸기 @@ -3055,14 +3184,20 @@ 새 분기로 전환 - 선택한 항목으로 폴더 생성 + 선택한 {0, plural, other { 개 항목}}으로 폴더 만들기 속성 + + 파일 탐색기 속성 열기 + 속성 + + 파일 탐색기 속성 창 열기 + 로컬 저장소 @@ -3081,6 +3216,9 @@ 패치 + + Git 저장소 복제 + 끌어오기 @@ -3125,7 +3263,7 @@ Mica Alt - 앱 배경 재질 + 배경 Solid @@ -3157,17 +3295,17 @@ 현재 GitHub에 액세스할 수 없습니다. - - VS Code에서 폴더 열기 + + {0}에서 폴더 열기 - - Visual Studio Code로 열기 + + {0}에서 현재 디렉터리를 엽니다 - - VS Code에서 저장소 열기 + + {0}에서 Repo 열기 - - Git 저장소 root를 Visual Studio Code에서 열기 + + {0}에서 Git & Repo의 루트를 엽니다 코드 복사 @@ -3194,7 +3332,7 @@ 리포지토리 초기화 - 리포지토리 초기화 + 현재 폴더를 Git 저장소로 초기화 원격 리포지토리 이름 @@ -3209,10 +3347,10 @@ 경로로 정렬 - 새 분할 화면에서 열기 + 선택한 경로를 새 창에서 열기 - 새 탭에서 열기 + 선택한 경로를 새 탭에서 열기 새 창에서 열기 @@ -3240,10 +3378,10 @@ 현재 '{0}' 명령을 실행할 수 없습니다. - 명령어 팔레트 + 명령 팔레트 - 명령어 팔레트 열기 + 경로 막대에서 명령어 열기 시작 시간: @@ -3267,7 +3405,7 @@ Files가 관리자 권한으로 실행 중입니다. - Windows와 WinAppSdk의 제약 때문에, Files를 관리자 권한으로 실행하면 드래그 앤 드롭 기능을 사용할 수 없습니다. 드래그 앤 드롭을 활성화하려면 시작 메뉴에서 UAC(사용자 계정 컨트롤) 설정을 열고 세 번째 옵션을 선택한 다음, Windows를 재부팅하면 됩니다. 이 방법을 사용하지 않는다면, Files는 드래그 앤 드롭 기능 없이도 사용 가능합니다. + Windows 및 WinAppSdk의 제한으로 인해 관리자 권한으로 파일을 실행할 때는 끌어서 놓기를 사용할 수 없습니다. 끌어서 놓기를 사용하려면 시작 메뉴에서 UAC(사용자 계정 컨트롤)를 열고 세 번째 옵션을 선택한 다음 Windows를 다시 시작하면 이 제한을 해결할 수 있습니다. 그렇지 않으면 끌어서 놓기 없이 파일을 계속 사용할 수 있습니다. 창을 닫아도 백그라운드에서 앱 계속 실행 @@ -3373,11 +3511,14 @@ 항목 처리 중... + + 항목 찾는 중... + 속도: - 항목 {0}개를 "{1}"(으)로 압축하는 작업을 취소했습니다 + 항목 {0} 개를 "{1} "으로 압축하는 작업을 취소했습니다 Shown in a StatusCenter card. @@ -3385,61 +3526,129 @@ Shown in a StatusCenter card. - 항목 {0}개를 "{1}"(으)로 압축했습니다 + {0, plural, other {# 개 항목}}を "{1}" 압축 완료 Shown in a StatusCenter card. - 항목 {0}개를 "{1}"에서 "{2}"(으)로 압축했습니다 + ​"{1}" 에서 "{2}" 로 {0, plural, other {# 개 항목}} 압축 완료 Shown in a StatusCenter card. - 항목 {0}개를 "{1}"(으)로 압축하는 작업 중 오류가 발생했습니다. + ​{0, plural, other {# 개 항목}}을 "{1}" 로 압축하는 중 오류가 발생했습니다 Shown in a StatusCenter card. - 항목 {0}개를 {1}에서 {2}(으)로 압축하지 못했습니다. + "{1}" 에서 "{2}" 로 {0, plural, other {# 개 항목}} 압축에 실패했습니다 Shown in a StatusCenter card. - 항목 {0}개를 "{1}"(으)로 압축 중 + {0, plural, other { 개 항목}} "{1}" 로 압축 중 Shown in a StatusCenter card. - 항목 {0}개를 "{1}"에서 "{2}"(으)로 압축 중 + Compressing {0, plural, one {# item} other {# items}} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Canceled cloning {0} to "{1}" + Shown in a StatusCenter card. + + + Canceled cloning {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Cloned "{0}" to "{1}" + Shown in a StatusCenter card. + + + Cloned {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Error cloning "{0}" to "{1}" + Shown in a StatusCenter card. + + + Failed to clone {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Cloning "{0}" to "{1}" + Shown in a StatusCenter card. + + + {0}을 "{1}"에서 "{2}"로 복제 중 입니다 + Shown in a StatusCenter card. + + + Canceled installing {0} fonts + Shown in a StatusCenter card. + + + Canceled installing {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Error installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Failed to install {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} from "{1}" Shown in a StatusCenter card. - 항목 {0}개를 "{1}"(으)로 복사하는 작업을 취소했습니다 + Canceled copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - 항목 {0}개를 "{1}"에서 "{2}"(으)로 복사하는 작업을 취소했습니다 + Canceled copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - 항목 {0}개를 "{1}"(으)로 복사했습니다 + Copied {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - 항목 {0}개를 "{1}"에서 "{2}"(으)로 복사했습니다 + Copied {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - 항목 {0}개를 "{1}"(으)로 복사하는 작업 중 오류가 발생했습니다. + Error copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - 항목 {0}개를 "{1}"에서 "{2}"(으)로 복사하지 못했습니다. + Failed to copy {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - 항목 {0}개를 "{1}"(으)로 복사 중 + Copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - 항목 {0}개를 "{1}"에서 "{2}"(으)로 복사 중 + Copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. + + Discovered {0, plural, one {# item} other {# items}} + Shown in a StatusCenter card during file discovery phase. + "{0}"을(를) "{1}"에 압축 해제하는 작업을 취소했습니다 Shown in a StatusCenter card. @@ -3473,55 +3682,55 @@ Shown in a StatusCenter card. - 항목 {0}개를 "{1}"에서 삭제하는 작업을 취소했습니다 + Canceled deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - 항목 {0}개를 "{1}"에서 삭제했습니다 + Deleted {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - 항목 {0}개를 "{1}"에서 삭제하는 작업 중 오류가 발생했습니다 + Error deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - 항목 {0}개를 "{1}"에서 삭제하는 데 실패했습니다 + Failed to delete {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - 항목 {0}개를 "{1}"에서 삭제 중 + Deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - 항목 {0}개를 "{1}"(으)로 이동하는 작업을 취소했습니다 + Canceled moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - 항목 {0}개를 "{1}"에서 "{2}"(으)로 이동하는 작업을 취소했습니다 + Canceled moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - 항목 {0}개를 "{1}"(으)로 이동했습니다 + Moved {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - 항목 {0}개를 "{1}"에서 "{2}"(으)로 이동했습니다 + Moved {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - 항목 {0}개를 "{1}"(으)로 이동 중 + Moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - 항목 {0}개를 "{1}"에서 "{2}"(으)로 이동 중 + Moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - 항목 {0}개를 "{1}"(으)로 이동하는 작업 중 오류가 발생했습니다. + Error moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - 항목 {0}개를 "{1}"에서 "{2}"(으)로 이동하지 못했습니다. + Failed to move {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. @@ -3549,7 +3758,7 @@ Shown in a StatusCenter card. - {0}/{1} 항목 처리 완료 + {0}/{1, plural, one {# item} other {# items}} processed Shown in a StatusCenter card. Used as "8/20 items processed" @@ -3567,6 +3776,9 @@ 배경 이미지를 설정하지 못했습니다 + + Failed to open the settings file + Git 브랜치 삭제 @@ -3592,7 +3804,7 @@ 태그를 적용하는 중 오류가 발생했습니다. - 단일 파일 아카이브는 현재 폴더에, 다중 파일 아카이브는 새로운 폴더에 압축 해제합니다 + Extract selected {0, plural, one {archive} other {archives}} here for single-item or to new folder for multi-item 여기에 압축 해제 (스마트) @@ -3601,7 +3813,7 @@ 파일과 폴더를 함께 정렬 - 파일과 폴더를 함께 정렬하기 + 파일과 폴더를 함께 정렬 파일 우선 정렬 @@ -3657,9 +3869,6 @@ 질문과 토론 - - 타일 보기에서 크기 설정을 아직 지원하지 않습니다. - 컴팩트 Used to describe layout sizes @@ -3821,6 +4030,10 @@ ICO 파일 This is the friendly name for ICO files. + + ICL 파일 + This is the friendly name for ICL files. + ZIP 파일 This is the friendly name for ZIP files. @@ -3829,6 +4042,10 @@ 비트맵 파일 This is the friendly name for bitmap files. + + Image Files + This is the friendly name for image files. + Num @@ -3855,51 +4072,309 @@ 도구 모음 표시 - Setting that controls if the Toolbar is shown in the main view + Setting that controls if the toolbar is shown in the main view 탭 동작 메뉴 - - Add vertical pane + + 세로로 분할 - Add a vertical pane + Split pane vertically - - Add horizontal pane + + 가로로 분할 - - Add a horizontal pane + + Split pane horizontally - Arrange vertically + 수직 정렬 - Arrange panes vertically + 패널을 수직 정렬합니다 - Arrange horizontally + 수평 정렬 - Arrange panes horizontally + 패널을 수평 정렬합니다 - - Add pane + + 분할 화면 - Show tab actions button in the title bar + 제목 표시줄에 탭 동작 버튼 표시 - Arrange panes + 패널 정렬 + + + Default dual pane split direction - - Default pane arrangement + + Dual pane mode - Horizontal + 수평 - Vertical + 수직 + + + Files 아이콘을 시스템 트레이에 표시 + + + CPU 스레드 + + + 홈 페이지로 이동 + + + 도구 모음 + + + 사용자 ID + + + 이름 일괄 바꾸기 + + + 압축 내용 + + + 대체 데이터 생성 옵션 표시 + + + 대체 데이터 생성 + + + Create alternate data stream for the selected {0, plural, one {item} other {items}} + + + 데이터 이름 입력 + + + 대체 데이터를 만드는 동안 오류가 발생했습니다 + + + 대체 데이터는 NTFS로 포맷 된 드라이브에서만 작동한다는 점에 유의하십시오. + + + 대체 데이터는 현재 숨겨져 있습니다 + + + 대체 데이터를 표시하시겠습니까? 파일 및 폴더 설정 페이지에서 언제든지 이 설정을 수정할 수 있습니다. + + + 태그 관리 + + + 사용 가능 + + + 합계 + + + 항상 새로 생성된 탭으로 초점 전환 + + + Toggle shelf pane + + + Toggle the visibility of the shelf pane + + + 주소 창에 선반 창 고정 표시 + + + 선반 + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + 항목 삭제 + + + 선반에서 제거 + + + 선반에 추가 + Tooltip that displays when dragging items to the Shelf Pane + + + Enter a hash to compare + Placeholder that appears in the compare hash text box + + + 일치함 {0} + Appears when two compared hashes match, e.g. "Matches SHA256" + + + No matches found + Appears when two compared hashes don't match + + + 경로 또는 별칭 + + + 경로가 잘못되었습니다 + + + 테스트 통합 + + + {0}은 찾을 수 없습니다. 설정을 확인한 후 다시 시도하십시오. + + + 구성된 IDE를 찾을 수 없습니다 + + + 설정 열기 + + + Visual Studio 코드 + + + 경로 또는 실행 별칭을 입력합니다 + + + IDE의 이름을 입력합니다 + + + Clone repo + Clone repo dialog title + + + Clone + Primary action button in the clone repo dialog + + + Repository URL + URL textbox header in the clone repo dialog + + + Cannot clone repo + Cannot clone repo dialog title + + + 비교할 파일 + Button that appears in file hash properties that allows the user to compare two files + + + Size format + + + Binary + + + Decimal + + + You can add sections to the sidebar by right-clicking and selecting the sections you want to add + + + Drag files or folders here to interact with them across different tabs + + + 탐색할 경로를 입력하세요... + + + 기능 및 명령 검색... + + + 파일 및 폴더 검색... + + + During file operations + + + Show status center button + + + Status center progress ring + Screen reader name for the status center progress ring + + + Icon files + This is the friendly name for a variety of different icon files. + + + Show collapsed path breadcrumbs + + + Show folders in {0} + + + Show folders in Home + + + There are no commands containing {0} + + + See more + + + 필터링 대상 + + + 파일이름 + + + 디지털 서명 + + + Signature list + + + 발급자: + + + 발급 대상: + + + 유효 기간(시작): + + + 유효 기간(종료): + + + No signature was found. + + + Show option to open folders in Windows Terminal + + + Open log file + + + Open log file in your default editor + + + Open log file location in your default file manager + + + Unable to open the log file + + + Show status bar + + + Only in Columns View + + + New {0} + + + Enable smooth scrolling + + + Scrolling + + + Show option to Pin to Sidebar + + + Show option to Pin to the Start Menu \ No newline at end of file diff --git a/src/Files.App/Strings/ku-Arab/Resources.resw b/src/Files.App/Strings/ku-Arab/Resources.resw deleted file mode 100644 index b981a627e1eb..000000000000 --- a/src/Files.App/Strings/ku-Arab/Resources.resw +++ /dev/null @@ -1,3905 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - New window - - - Copy path - - - Copy path with quotes - - - Browse - - - Size - - - Created: - - - Path - - - Size: - - - Size on disk: - - - Uncompressed size: - - - This action cannot be done - - - The destination folder - - - is a subfolder of the source folder - - - Skip - - - Select All - - - Invert selection - - - Clear selection - - - Modified: - - - Accessed: - - - Clear all items - - - Enter a path to navigate to or type ">" to open the command palette - - - Search - - - Files you've previously accessed will show up here - - - Remove this item - - - GitHub repository - - - About - - - Open source - - - Date format - - - Theme - - - Show extensions for known file types - - - Show hidden files and folders - - - Show dot files - - - Appearance - - - Background color - - - Advanced - - - Continue where you left off - - - Open a new tab - - - Open a specific page or pages - - - Desktop - - - Documents - - - Downloads - - - Name - - - Ascending - - - Paste - - - New - - - Properties - - - Open - - - Open in new tab - - - Open in new window - - - Share - - - Cut - - - Delete - - - Pin to Sidebar - - - Welcome to Files! - - - Grant permission - - - To get started, you'll need to grant us permission to display your files. This will open a Settings page where you can grant us this permission. You'll need to reopen the app once you've completed this step. - - - Display - - - Enter an item name - - - Set name - - - Light - - - Dark - - - Application - - - System - - - New folder - - - New file - - - This folder is empty. - - - Back - - - Forward - - - Up - - - Refresh - - - An item with this name already exists in this directory. - - - Replace existing item - - - Item already exists - - - Set as lockscreen background - - - Set as app background - - - We weren't able to delete this item - - - Please insert the necessary drive to access this item. - - - Drive unplugged - - - The file you are attempting to access may have been moved or deleted. - - - Cannot open properties for this file - - - The file you are attempting to access may have been moved or deleted. - - - File Not Found - - - The folder you are attempting to access may have been moved or deleted. - - - Did you delete this folder? - - - The file you are attempting to access is currently being used by {0} - - - The file you are attempting to access is currently being used by another application - - - File is in use - - - Layout - - - Read-only - - - {0, plural, one {# day ago} other {# days ago}} - - - 1 day ago - - - {0} days ago - - - {0, plural, one {# hour ago} other {# hours ago}} - - - 1 hour ago - - - {0} hours ago - - - {0, plural, one {# minute ago} other {# minutes ago}} - - - 1 minute ago - - - {0} minutes ago - - - {0, plural, one {# second ago} other {# seconds ago}} - - - 1 second ago - - - {0} seconds ago - - - Now - - - The requested operation is not supported - - - {0} free of {1} - - - Send to - - - The item name must not contain the following characters: \ / : * ? " < > | - - - The item name specified is invalid - - - {0, plural, one {# item selected} other {# items selected}} - ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural - - - {0, plural, one {item} other {items}} - ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural - - - Yes - - - Are you sure you want to permanently delete all these items? - - - Empty recycle bin - - - bytes - - - KB - - - MB - - - GB - - - TB - - - PB - - - B - - - {0, number} {0, plural, one {file} other {files}}, {1, number} {1, plural, one {folder} other {folders}} - - - {0, number} {0, plural, one {file} other {files}}, {1, number} {1, plural, one {folder} other {folders}} from {2, number} {2, plural, one {location} other {locations}} - - - Run as another user - - - All type of {0} - - - Different types - - - All in {0} - - - Used space: - - - Free space: - - - Capacity: - - - File system: - - - Recent - - - Move tab here - - - Status - - - Windows default - - - No results - - - Can't access any items to display - - - Move to {0} - - - Copy to {0} - - - Create shortcut - - - Open file location - - - Arguments: - - - Destination: - - - Shortcut type: - - - Web link - - - General - - - Shortcut - - - Internet shortcut - - - {0} - shortcut - - - Files ran into a problem that the developers didn't prepare for yet. - - - Something went wrong! - - - Report this issue - - - The item referenced is either invalid or inaccessible.{0}Error message:{0}{1} - - - Invalid item - - - Version: - - - There's nothing to share right now... - - - The items you've selected will be shared - - - The selected item will be shared - - - Sharing {0} - - - Sharing {0} {1} - - - The item you're attempting to rename no longer exists. Please verify the correct location of the item you need to rename. - - - Item no longer exists - - - The name specified was invalid. Please check the desired item name and try again. - - - Invalid item name - - - The length of the item name specified exceeds the maximum limit. Please check the desired name and try again. - - - Item name was too long - - - Show more options - - - The application needs to be restarted in order to apply these settings, would you like to restart the app? - - - Sponsor us on GitHub - - - We weren't able to create this item - - - Access Denied - - - Set as - - - Cancel - - - Create a new item - - - Choose a type for this new item below - - - File - - - Creates an empty file - - - Folder - - - Creates an empty folder - - - Refresh the directory - - - Language - - - Move shell extensions into a sub menu - - - Show confirmation dialog when deleting items - - - There was an issue saving some properties. - - - Error - - - Close anyway - - - Rating - - - Item Path - - - Item Type - - - Title - - - Subject - - - Comment - - - Copyright - - - Date Modified - - - Bit Depth - - - Dimensions - - - Horizontal Size - - - Vertical Size - - - Horizontal Resolution - - - Vertical Resolution - - - Color Space - - - sRGB - - - Unspecified - - - Longitude Decimal - - - Latitude Decimal - - - Altitude - - - Date Taken - - - Camera Manufacturer - - - Camera Model - - - Exposure Time - - - Focal Length - - - Aperture - - - People Names - - - Channel Count - - - Format - - - Sample Rate - - - Album Artist - - - Album Title - - - Artist - - - Beats Per Minute - - - Composer - - - Conductor - - - Disc Number - - - Genre - - - Track Number - - - Duration - - - Frame Count - - - Protection Type - - - Author Url - - - Content Distributor - - - Date Released - - - Series Name - - - Season Number - - - Episode Number - - - Producer - - - Promotion Url - - - Provider Style - - - Publisher - - - Thumbnail Large Path - - - Thumbnail Large Uri - - - Thumbnail Small Path - - - Thumbnail Small Uri - - - User Web Url - - - Writer - - - Year - - - Contributor - - - Last Author - - - Revision Number - - - Version - - - Date Created - - - Total Editing Time - - - Template - - - Word Count - - - Character Count - - - Line Count - - - Paragraph Count - - - Page Count - - - Slide Count - - - Frame Rate - - - Encoding Bitrate - - - Audio Encoding Bitrate - - - Video Encoding Bitrate - - - Compression - - - Frame Width - - - Frame Height - - - Orientation - - - Core - - - Image - - - Photo - - - GPS - - - Media - - - Audio - - - Music - - - Video - - - Document - - - Address - - - Selection options - - - Select - - - Recycle bin item - - - Shortcut item - - - Drives - - - Move here - - - Safe to remove hardware - - - The device can now be safely removed from the computer. - - - Problem Ejecting Device - - - This device is currently in use. Close any programs, windows or tabs that might be using the device, and then try again. - - - Eject - - - Duplicate tab - - - Move tab to new window - - - New tab - - - Some properties may contain personal information. - - - Clear All Properties - - - Check the status of file operations here - - - Status center - - - Search results in {1} for {0} - - - Details - - - No - - - Show protected system files - - - Sync layout and sorting preferences across directories - - - Customize the right click context menu - - - Original path - - - Add - - - Edit - - - Remove - - - Open tabs in dual pane mode - - - Show option to open folders in a new pane - - - Show option to copy path - - - Show option to create folder with selection - - - Show option to create shortcut - - - New pane - - - Open in new pane - - - Files & folders - - - Details (Ctrl+Shift+1) - - - Tiles (Ctrl+Shift+2) - - - Date deleted - - - Cloud Drives - - - Confirm - - - Desired name - - - Toggle the preview pane - - - Toggle the details pane - - - Toggle the details pane to view basic file properties - - - Toggle the info pane - - - Toggle the info pane to view the detail/preview panes - - - Toggle the Toolbar - - - No preview available - - - Item Name - - - Unpin from Sidebar - - - Network - - - File details - - - File preview - - - Selected file preview pane - - - Feedback - - - Available when online - - - Documentation - - - Available offline - - - Partially available offline - - - Syncing - - - Excluded from sync - - - Not calculated - - - Unknown - - - If you change a file extension, the file might become unusable. Are you sure you want to change it? - - - CD ROM Drive - - - Cloud Drive - - - Fixed Disk Drive - - - Floppy Disk Drive - - - Network Drive - - - Unmounted Drive - - - RAM Disk Drive - - - Removable Storage Device - - - Unknown - - - Virtual Drive - - - Date created - - - More options... - - - Map network drive - - - Pinned - - - Libraries - - - No item selected - - - Target - - - Arguments - - - Default - - - Item count - - - This action requires administrator rights - - - Would you like to continue as administrator? - - - Columns (Ctrl+Shift+6) - - - Pin to the Start Menu - - - Unpin from the Start Menu - - - Library - - - Locations: - - - Set as default save path - - - No locations - - - Input field cannot be empty! - - - The name must not contain the following characters: \ / : * ? " < > | - - - Using this name is not allowed! - - - Library with the same name already exists! - - - Open Storage Sense - - - Copy - - - Copy {0, plural, one {item} other {items}} - - - Delete {0, plural, one {item} other {items}} - - - Move - - - {0, plural, one {One item will be moved} other {# items will be moved}} - - - Move {0, plural, one {item} other {items}} - - - {0, plural, one {One item will be deleted} other {# items will be deleted}} - - - Continue - - - {0, plural, one {There is one conflicting file name} other {There are # conflicting file names}} - - - {0, plural, one {There is one conflicting file name} other {There are # conflicting file names}}, and {1, plural, one {one outgoing item} other {# outgoing items}} - - - Conflicting {0, plural, one {file name} other {file names}} - - - {0, plural, one {One item will be copied} other {# items will be copied}} - - - Permanently delete - - - ISO speed - - - Replace existing - - - Generate new name - - - Create folder with selection - - - Type: - - - Date modified: - - - Open with - - - File icon - - - Original path column - - - Item type column - - - Date modified column - - - Date deleted column - - - Sort - - - Date modified - - - Original folder - - - Descending - - - Huge - - - Very large - - - Large - - - Medium - - - Small - - - Tiny - - - Future - - - Today - - - Yesterday - - - Earlier this week - - - Last week - - - Earlier this month - - - Last month - - - Earlier this year - - - Last year - - - Year {0} - - - {0} item - - - {0} items - - - Reopen closed tab - - - Rename - - - Privacy - - - the Recycle Bin - - - Canceling - - - Clear - - - Widgets - - - Sync status - - - Security - - - Advanced permissions - - - Allow - - - Deny - - - Full control - - - List directory contents - - - Modify - - - Permissions for {0} - - - Read and execute - - - Read - - - Group or user names - - - Write - - - Unknown account - - - You do not have permissions to view the security properties of this object. Click "Advanced permissions" to proceed. - - - Owner: - - - Unknown owner - - - Extract archive - - - Open destination folder when complete - - - Extract to {0}\ - - - Create new library - - - Restore default libraries - - - Are you sure you want to restore the default libraries? All your files will remain on your storage. - - - Restore Libraries - - - Owner - - - Special - - - Access - - - Applies to - - - Principal - - - files - - - this folder - - - subfolders - - - Inherited - - - Permissions - - - You do not have permissions to view the security properties of this object. You can try to take ownership of this object. - - - This folder and files - - - This folder, subfolders and files - - - Only files - - - Only subfolders - - - Only subfolders and files - - - Only this folder - - - This folder and subfolders - - - Append data - - - Change permission - - - Create folders - - - Create files - - - Delete subdirectories and files - - - Execute files - - - Read attributes - - - Read data - - - Read extended attributes - - - Read permissions - - - Take ownership - - - Visit folder - - - Write attributes - - - Write data - - - Write extended attributes - - - Convert inherited permissions into explicit permissions - - - Enable inheritance - - - Remove all inherited permissions - - - Reset child permissions - - - Replace all child object permissions entries with inheritable permission entries from this object - - - Close active pane - - - Enter compact overlay - - - Exit compact overlay - - - File description - - - Company - - - Language - - - Trademarks - - - File version - - - Load full preview - - - Downloads the item from the cloud and loads the preview - - - {0, plural, one {# item} other {# items}} ({1, plural, one {# file} other {# files}}, {2, plural, one {# folder} other {# folders}}) - - - Uncompressed size - - - WSL - - - Hide {0} section - - - Add file - - - Updates available - - - Updates are ready to install - - - Close - - - Update - - - Home - - - Ctrl+Shift+1 - - - Ctrl+Shift+2 - - - Ctrl+Shift+6 - - - The archive extraction completed successfully. - - - Extracting archive - - - Extracting complete! - - - Open parent folder - - - Unknown - - - Edit tags - - - Part of set - - - Open drive - - - Please insert a disc into drive {0} - - - Insert a disc - - - OK - - - Credential required - - - Anonymous - - - Provide your credential: - - - UserName - - - No items found - - - Open log location - - - Create link in {0} - - - Columns - - - Tiles - - - Open folders in new tab - - - Status center - - - Date created column - - - Item size column - - - Experimental feature flags - - - Help and support - - - Submit feature request - - - Submit bug report - - - Set Files as the default file manager - - - Use Files as Open File Dialog - - - Tag - - - Open items with a single click - - - Folder path - - - Export settings - - - Import settings - - - Couldn't import settings. The settings file is corrupted. - - - Error importing settings - - - Empty Recycle Bin - - - Sidebar - - - Choose a custom folder icon - - - Customization - - - Restore default - - - Open tab in existing instance when opening Files from another app - - - Startup settings - - - Compatibility - - - None - - - On Windows login - - - On this program start - - - System - - - System (Enhanced) - - - 16-bit (65536) color - - - 8-bit (256) color - - - Disable full-screen optimizations - - - Run in 640 x 480 screen resolution - - - Override high DPI scaling behavior - - - Reduced color mode - - - Register this program for restart - - - Run as administrator - - - Run compatibility troubleshooter - - - Use DPI settings of the main monitor - - - Do not adjust DPI - - - Do not override DPI - - - Compatibility mode - - - No reduced color - - - Third party libraries - - - This option modifies the system registry and can have unexpected side effects on your device. Continue at your own risk. - - - Create Library - - - Enter Library name - - - Calculate folder sizes - - - Recent files - - - Open Files on Windows startup - - - Attributes - - - Hidden - - - More details - - - Read only - - - Cleanup your drive contents - - - Type - - - Bytes - - - Extract - - - Extract files - - - Extract here - - - Ctrl+E - - - Extract to {0} - - - Run script - - - Run with PowerShell - - - Calculating folder sizes is resource intensive and may cause your CPU usage to increase. - - - Rotate left - - - Rotate right - - - Install - - - Close tabs to the left - - - Close tabs to the right - - - Close other tabs - - - Music - - - Pictures - - - Update Files - - - Tags - - - Universal - - - ex: {0}, {1} - - - Show thumbnails - - - Retry - - - Move operation is not supported in this context. Do you want to copy the items instead? - - - Hidden items - - - Custom - - - Set as desktop slideshow - - - Size all columns to fit - - - Adaptive layout - - - Adaptive layout (Ctrl+Shift+7) - - - Ctrl+Shift+7 - - - Show alternate data streams - - - Behaviors - - - Review Files - - - Would you like to review Files? - - - Set as background - - - Set as desktop background - - - Set as default - - - Date column - - - Date created column - - - Size column - - - Tag column - - - Type column - - - Lockscreen - - - Open folders with a single click in the Columns Layout - - - Opening items - - - Compress - - - Select files and folders when hovering over them - - - Are you sure you want to restore all items in the recycle bin? - - - Restore all items - - - Do you want to restore the {0} selected item(s)? - - - Restore selection - - - Restore All Items - - - Restore - - - Shortcut cannot be opened - - - The target destination cannot be found {0}. Do you want to delete this shortcut? - - - You don't have permission to access this folder. - - - Archive password - - - Path - - - Sorting and grouping - - - Create archive - - - Create - - - Create {0} - - - Enter a name - - - Compression level - - - Ultra - - - High - - - Normal - - - Low - - - Fast - - - None - - - Splitting size - - - Do not split - - - CD - - - DVD - - - FAT - - - Blu-ray - - - Encryption - - - Password - - - Format - - - Sync status column - - - Group by - - - Sort by - - - Group in descending order - - - Sort in descending order - - - Create a new shortcut - - - Create shortcuts to local or network programs, files, folders, computers or Internet addresses. - - - Enter the location of the item: - - - Creates a shortcut - - - Recently used files is currently disabled in Windows File Explorer. - - - Edit settings file - - - What's new - - - Creating a shortcut in this location requires administrator privileges - - - Would you like to create the shortcut on the desktop instead? - - - The specified item name is invalid - - - Settings - - - Double click on a blank space to go up one directory - - - View more - - - Show edit tags flyout - - - Show compression options - - - Show send to menu - - - Show option to open folders in a new tab - - - Show option to open folders in a new window - - - Quick access - - - Enter your credentials to connect to: {0} - - - Enter network credentials - - - Remember my credentials - - - Network folder error - - - App version - - - Windows version - - - Always - - - Permanent deletion only - - - Never - - - Tag color - - - New tag - - - Create new tag - - - Loading... - - - Context menu options - - - Show file extensions - - - Show hidden items - - - Format... - - - Help - - - Toggle full screen - - - Are you sure you want to delete this tag? - - - Play all - - - Height - - - Width - - - Apply this action to all conflicting items - - - Open in Windows Terminal - - - Open in Windows Terminal as administrator - - - Save - - - Multiselect - - - Reorder sidebar items - - - Hashes - - - An error occurred during the calculation - - - Failed to calculate the hash, please close the file and try again. - - - Hashes aren't available for online files - - - Algorithm - - - Hash value - - - Select hashes to show - - - Calculating... - - - Pinned items - - - Decrease size - - - Increase size - - - Toggle sort direction - - - Invalid name - - - Name must not be empty or start or end with a period. - - - Videos - - - Launch preview popup - - - Toggle compact overlay - - - Open online help page in browser - - - Toggle full screen - - - Enter compact overlay - - - Exit compact overlay - - - Toggle compact overlay - - - Go to search box - - - Toggle whether to show hidden items - - - Toggle whether to show file extensions - - - Toggle the preview pane to view file previews - - - Toggle whether to show sidebar - - - Copy item(s) to clipboard - - - Copy path of selected items to the clipboard - - - Copy path of selected items with quotes to the clipboard - - - Cut item(s) to clipboard - - - Paste item(s) from clipboard to current folder - - - Paste item(s) from clipboard to selected folder - - - Delete item(s) - - - Create new folder - - - Create new shortcut(s) to selected item(s) - - - Create new shortcut to any item - - - Empty recycle bin - - - Open "Format Drive" menu for selected item - - - Restore selected item(s) from recycle bin - - - Restore all items from recycle bin - - - Open item(s) - - - Open item(s) with selected application - - - Open parent folder of searched item - - - Refresh page contents - - - Rename selected item - - - Select all items - - - Invert item selection - - - Clear item selection - - - Toggle item selection - - - Share selected file(s) with others - - - Pin item(s) to the Start Menu - - - Unpin item(s) from the Start Menu - - - Pin folder(s) to Sidebar - - - Unpin folder(s) from Sidebar - - - Set selected picture as desktop background - - - Set selected pictures as desktop slideshow - - - Set selected picture as lockscreen background - - - Set selected picture as the app background - - - Install selected font(s) - - - Install driver(s) using selected inf file(s) - - - Install selected certificate(s) - - - Run selected application as administrator - - - Run selected application as another user - - - Run selected PowerShell script - - - Launch preview in popup window - - - Create archive with selected item(s) - - - Create 7z archive instantly with selected item(s) - - - Create zip archive instantly with selected item(s) - - - Extract items from selected archive(s) to any folder - - - Extract items from selected archive(s) to current folder - - - Extract items from selected archive(s) to new folder - - - Rotate selected image(s) to the left - - - Rotate selected image(s) to the right - - - Open settings page - - - Open folder in terminal - - - Open folder in terminal as administrator - - - Decrease item size in the current view - - - Increase item size in the current view - - - Switch to details view - - - Switch to tiles view - - - Switch to list view - - - List - - - Grid - - - Switch to grid view - - - Switch to columns view - - - Switch views adaptively - - - Sort items by name - - - Sort items by date modified - - - Sort items by date created - - - Sort items by size - - - Sort items by type - - - Sort items by sync status - - - Sort items by tags - - - Sort items by original folder - - - Sort items by date deleted - - - Sort items in ascending order - - - Sort items in descending order - - - Toggle item sort direction - - - List items without grouping - - - Group items by name - - - Group items by date modified - - - Group items by date created - - - Group items by size - - - Group items by type - - - Group items by sync status - - - Group items by tags - - - Group items by original folder - - - Group items by date deleted - - - Group items by folder path - - - Sort groups in ascending order - - - Sort groups in descending order - - - Toggle group sort direction - - - Open new tab - - - Navigate backward in navigation history - - - Navigate forward in navigation history - - - Navigate up one directory - - - Duplicate current tab - - - Duplicate selected tab - - - Close tabs to the left of current tab - - - Close tabs to the left of selected tab - - - Close tabs to the right of current tab - - - Close tabs to the right of selected tab - - - Close tabs other than current tab - - - Close tabs other than selected tab - - - Reopen last closed tab - - - Move to the previous tab - - - Move to the next tab - - - Close current tab - - - Close active pane - - - Focus other pane - - - Switch focus to the non active pane - - - Toggle the sidebar - - - Alt - Key name for hotkeys in menus. Use abbreviation if possible. - - - Ctrl - Key name for hotkeys in menus. Use abbreviation if possible. - - - Shift - Key name for hotkeys in menus. Use abbreviation if possible. - - - Win - Key name for hotkeys in menus. Use abbreviation if possible. - - - Enter - Key name for hotkeys in menus. Use abbreviation if possible. - - - Space - Key name for hotkeys in menus. Use abbreviation if possible. - - - Esc - Key name for hotkeys in menus. Use abbreviation if possible. - - - Backspace - Key name for hotkeys in menus. Use abbreviation if possible. - - - Tab - Key name for hotkeys in menus. Use abbreviation if possible. - - - Ins - Key name for hotkeys in menus. Use abbreviation if possible. - - - Del - Key name for hotkeys in menus. Use abbreviation if possible. - - - Left - Key name for hotkeys in menus. Use abbreviation if possible. - - - Right - Key name for hotkeys in menus. Use abbreviation if possible. - - - Down - Key name for hotkeys in menus. Use abbreviation if possible. - - - Up - Key name for hotkeys in menus. Use abbreviation if possible. - - - Home - Key name for hotkeys in menus. Use abbreviation if possible. - - - End - Key name for hotkeys in menus. Use abbreviation if possible. - - - PageDown - Key name for hotkeys in menus. Use abbreviation if possible. - - - PageUp - Key name for hotkeys in menus. Use abbreviation if possible. - - - Sep - Key name for hotkeys in menus. Use abbreviation if possible. - - - Pause - Key name for hotkeys in menus. Use abbreviation if possible. - - - Sleep - Key name for hotkeys in menus. Use abbreviation if possible. - - - Clear - Key name for hotkeys in menus. Use abbreviation if possible. - - - Print - Key name for hotkeys in menus. Use abbreviation if possible. - - - Help - Key name for hotkeys in menus. Use abbreviation if possible. - - - Mouse4 - Key name for hotkeys in menus. Use abbreviation if possible. - - - Mouse5 - Key name for hotkeys in menus. Use abbreviation if possible. - - - App - Key name for hotkeys in menus. Use abbreviation if possible. - - - App1 - Key name for hotkeys in menus. Use abbreviation if possible. - - - App2 - Key name for hotkeys in menus. Use abbreviation if possible. - - - Mail - Key name for hotkeys in menus. Use abbreviation if possible. - - - GoHome - Key name for hotkeys in menus. Use abbreviation if possible. - - - GoBack - Key name for hotkeys in menus. Use abbreviation if possible. - - - GoForward - Key name for hotkeys in menus. Use abbreviation if possible. - - - BrowserRefresh - Key name for hotkeys in menus. Use abbreviation if possible. - - - BrowserStop - Key name for hotkeys in menus. Use abbreviation if possible. - - - Search - Key name for hotkeys in menus. Use abbreviation if possible. - - - Favorites - Key name for hotkeys in menus. Use abbreviation if possible. - - - PlayPause - Key name for hotkeys in menus. Use abbreviation if possible. - - - MediaStop - Key name for hotkeys in menus. Use abbreviation if possible. - - - PrevTrack - Key name for hotkeys in menus. Use abbreviation if possible. - - - NextTrack - Key name for hotkeys in menus. Use abbreviation if possible. - - - MediaSelect - Key name for hotkeys in menus. Use abbreviation if possible. - - - Mute - Key name for hotkeys in menus. Use abbreviation if possible. - - - VolDown - Key name for hotkeys in menus. Use abbreviation if possible. - - - VolUp - Key name for hotkeys in menus. Use abbreviation if possible. - - - Change - - - Toggle selection - - - Show warning when changing file extensions - - - This PC - - - Recycle Bin - - - Untagged - - - Moves to the previous tab - - - Moves to the next tab - - - Closes current tab - - - Edit path - - - Redo - - - Undo - - - Invalid location - - - Are you sure you want to copy these files without their properties? - - - These files have properties that can't be copied to the new location - - - These files have properties that can't be moved to the new location - - - Are you sure you want to move these files without their properties? - - - Show checkboxes when selecting items - - - Focus path bar - - - Create new item - - - No groups or users have permission to access this object. However, the owner of this object can assign permissions. - - - Open the item's location - - - Delete permanently - - - Delete item(s) permanently - - - Play the selected media files - - - Undo the last file operation - - - Redo the last file operation - - - Location: - - - Group items by month - - - Group items by year - - - Month - - - Day - - - Toggle unit for grouping by date - - - Toggle grouping unit - - - Year - - - Group by date unit - - - Group items by day of date created - - - Group items by month of date created - - - Group items by year of date created - - - Group items by day of date deleted - - - Group items by month of date deleted - - - Group items by year of date deleted - - - Group items by day of date modified - - - Group items by month of date modified - - - Group items by year of date modified - - - Click 'Advanced permissions' to continue. - - - You must have Read permissions to view the properties of this item. - - - To try taking ownership of the item, which includes permission to view its properties, click Change above. - - - Unable to display permissions. - - - Leave my changes on '{0}' - - - Discard my changes - - - Bring my changes to '{0}' - - - You have uncommitted changes on this branch. What would you like to do with them? - - - Switch branch - - - Branches - - - Switch - - - New branch - - - Invalid branch name - - - Create branch - - - Create branch - - - Based on - - - Switch to new branch - - - Create a folder with the currently selected item(s) - - - Open properties - - - Open properties window - - - Locals - - - Remotes - - - Translate on Crowdin - - - Fetch - - - Pull - - - Run git fetch - - - Run git pull - - - Preview - - - Git status - - - Git error - - - git pull failed due to a timeout error - - - (multiple values) - Text indicating that multiple selected files have different metadata values. - - - Author - - - Date committed - - - Commit message - - - Commit SHA - - - Git - - - Acrylic - - - Mica - - - Mica Alt - - - Backdrop Material - - - Solid - - - Manage branches - - - Push - - - Run git push - - - Sync - - - Run git pull and then git push - - - {0} outgoing / {1} incoming commits - - - Connect to GitHub - - - Enter the following code at the link below to start working with GitHub repositories. - - - Files cannot access GitHub right now. - - - Open folder in VS Code - - - Open the current directory in Visual Studio Code - - - Open repo in VS Code - - - Open the root of the Git repo in Visual Studio Code - - - Copy code - - - Added - - - Deleted - - - Modified - - - Untracked - - - Unable to display current owner. - - - Great! You are now authorized. - - - Initialize repo - - - Initialize a Git repository - - - Remote Repository Name - - - Current Branch - - - Path column - - - Sort items by path - - - Open directory in new pane - - - Open directory in new tab - - - Open directory in new window - - - Open all - - - Open all tagged items - - - Drive ({0}) - {0} is the drive letter. - - - Invalid command - - - '{0}' is not recognized as a command. - - - Command not executable - - - The '{0}' command is not ready to be executed. - - - Command palette - - - Open command palette - - - Start in: - - - Turn on BitLocker - - - Manage BitLocker - - - The following items are too large to be copied to this drive - - - Format drive - - - Don't show again - - - Files is running as administrator - - - Due to a limitation with Windows and WinAppSdk, drag and drop isn't available when running Files as admin. If you wish to use drag and drop, you can work around this limitation by opening UAC (User Account Control) from the Start Menu and selecting the third level, and restarting Windows. Otherwise, you can keep using Files without drag & drop. - - - Leave app running in the background when the window is closed - - - Blue - One of the custom color themes - - - Blue Gray - One of the custom color themes - - - Brick Red - One of the custom color themes - - - Camouflage - One of the custom color themes - - - Cool Blue Bright - One of the custom color themes - - - Gray - One of the custom color themes - - - Gray Dark - One of the custom color themes - - - Green - One of the custom color themes - - - Iris Pastel - One of the custom color themes - - - Mint Light - One of the custom color themes - - - Mod Red - One of the custom color themes - - - Orange Bright - One of the custom color themes - - - Overcast - One of the custom color themes - - - Red - One of the custom color themes - - - Rose Bright - One of the custom color themes - - - Seafoam - One of the custom color themes - - - Storm - One of the custom color themes - - - Violet Red Light - One of the custom color themes - - - Yellow Gold - One of the custom color themes - - - Clear completed - - - Name: - - - {0} of {1} processed - Shown in a StatusCenter card. Used as "64 MB of 1 GB processed" - - - items - - - Files can't initialize this directory as a Git repository. - - - Contributing Artist - - - Add page - - - Processing items... - - - Speed: - - - Canceled compressing {0} items to "{1}" - Shown in a StatusCenter card. - - - Canceled compressing {0} items from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Compressed {0} item(s) to "{1}" - Shown in a StatusCenter card. - - - Compressed {0} item(s) from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Error compressing {0} item(s) to "{1}" - Shown in a StatusCenter card. - - - Failed to compress {0} item(s) from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Compressing {0} item(s) to "{1}" - Shown in a StatusCenter card. - - - Compressing {0} item(s) from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Canceled copying {0} item(s) to "{1}" - Shown in a StatusCenter card. - - - Canceled copying {0} item(s) from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Copied {0} item(s) to "{1}" - Shown in a StatusCenter card. - - - Copied {0} item(s) from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Error copying {0} item(s) to "{1}" - Shown in a StatusCenter card. - - - Failed to copy {0} item(s) from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Copying {0} item(s) to "{1}" - Shown in a StatusCenter card. - - - Copying {0} item(s) from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Canceled extracting "{0}" to "{1}" - Shown in a StatusCenter card. - - - Canceled extracting "{0}" from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Extracted "{0}" to "{1}" - Shown in a StatusCenter card. - - - Extracted "{0}" from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Error extracting "{0}" to "{1}" - Shown in a StatusCenter card. - - - Failed to extract "{0}" from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Extracting "{0}" to "{1}" - Shown in a StatusCenter card. - - - Extracting "{0}" from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Canceled deleting {0} item(s) from "{1}" - Shown in a StatusCenter card. - - - Deleted {0} item(s) from "{1}" - Shown in a StatusCenter card. - - - Error deleting {0} item(s) from "{1}" - Shown in a StatusCenter card. - - - Failed to delete {0} item(s) from "{1}" - Shown in a StatusCenter card. - - - Deleting {0} item(s) from "{1}" - Shown in a StatusCenter card. - - - Canceled moving {0} item(s) to "{1}" - Shown in a StatusCenter card. - - - Canceled moving {0} item(s) from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Moved {0} item(s) to "{1}" - Shown in a StatusCenter card. - - - Moved {0} item(s) from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Moving {0} item(s) to "{1}" - Shown in a StatusCenter card. - - - Moving {0} item(s) from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Error moving {0} item(s) to "{1}" - Shown in a StatusCenter card. - - - Failed to move {0} item(s) from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Canceled emptying Recycle Bin - Shown in a StatusCenter card. - - - Emptied Recycle Bin - Shown in a StatusCenter card. - - - Emptying Recycle Bin - Shown in a StatusCenter card. - - - Error emptying Recycle Bin - Shown in a StatusCenter card. - - - Failed to empty Recycle Bin - Shown in a StatusCenter card. - - - Preparing the operation... - Shown in a StatusCenter card. - - - {0}/{1} item(s) processed - Shown in a StatusCenter card. Used as "8/20 items processed" - - - Unblock downloaded file - - - No ongoing file operations - - - The option to open Files on Windows Startup is not available due to your system settings or group policy. To re-enable, open the startup page in Task Manager. - - - Failed to restore items from Recycle Bin - - - Failed to set the background wallpaper - - - Delete Git branch - - - Are you sure you want to permanently delete "{0}" branch? - - - Connected to GitHub - - - Logout - - - Connect to GitHub - - - Login - - - Tags are currently only compatible on drives formatted as NTFS. - - - There was an error applying this tag - - - Extract items from selected archive(s) to current folder for single-item archive, or to new folder for multi-item archive - - - Extract here (Smart) - - - Sort files and folders together - - - Sort files and folders together - - - Sort files first - - - Sort files first then folders - - - Sort folders first - - - Sort folders first then files - - - Sort priority - - - Failed to rotate the image - - - Restart - - - Quit - - - Failed to share items - - - Scroll to previous folder when navigating up - - - Open new window - - - Change album cover - - - Failed to rename item - - - Editing "{0}" requires additional permissions - - - Edit permissions - - - Files is still running in the background to improve startup performance. - - - Where did Files go? - - - Questions & discussions - - - Additional sizes are not yet available for the Tiles View. - - - Compact - Used to describe layout sizes - - - Small - Used to describe layout sizes - - - Medium - Used to describe layout sizes - - - Medium + - Used to describe layout sizes - - - Medium ++ - Used to describe layout sizes - - - Medium +++ - Used to describe layout sizes - - - Medium ++++ - Used to describe layout sizes - - - Medium +++++ - Used to describe layout sizes - - - Large - Used to describe layout sizes - - - Large + - Used to describe layout sizes - - - Large ++ - Used to describe layout sizes - - - Large +++ - Used to describe layout sizes - - - Extra large - Used to describe layout sizes - - - Details view - - - Layout type - - - Actions - - - Commands - - - Add command - - - Restore defaults - - - Choose an action - - - Are you sure you want to restore the default key bindings? This action cannot be undone. - - - This key binding is already being used, please choose a different key binding to continue. - - - The key binding you choose cannot be used, please try again using a different key binding. - - - Customized - - - Adaptive layout is not available when preferences are synced, the default layout has been changed to Details. - - - Background image - - - Bottom - Image alignment type - - - Center - Image alignment type - - - Fill - Image stretch type - - - Horizontal alignment - - - Image fit - - - Left - Image alignment type - - - Opacity - - - Right - Image alignment type - - - Top - Image alignment type - - - Uniform - Image stretch type - - - Uniform to fill - Image stretch type - - - Vertical alignment - - - Thin Acrylic - This is a type of backdrop for the application background - - - Show for all locations - Setting where users can choose to display "Open IDE" button for Git Repos - - - Developer tools - - - Configure the "Open IDE" button on the status bar - - - Show for Git repos - Setting where users can choose to display "Open IDE" button for all locations. - - - Application extension - This is the friendly name for DLL files. - - - ICO File - This is the friendly name for ICO files. - - - Zip File - This is the friendly name for ZIP files. - - - Bitmap Files - This is the friendly name for bitmap files. - - - Num - - - Network locations - - - There are no network locations. If you don't use network locations, you can disable the widget. - - - Disable - - - Edit in Notepad - - - Edit the selected file in Notepad - - - Dimensions: - - - Status: - - - Show Toolbar - Setting that controls if the Toolbar is shown in the main view - - - Tab actions menu - - - Add vertical pane - - - Add a vertical pane - - - Add horizontal pane - - - Add a horizontal pane - - - Arrange vertically - - - Arrange panes vertically - - - Arrange horizontally - - - Arrange panes horizontally - - - Add pane - - - Show tab actions button in the title bar - - - Arrange panes - - - Default pane arrangement - - - Horizontal - - - Vertical - - \ No newline at end of file diff --git a/src/Files.App/Strings/lt-LT/Resources.resw b/src/Files.App/Strings/lt-LT/Resources.resw index a912fcfba657..0ebb4c67c357 100644 --- a/src/Files.App/Strings/lt-LT/Resources.resw +++ b/src/Files.App/Strings/lt-LT/Resources.resw @@ -118,16 +118,22 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - New window + Naujas langas - Copy path + Kopijuoti kelio adresÄ… + + + Kopijuoti daikto kelio adresÄ… - Copy path with quotes + Kopijuoti kelio adresÄ… su kabutÄ—mis + + + Kopijuoti pasirinkto elemento keliÄ… su kabutÄ—mis - Naršyti + NarÅ¡yti Dydis @@ -136,37 +142,37 @@ Sukurta: - Path + Vieta Dydis: - Size on disk: + Dydis diske: - Uncompressed size: + Nesuspaustas/Nesuglaudintas dydis: - Ši veiksma negalima atlikti + Å i veiksma negalima atlikti Paskirties aplankas - yra šaltinio aplanko poaplankis + yra Å¡altinio aplanko poaplankis Praleisti - Pažymėti visus + PažymÄ—ti viskÄ… - Invert selection + Invertuoti pasirinkimÄ… - Clear selection + IÅ¡valyti pasirinkimÄ… Modifikuota: @@ -175,145 +181,151 @@ Atidaryta: - Išvalyti visus elementus + IÅ¡valyti visus elementus - Enter a path to navigate to or type ">" to open the command palette + Ä®veskite keliÄ…, kuriuo norite pereiti, arba įraÅ¡ykite ">", kad atvertumÄ—te komandų paletÄ™ - Ieškoti + IeÅ¡koti - Čia bus rodomi failai, kuriuos anksčiau atidarėte + ÄŒia bus rodomi failai, kuriuos anksčiau atidarÄ—te - Remove this item + PaÅ¡alinti šį elementÄ… - GitHub repository + „GitHub“ saugykla Apie - Open source + Atvirasis kodas Datos formatas - Theme + Apipavidalinimas - Show extensions for known file types + Rodyti žinomų failų plÄ—tinius - Show hidden files and folders + Rodyti paslÄ—ptus failus ir aplankus - Show dot files + Rodyti taÅ¡kinius failus - Išvaizda + IÅ¡vaizda - Background color + Fono spalva - Išplėstinė + IÅ¡plÄ—stinÄ— - Continue where you left off + TÄ™sti nuo ten, kur baigÄ—te - Open a new tab + Atidaryti naujÄ… skirtukÄ… - Open a specific page or pages + Atidaryti konkretų puslapį arba puslapius - Desktop + Darbalaukis - Documents + Dokumentai - Downloads + Atsisiuntimai - Name + Pavadinimas/-Ä… - Ascending + DidÄ—jančia tvarka - Paste + Ä®klijuoti + + + Ä®klijuoti nuorodÄ… - New + Kurti naujÄ… - Properties + YpatybÄ—s - Open + Atidaryti/Atverti - Open in new tab + Atidaryti naujame skirtuke - Open in new window + Atidaryti naujame lange - Share + Bendrinti - Cut + IÅ¡kirpti - Delete + IÅ¡trinti - Pin to Sidebar + Prisegti prie Å¡oninÄ—s juostos - Welcome to Files! + Sveiki atvykÄ™ į Files! - Grant permission + Suteikti leidimÄ… - To get started, you'll need to grant us permission to display your files. This will open a Settings page where you can grant us this permission. You'll need to reopen the app once you've completed this step. + NorÄ—dami pradÄ—ti, turÄ—site suteikti mums leidimÄ… rodyti jÅ«sų failus. Bus atidarytas nustatymų puslapis, kuriame galÄ—site suteikti šį leidimÄ…. AtlikÄ™ šį veiksmÄ… turÄ—site iÅ¡ naujo atidaryti programÄ…. - Display + Ekranas - Enter an item name + Ä®veskite elemento pavadinimÄ… - - Set name + + Sukurti naujÄ… – „{0}“ - Light + Å viesus - Dark + Tamsus + + + Naudoti sistemos nustatymÄ… - Paraiška + ParaiÅ¡ka - System + Sistema - New folder + Naujas aplankas - New file + Naujas failas - This folder is empty. + Å is aplankas tuščias. Back (Alt+Left Arrow) @@ -325,118 +337,118 @@ Up (Alt+Up Arrow) - Refresh + Atnaujinti - An item with this name already exists in this directory. + Elementas tokiu pavadinimu Å¡iame kataloge jau yra. - Replace existing item + Pakeisti esamÄ… elementÄ… - Item already exists + Elementas jau yra - Set as lockscreen background + Nustatyti kaip užrakinimo ekrano fonÄ… - Set as app background + Nustatyti kaip programos fonÄ… - We weren't able to delete this item + Nepavyko iÅ¡trinti Å¡io elemento - Please insert the necessary drive to access this item. + Įdėkite reikiamą diską, kad pasiektumėte šį elementą. - Drive unplugged + Diskas atjungtas - The file you are attempting to access may have been moved or deleted. + Failas, kurį bandote pasiekti, gali būti perkeltas arba ištrintas. - Cannot open properties for this file + Negalima atidaryti šio failo ypatybių - The file you are attempting to access may have been moved or deleted. + Failas, kurį bandote pasiekti, gali būti perkeltas arba ištrintas. - File Not Found + Failas nerastas - The folder you are attempting to access may have been moved or deleted. + Aplankas, kurį bandote pasiekti, gali būti perkeltas arba ištrintas. - Did you delete this folder? + Ar ištrynėte šį aplanką? - The file you are attempting to access is currently being used by {0} + Failą, kurį bandote pasiekti, šiuo metu naudoja {0} - The file you are attempting to access is currently being used by another application + Failą, kurį bandote pasiekti, šiuo metu naudoja kita programa - File is in use + Failas yra naudojamas Maketavimas - Read-only + Tik skaityti {0, plural, one {# day ago} other {# days ago}} - 1 day ago + prieš 1 dieną - {0} days ago + Prieš {0} dienų {0, plural, one {# hour ago} other {# hours ago}} - 1 hour ago + prieš 1 val - {0} hours ago + Prieš {0} valandas {0, plural, one {# minute ago} other {# minutes ago}} - 1 minute ago + prieš 1 minutę - {0} minutes ago + Prieš {0} minučių {0, plural, one {# second ago} other {# seconds ago}} - 1 second ago + prieš 1 sekundę - {0} seconds ago + Prieš {0} sekundžių - Now + Dabar - The requested operation is not supported + Prašoma operacija nepalaikoma - {0} free of {1} + {1} nemokamai iš {1} - Send to + Siųsti į - The item name must not contain the following characters: \ / : * ? " < > | + Elemento pavadinime negali būti šių simbolių: \ / : * ? " < > | - The item name specified is invalid + Nurodytas prekės pavadinimas neteisingas {0, plural, one {# item selected} few {# items selected} many {# items selected} other {# items selected}} @@ -447,16 +459,16 @@ ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural - Yes + Taip - Are you sure you want to permanently delete all these items? + Ar tikrai norite visam laikui ištrinti visus šiuos elementus? - Empty recycle bin + Tuščia šiukšlių dėžė - bytes + baitų KB @@ -483,763 +495,784 @@ {0, number} {0, plural, one {file} other {files}}, {1, number} {1, plural, one {folder} other {folders}} from {2, number} {2, plural, one {location} other {locations}} - Run as another user + Vykdykite kaip kitas vartotojas - All type of {0} + Visų tipų {0} - Different types + Skirtingi tipai - All in {0} + Viskas {0} - Used space: + Panaudota erdvė: - Free space: + Laisva vieta: - Capacity: + Talpa: - File system: + Failų sistema: - Recent + Neseniai - Move tab here + Perkelti skirtuką čia - Status - - - Windows numatytasis + Būsena - No results + NÄ—ra rezultatų - Can't access any items to display + Nepavyksta pasiekti jokių rodytinų elementų - Move to {0} + Perkelti į – {0} - Copy to {0} + Kopijuoti į {0} + + + Klonuoti į {0} - Create shortcut + Sukurti nuorodą - Open file location + Atidaryti failo vietą - Arguments: + Argumentai: - Destination: + Paskirtis: - Shortcut type: + Spartaus klavišo tipas: - Web link + Interneto nuoroda Bendra - Shortcut + Nuoroda - Internet shortcut + InternetinÄ— nuoroda - {0} - shortcut + {0} – spartusis klavišas - Files ran into a problem that the developers didn't prepare for yet. + Failai susidūrė su problema, kuriai kūrėjai dar nepasirengė. - Something went wrong! + Kažkas ne taip! - Report this issue + PraneÅ¡ti apie Å¡iÄ… problemÄ… - The item referenced is either invalid or inaccessible.{0}Error message:{0}{1} + Nurodytas elementas yra netinkamas arba neprieinamas.{1}Klaidos pranešimas:{1}{1} - Invalid item + Netinkama prekė - Version: + Versija: - There's nothing to share right now... + Šiuo metu nėra kuo dalintis... - The items you've selected will be shared + Jūsų pasirinkti elementai bus bendrinami - The selected item will be shared + Pasirinktas elementas bus bendrinamas - Sharing {0} + Bendrinimas {0} - Sharing {0} {1} + Bendrinimas {1} {1} - The item you're attempting to rename no longer exists. Please verify the correct location of the item you need to rename. + Elemento, kurį bandote pervardyti, nebėra. Patvirtinkite teisingą elemento, kurį reikia pervardyti, vietą. - Item no longer exists + Elemento nebėra - The name specified was invalid. Please check the desired item name and try again. + Nurodytas pavadinimas neteisingas. Patikrinkite norimo elemento pavadinimą ir bandykite dar kartą. - Invalid item name + Neteisingas prekės pavadinimas - The length of the item name specified exceeds the maximum limit. Please check the desired name and try again. + Nurodytas prekės pavadinimo ilgis viršija didžiausią ribą. Patikrinkite norimą vardą ir bandykite dar kartą. - Item name was too long + Elemento pavadinimas buvo per ilgas - Show more options + Rodyti daugiau parinkčių - The application needs to be restarted in order to apply these settings, would you like to restart the app? + Norint pritaikyti šiuos nustatymus, programą reikia paleisti iš naujo. Ar norėtumėte iš naujo paleisti programą? - Sponsor us on GitHub + Paremkite mus „GitHub“ platformoje - We weren't able to create this item + Nepavyko sukurti šio elemento - Access Denied + Prieiga uždrausta - Set as + Nustatyti kaip - Cancel + Atšaukti - Create a new item + Sukurti naują elementą - Choose a type for this new item below + Toliau pasirinkite šio naujo elemento tipą - File + Failas - Creates an empty file + Sukuria tuščiÄ… failÄ… - Folder + Aplankas - Creates an empty folder + Sukuria tuščią aplanką - Refresh the directory + Atnaujinkite katalogą Kalba - Move shell extensions into a sub menu + Perkelkite apvalkalo plėtinius į antrinį meniu - Show confirmation dialog when deleting items + Rodyti patvirtinimo dialogo langą ištrinant elementus - There was an issue saving some properties. + Išsaugant kai kurias nuosavybes kilo problema. - Error + Klaida - Close anyway + Vis tiek uždaryti - Rating + Įvertinimas - Item Path + Prekės kelias - Item Type + Prekės tipas - Title + Pavadinimas - Subject + Tema - Comment + komentuoti - Copyright + Autorių teisės - Date Modified + Pakeitimo data - Bit Depth + Bitų gylis - Dimensions + Matmenys - Horizontal Size + Horizontalus dydis - Vertical Size + Vertikalus dydis - Horizontal Resolution + Horizontali raiška - Vertical Resolution + Vertikali raiška - Color Space + Spalvų erdvė sRGB - Unspecified + Nenurodyta - Longitude Decimal + Dešimtainė ilguma - Latitude Decimal + Dešimtainė platuma - Altitude + Aukštis virš jūros lygio - Date Taken + Paėmimo data - Camera Manufacturer + Fotoaparato gamintojas - Camera Model + Kameros modelis - Exposure Time + Ekspozicijos laikas - Focal Length + Židinio nuotolis - Aperture + Diafragma - People Names + Žmonių vardai - Channel Count + Kanalų skaičius - Format + Formatas - Sample Rate + Mėginio dažnis - Album Artist + Albumo atlikėjas - Album Title + Albumo pavadinimas - Artist + Menininkas - Beats Per Minute + dūžiai per minutę - Composer + Kompozitorius - Conductor + Dirigentas - Disc Number + Disko numeris - Genre + Žanras - Track Number + Takelio numeris - Duration + TrukmÄ— - Frame Count + Kadrų skaičius - Protection Type + Apsaugos tipas - Author Url + Autoriaus URL - Content Distributor + Turinio platintojas - Date Released + Išleidimo data - Series Name + Serijos pavadinimas - Season Number + Sezono numeris - Episode Number + Epizodo numeris - Producer + Gamintojas - Promotion Url + Reklamos URL - Provider Style + Teikėjo stilius - Publisher + Leidėjas - Thumbnail Large Path + Miniatiūra didelis kelias - Thumbnail Large Uri + Miniatiūra Large Uri - Thumbnail Small Path + Miniatiūros mažas kelias - Thumbnail Small Uri + Miniatiūra Small Uri - User Web Url + Naudotojo žiniatinklio URL - Writer + Rašytojas - Year + Metai - Contributor + Bendraautoris - Last Author + Paskutinis autorius - Revision Number + Pataisos numeris - Version + Versija - Date Created + Sukūrimo data - Total Editing Time + Bendras redagavimo laikas - Template + Šablonas - Word Count + Žodžių skaičius - Character Count + Simbolių skaičius - Line Count + Eilučių skaičius - Paragraph Count + Pastraipų skaičius - Page Count + Puslapių skaičius - Slide Count + Skaidrių skaičius - Frame Rate + Kadrų dažnis - Encoding Bitrate + Kodavimo bitų sparta - Audio Encoding Bitrate + Garso kodavimo bitų sparta - Video Encoding Bitrate + Vaizdo įrašų kodavimo bitų sparta - Compression + Suspaudimas - Frame Width + Rėmo plotis - Frame Height + Rėmo aukštis - Orientation + Orientacija - Core + Šerdis - Image + Vaizdas - Photo + Nuotrauka GPS - Media + Žiniasklaida - Audio + Garsas - Music + Muzika - Video + Vaizdo įrašas - Document + dokumentas - Address + Adresas - Selection options + Pasirinkimo parinktys - Select + Pasirinkite - Recycle bin item + Šiukšliadėžės elementas - Shortcut item + Spartusis elementas - Drives + Pavaros - Move here + Perkelk čia - Safe to remove hardware + Saugu pašalinti aparatinę įrangą - The device can now be safely removed from the computer. + Dabar įrenginį galima saugiai išimti iš kompiuterio. - Problem Ejecting Device + Ä®vyko problema, bandant iÅ¡stumti įrenginį - This device is currently in use. Close any programs, windows or tabs that might be using the device, and then try again. + Šis įrenginys šiuo metu naudojamas. Uždarykite visas programas, langus ar skirtukus, kurie gali naudoti įrenginį, tada bandykite dar kartą. - Eject + IÅ¡stumti - Duplicate tab + Dubliuoti skirtuką - Move tab to new window + Perkelti skirtuką į naują langą - New tab + Naujas skirtukas - Some properties may contain personal information. + Kai kuriose nuosavybėse gali būti asmeninės informacijos. - Clear All Properties + Išvalyti visas ypatybes - Check the status of file operations here + Patikrinkite failų operacijų būseną čia - Status center + Būsenos centras - Search results in {1} for {0} + {0} paieškos rezultatai {0} + + + Paieškos rezultatai pagal `{0}` - Details + Detalės - No + Ne - Show protected system files + Rodyti apsaugotus sistemos failus - Sync layout and sorting preferences across directories + Sinchronizuokite katalogų išdėstymo ir rūšiavimo nuostatas - Customize the right click context menu + Tinkinkite dešiniuoju pelės klavišu esantį kontekstinį meniu - Original path + Originalus kelias - Add + PridÄ—ti - Edit + Redaguoti - Remove + Pašalinti - Open tabs in dual pane mode + Atidarykite skirtukus dviejų langelių režimu - Show option to open folders in a new pane + Rodyti aplankų atidarymo naujoje srityje parinktį - Show option to copy path + Rodyti parinktį kopijuoti kelią - Show option to create folder with selection + Rodyti parinktį sukurti aplanką su pasirinkimu - Show option to create shortcut + Rodyti nuorodą sukurti nuorodą - New pane + Nauja sritis - Open in new pane + Atidaryti naujoje srityje Failai ir aplankai - Details (Ctrl+Shift+1) - - - Tiles (Ctrl+Shift+2) + Išsami informacija (Ctrl + Shift + 1) - Date deleted + Ištrynimo data - Cloud Drives + Debesijos saugykla - Confirm + Patvirtinti - Desired name + Norimas vardas - Toggle the preview pane + Perjungti peržiūros sritį - Toggle the details pane + Perjungti išsamios informacijos sritį - Toggle the details pane to view basic file properties + Perjunkite išsamios informacijos sritį, kad peržiūrėtumėte pagrindines failo ypatybes - Toggle the info pane + Perjungti informacijos sritį - Toggle the info pane to view the detail/preview panes + Perjungti išsamios informacijos / peržiūros sričių matomumą - Toggle the Toolbar + Perjungti įrankių juostą + + + Perjungti įrankių juostos matomumą + + + Perjungti filtro antraštę + + + Perjungti filtro antraštės matomumą + + + Perjungti dvigubą sritį + + + Perjungti dviejų langelių režimą - No preview available + Peržiūra nepasiekiama - Item Name + Prekės pavadinimas - Unpin from Sidebar + Atsegti nuo šoninės juostos - Network + Tinklas - File details + Išsami failo informacija - File preview + Failo peržiūra - Selected file preview pane + Pasirinkto failo peržiūros sritis - Feedback + Atsiliepimai - Available when online + Galimas prisijungus - Documentation + Dokumentacija - Available offline + Galima neprisijungus - Partially available offline + Iš dalies pasiekiama neprisijungus - Syncing + Sinchronizuojama - Excluded from sync + Išskirta iš sinchronizavimo - Not calculated + Neapskaičiuota/-s - Unknown + Nežinoma - If you change a file extension, the file might become unusable. Are you sure you want to change it? + Jei pakeisite failo plėtinį, failas gali tapti netinkamas naudoti. Ar tikrai norite jį pakeisti? - CD ROM Drive + CD ROM įrenginys - Cloud Drive + Debesijos saugykla - Fixed Disk Drive + Fiksuotas disko įrenginys - Floppy Disk Drive + Diskelių įrenginys - Network Drive + Tinklo diskas - Unmounted Drive + Atjungtas diskas - RAM Disk Drive + RAM disko įrenginys - Removable Storage Device + Nuimamas saugojimo įrenginys - Unknown + Nežinoma - Virtual Drive + Virtualus diskas - Date created + Sukūrimo data - More options... + Daugiau parinkčių... - Map network drive + Žemėlapio tinklo diskas - Pinned + Prisegta - Libraries + Bibliotekos - No item selected + Nepasirinktas joks elementas - Target + Tikslas - Arguments + Argumentai - Default + Numatytoji - Item count + Prekių skaičius - This action requires administrator rights + Šiam veiksmui reikalingos administratoriaus teisės - Would you like to continue as administrator? + Ar norėtumėte toliau dirbti administratoriumi? - Columns (Ctrl+Shift+6) + Stulpeliai (Ctrl + Shift + 6) - Pin to the Start Menu + Prisekite prie meniu Pradėti - Unpin from the Start Menu + Atsegti iš meniu Pradėti - Library + Biblioteka - Locations: + Vietos: - Set as default save path + Nustatyti kaip numatytąjį išsaugojimo kelią - No locations + Nėra vietų - Input field cannot be empty! + Įvesties laukas negali būti tuščias! - The name must not contain the following characters: \ / : * ? " < > | + Varde negali būti šių simbolių: \ / : * ? " < > | - Using this name is not allowed! + Šio vardo naudojimas neleidžiamas! - Library with the same name already exists! + Biblioteka tokiu pat pavadinimu jau yra! - Open Storage Sense + Atidarykite „Storage Sense“. + + + „Windows“ nustatymuose atidarykite „Storage Sense“ puslapį + + + Valymas - Copy + Kopijuoti Copy {0, plural, one {item} other {items}} @@ -1248,727 +1281,736 @@ Delete {0, plural, one {item} other {items}} - Move + Judėti - {0, plural, one {One item will be moved} other {# items will be moved}} + {0, plural, one {Vienas elementas bus perkeltas} other {# elementai bus perkelti}} Move {0, plural, one {item} other {items}} - {0, plural, one {One item will be deleted} other {# items will be deleted}} + {0, plural, one {Bus ištrintas vienas elementas} other {Bus ištrinta # elementų}} - Continue + Tęsti - {0, plural, one {There is one conflicting file name} other {There are # conflicting file names}} + {0, plural, one {Yra vienas nesuderinamas failo pavadinimas} other {Yra # nesuderinamų failų pavadinimų}} - {0, plural, one {There is one conflicting file name} other {There are # conflicting file names}}, and {1, plural, one {one outgoing item} other {# outgoing items}} + {0, plural, one {Yra vienas nesuderinamas failo pavadinimas} other {Yra # nesuderinamų failų pavadinimų}} ir {1, plural, one {one outgoing item} other {# outgoing items}} - Conflicting {0, plural, one {file name} other {file names}} + Conflicting {0, plural, one {failo pavadinimas} other {failų pavadinimai}} - {0, plural, one {One item will be copied} other {# items will be copied}} + {0, plural, one {Bus nukopijuotas vienas elementas} other {Bus nukopijuota # elementų}} - Permanently delete + Ištrinti visam laikui - ISO speed + ISO greitis - Replace existing + Pakeiskite esamą - Generate new name + Sukurti naują pavadinimą - Create folder with selection + Sukurkite aplanką su pasirinkimu - Type: + Tipas: - Date modified: + Pakeitimo data: - Open with + Atidaryti naudojant - File icon + Failo piktograma - Original path column + Originalus kelio stulpelis - Item type column + Elemento tipo stulpelis - Date modified column + Keitimo datos stulpelis - Date deleted column + Stulpelio ištrynimo data - Sort + Rūšiuoti - Date modified + Pakeitimo data - Original folder + Originalus aplankas - Descending + Mažėjantis - Huge + Didžiulis - Very large + Labai didelis - Large + Didelis - Medium + Vidutinis - Small + Mažas - Tiny + Mažytis - Future + Ateitis - Today + Å iandien - Yesterday + Vakar - Earlier this week + Anksčiau Å¡iÄ… savaitÄ™ - Last week + PraeitÄ… savaitÄ™ - Earlier this month + Anksčiau šį mėnesį - Last month + Praėjusį mėnesį - Earlier this year + Anksčiau šiais metais - Last year + Praėjusiais metais - Year {0} + Metai {0} - {0} item + {0} prekė - {0} items + {0} prekių - Reopen closed tab + Iš naujo atidaryti skirtuką - Rename + Pervardyti - Privacy + Privatumas - the Recycle Bin + šiukšliadėžę - Canceling + Atšaukiamas - Clear + Aišku - Widgets + Valdikliai - Sync status + Sinchronizavimo būsena - Security + Saugumas - Advanced permissions + Išplėstiniai leidimai - Allow + Leisti - Deny + Neigti - Full control + Pilna kontrolė - List directory contents + Išvardykite katalogo turinį - Modify + Modifikuoti - Permissions for {0} + {0} leidimai - Read and execute + Skaityti ir vykdyti - Read + Skaityti - Group or user names + Grupės arba vartotojų vardai - Write + Rašyti - Unknown account + Nežinoma paskyra - You do not have permissions to view the security properties of this object. Click "Advanced permissions" to proceed. + Neturite leidimo peržiūrėti šio objekto saugos ypatybes. Norėdami tęsti, spustelėkite „Išplėstiniai leidimai“. - Owner: + Savininkas/-Ä—: - Unknown owner + Nežinoma/-s savininkÄ—/-as - Extract archive + Išskleisti archyvą - Open destination folder when complete + Baigę atidarykite paskirties aplanką - Extract to {0}\ + Ištrauka į {0}\ - Create new library + Sukurti naują biblioteką - Restore default libraries + Atkurti numatytąsias bibliotekas - Are you sure you want to restore the default libraries? All your files will remain on your storage. + Ar tikrai norite atkurti numatytąsias bibliotekas? Visi failai liks jūsų saugykloje. - Restore Libraries + Atkurti bibliotekas - Owner + Savininkas/-Ä— - Special + Specialusis - Access + Prieiga - Applies to + Taikoma - Principal + direktorius - files + failai - this folder + šį aplanką - subfolders + poaplankius - Inherited + Paveldima - Permissions + Leidimai - You do not have permissions to view the security properties of this object. You can try to take ownership of this object. + Neturite leidimo peržiūrėti šio objekto saugos ypatybes. Galite pabandyti perimti šio objekto nuosavybę. - This folder and files + Šis aplankas ir failai - This folder, subfolders and files + Šis aplankas, poaplankiai ir failai - Only files + Tik failai - Only subfolders + Tik poaplankiai - Only subfolders and files + Tik poaplankiai ir failai - Only this folder + Tik šis aplankas - This folder and subfolders + Šis aplankas ir poaplankiai - Append data + Pridėti duomenis - Change permission + Keisti leidimą - Create folders + Sukurti aplankus - Create files + Sukurti failus - Delete subdirectories and files + Ištrinkite pakatalogius ir failus - Execute files + Vykdyti failus - Read attributes + Skaityti atributus - Read data + Skaityti duomenis - Read extended attributes + Skaityti išplėstinius atributus - Read permissions + Skaitymo leidimai - Take ownership + Priimk nuosavybę - Visit folder + Aplankyti aplanką - Write attributes + Rašyti atributus - Write data + Rašyti duomenis - Write extended attributes + Parašykite išplėstinius atributus - Convert inherited permissions into explicit permissions + Konvertuoti paveldėtus leidimus į aiškius leidimus - Enable inheritance + Įgalinti paveldėjimą - Remove all inherited permissions + Pašalinkite visus paveldėtus leidimus - Reset child permissions + Iš naujo nustatyti vaiko leidimus - Replace all child object permissions entries with inheritable permission entries from this object + Pakeiskite visus antrinio objekto leidimų įrašus paveldimomis šio objekto leidimo įrašais - Close active pane + Uždaryti langą - Enter compact overlay + Įveskite kompaktišką perdangą - Exit compact overlay + Išeikite iš kompaktiškos perdangos - File description + Failo aprašymas - Company + Ä®monÄ— Kalba - Trademarks + Prekių ženklai - File version + Failo versija - Load full preview + Įkelti visą peržiūrą - Downloads the item from the cloud and loads the preview + Atsisiunčiamas elementas iš debesies ir įkeliama peržiūra {0, plural, one {# item} other {# items}} ({1, plural, one {# file} other {# files}}, {2, plural, one {# folder} other {# folders}}) - Uncompressed size + Nesuspaustas dydis - WSL + „WSL“ - Hide {0} section + Slėpti {0} skyrių - Add file + PridÄ—ti failÄ… - Updates available + Galimi atnaujinimai - Updates are ready to install + Atnaujinimai paruošti diegti - Close + Uždaryti - Update + Atnaujinti - Home - - - Ctrl+Shift+1 - - - Ctrl+Shift+2 - - - Ctrl+Shift+6 + Namai - The archive extraction completed successfully. + Archyvo ištraukimas sėkmingai baigtas. - Extracting archive + Ištraukiamas archyvas - Extracting complete! + Ištraukimas baigtas! - Open parent folder + Atidarykite pirminį aplanką - Unknown + Nežinoma - Edit tags + Redaguoti žymas - Part of set + Komplekto dalis - Open drive + Atidarykite diską - Please insert a disc into drive {0} + Įdėkite diską į įrenginį {0} - Insert a disc + Įdėkite diską - OK + Gerai/Supratau - Credential required + Reikalingas kredencialas - Anonymous + Anonimas - Provide your credential: + Pateikite savo kredencialus: - UserName + Vartotojo vardas - No items found + Elementų nerasta - Open log location + Atidaryti žurnalo vietą - Create link in {0} + Sukurti nuorodą {0} - Columns + Stulpeliai - - Tiles + + Kortelės - Open folders in new tab + Atidarykite aplankus naujame skirtuke - Status center + Būsenos centras - Date created column + Sukūrimo datos stulpelis - Item size column + Prekės dydžio stulpelis - Experimental feature flags + Eksperimentinės funkcijos vėliavėlės - Help and support + Pagalba ir palaikymas - Submit feature request + Pateikite funkcijos užklausą - Submit bug report + Pateikite pranešimą apie riktą - Set Files as the default file manager + Nustatykite Failai kaip numatytąją failų tvarkyklę - Use Files as Open File Dialog + Naudokite failus kaip atidaryto failo dialogo langą - Tag + Žyma - Open items with a single click + Atidarykite failus vienu paspaudimu - Folder path + Aplanko kelias - Export settings + Eksportuoti nustatymus - Import settings + Importuoti nustatymus - Couldn't import settings. The settings file is corrupted. + Nepavyko importuoti nustatymų. Nustatymų failas yra korumpuotas. - Error importing settings + Ä®vyko klaida importuojant nustatymus - Empty Recycle Bin + Ištuštinkite šiukšliadėžę - Sidebar + Šoninė juosta - Choose a custom folder icon + Pasirinkite tinkinto aplanko piktogramą - Customization + Tinkinimas - Restore default + Atkurti numatytuosius nustatymus - Open tab in existing instance when opening Files from another app + Atidarykite skirtuką esamame egzemplioriuje, kai atidarote failus iš kitos programos Paleidimo nustatymai - Compatibility + Suderinamumas - None + Nėra - On Windows login + Prisijungus prie „Windows“. - On this program start + Paleiskite šią programą - System + Sistema - System (Enhanced) + Sistema (patobulinta) - 16-bit (65536) color + 16 bitų (65536) spalva - 8-bit (256) color + 8 bitų (256) spalva - Disable full-screen optimizations + Išjungti viso ekrano optimizavimą - Run in 640 x 480 screen resolution + Veikia 640 x 480 ekrano raiška - Override high DPI scaling behavior + Nepaisyti didelio DPI mastelio keitimo elgesio - Reduced color mode + Sumažintas spalvų režimas - Register this program for restart + Užregistruokite šią programą, kad galėtumėte paleisti iš naujo + + + Pradžios langas + + + Normalus langas + + + Sumažintas + + + Maksimalus - Run as administrator + Vykdyti kaip administratorius - Run compatibility troubleshooter + Paleiskite suderinamumo trikčių šalinimo įrankį - Use DPI settings of the main monitor + Naudokite pagrindinio monitoriaus DPI nustatymus - Do not adjust DPI + Nereguliuokite DPI - Do not override DPI + Nepaisykite DPI - Compatibility mode + Suderinamumo režimas - No reduced color + Nėra sumažintos spalvos - Third party libraries + Trečiųjų šalių bibliotekos - This option modifies the system registry and can have unexpected side effects on your device. Continue at your own risk. + Ši parinktis pakeičia sistemos registrą ir gali turėti netikėtų šalutinių poveikių jūsų įrenginyje. Tęskite savo rizika. + + + Išlyginimo operacijos yra nuolatinės ir nerekomenduojamos. Tęskite savo rizika. - Create Library + Sukurti bibliotekÄ… - Enter Library name + Ä®veskite bibliotekos pavadinimÄ… - Calculate folder sizes + Apskaičiuokite aplankų dydžius - Recent files + Naujausi failai - Open Files on Windows startup + Atidarykite „Files“ paleidę „Windows“. - Attributes + Atributai - Hidden + Paslėpta - More details + Daugiau informacijos - Read only + Tik skaityti - Cleanup your drive contents + Išvalykite disko turinį - Type + Tipas/-Ä… - Bytes + Baitai - Extract + IÅ¡skleisti - Extract files + IÅ¡skleisti failus - Extract here + IÅ¡skleisti čia - Ctrl+E + Vald.+E - Extract to {0} + Ištrauka į {0} - Run script + Vykdykite scenarijų - Run with PowerShell + Vykdyti su „PowerShell“ - Calculating folder sizes is resource intensive and may cause your CPU usage to increase. + Aplanko dydžių skaičiavimas reikalauja daug išteklių, todėl gali padidėti procesoriaus naudojimas. - Rotate left + Pasukti į kairę - Rotate right + Pasukite į dešinę - Install + Ä®diegti - Close tabs to the left + Uždarykite skirtukus kairėje - Close tabs to the right + Uždarykite skirtukus dešinėje - Close other tabs + Uždarykite kitus skirtukus + + + Uždarykite visus skirtukus - Music + Muzika - Pictures + Paveikslėliai - Update Files + Atnaujinti failus - Žymos + Žymos - Universal + Universalus - ex: {0}, {1} + Pvz.: {0}, {1} - Show thumbnails + Rodyti miniatiÅ«ras - Retry + Bandykite dar kartą - Move operation is not supported in this context. Do you want to copy the items instead? + Perkėlimo operacija šiame kontekste nepalaikoma. Ar vietoj to norite kopijuoti elementus? - Hidden items + Paslėpti daiktai - Custom + Pasirinktinis - Set as desktop slideshow + Nustatyti kaip darbalaukio skaidrių demonstraciją - Size all columns to fit + Nustatykite visų stulpelių dydį, kad tilptų Automatically choose the best layout @@ -1977,133 +2019,181 @@ Adaptive (Ctrl+Shift+7) - Ctrl+Shift+7 + Ctrl + Shift + 7 - Show alternate data streams + Rodyti alternatyvius duomenų srautus - Behaviors + Elgesys + + + Sveiki! + + + Mėgaukitės failais? Apsvarstykite galimybę peržiūrėti „Microsoft Store“. - - Review Files + + Mėgaukitės failais? Apsvarstykite galimybę paremti projektą „GitHub“. - - Would you like to review Files? + + Rėmėjas + + + Ä®vertinkite mus + + + Atsisakyti - Set as background + Nustatyti kaip foną - Set as desktop background + Nustatyti kaip darbalaukio foną - Set as default + Nustatyti kaip numatytąjį - Date column + Datos stulpelis - Date created column + Sukūrimo datos stulpelis - Size column + Dydžio stulpelis - Tag column + Žymos stulpelis - Type column + Įveskite stulpelį - Lockscreen + Užrakinimo ekranas - - Open folders with a single click in the Columns Layout + + Atidarykite aplankus vienu paspaudimu - Opening items + Elementų atidarymas - Compress + Suspausti + + + Perkelkite visą turinį iš poaplankių į pasirinktą vietą + + + Išlyginti aplanką + + + Išlyginti + + + Išlyginus aplanką, visas jo poaplankių turinys bus perkeltas į pasirinktą vietą. Ši operacija yra nuolatinė ir jos anuliuoti negalima. Naudodami šią eksperimentinę funkciją pripažįstate riziką ir sutinkate nelaikyti Failų komandos atsakinga už duomenų praradimą. + + + Rodyti išlyginimo parinktis - Select files and folders when hovering over them + Užvesdami pelės žymeklį virš jų pasirinkite failus ir aplankus - Are you sure you want to restore all items in the recycle bin? + Ar tikrai norite atkurti visus šiukšliadėžėje esančius elementus? - Restore all items + Atkurti visus elementus - Do you want to restore the {0} selected item(s)? + Do you want to restore the {0, plural, one {selected item} other {{0} selected items}}? - Restore selection + Atkurti pasirinkimą - Restore All Items + Atkurti visus elementus - Restore + Atkurti - Shortcut cannot be opened + Spartusis klavišas negali būti atidarytas - The target destination cannot be found {0}. Do you want to delete this shortcut? + Tikslinės paskirties vietos nepavyko rasti {0}. Ar norite ištrinti šį spartųjį klavišą? - You don't have permission to access this folder. + Neturite leidimo pasiekti šio aplanko. - Archive password + Archyvuoti slaptažodį + + + Kodavimas + + + {0} (aptikta) - Path + Kelias - Sorting and grouping + Rūšiavimas ir grupavimas - Create archive + Sukurti archyvą - Create + Sukurti - Create {0} + Sukurti {0} - Enter a name + Ä®veskite pavadinimÄ… - Compression level + Suspaudimo lygis Ultra - High + Aukštas - Normal + Normalus - Low + Žemas - Fast + Greitai - None + Nėra - Splitting size + Padalijimo dydis + + + Neskaldyti + + + Automatinis + + + Žodyno dydis + + + Žodžio dydis + + + Numatomas atminties naudojimas: {0} - - Do not split + + Prieinama atmintis: {0} CD @@ -2112,584 +2202,614 @@ DVD - FAT + „FAT“ - Blu-ray + „Blu-ray“ - Encryption + Å ifravimas - Password + Slaptažodis - Format + Formatas - Sync status column + Sinchronizavimo būsenos stulpelis - Group by + Grupuoti pagal - Sort by + Rūšiuoti pagal - Group in descending order + Grupuoti mažėjančia tvarka - Sort in descending order + Rūšiuoti mažėjančia tvarka - Create a new shortcut + Sukurkite naują nuorodą - Create shortcuts to local or network programs, files, folders, computers or Internet addresses. + Kurkite nuorodas į vietines arba tinklo programas, failus, aplankus, kompiuterius ar interneto adresus. - Enter the location of the item: + Įveskite prekės vietą: - Creates a shortcut + Sukuria nuorodÄ… - Recently used files is currently disabled in Windows File Explorer. + Neseniai naudoti failai šiuo metu yra išjungti „Windows File Explorer“. - Edit settings file + Redaguoti nustatymų failÄ… - - What's new + + Atidarykite nustatymų failą numatytojoje redagavimo priemonėje + + + Išleidimo pastabos + + + Atidarykite laidos pastabas - Creating a shortcut in this location requires administrator privileges + Norint sukurti nuorodą šioje vietoje, reikia administratoriaus teisių - Would you like to create the shortcut on the desktop instead? + Ar norėtumėte sukurti spartųjį klavišą darbalaukyje? - The specified item name is invalid + Nurodytas elemento pavadinimas neteisingas Nustatymas - Double click on a blank space to go up one directory + Dukart spustelėkite tuščią vietą, kad pakiltumėte vienu katalogu - View more + Žiūrėti daugiau - Show edit tags flyout + Rodyti redagavimo žymas - Show compression options + Rodyti suspaudimo parinktis - Show send to menu + Rodyti siuntimo į meniu - Show option to open folders in a new tab + Rodyti parinktį atidaryti aplankus naujame skirtuke - Show option to open folders in a new window + Rodyti parinktį atidaryti aplankus naujame lange - Quick access + Greita prieiga - Enter your credentials to connect to: {0} + Įveskite savo kredencialus, kad prisijungtumėte prie: {0} - Enter network credentials + Įveskite tinklo kredencialus - Remember my credentials + Prisiminkite mano kredencialus - Network folder error + Tinklo aplanko klaida - App version + Programos versija - Windows version + „Windows“ versija - Always + Visada - Permanent deletion only + Tik visam laikui ištrynimas - Never + Niekada - Tag color + Žymės spalva - New tag + Nauja žyma - Create new tag + Sukurti naują žymą - Loading... + Įkeliama... - Context menu options + Kontekstinio meniu parinktys - Show file extensions - - - Show hidden items + Failų plėtiniai - Format... + Formatas - Help + Pagalba - Toggle full screen + Visas ekranas - Are you sure you want to delete this tag? + Ar tikrai norite ištrinti šią žymą? - Play all + Žaisti - Height + Aukštis - Width + Plotis - Apply this action to all conflicting items + Taikykite šį veiksmą visiems nesuderinamiems elementams - Open in Windows Terminal + Atidarykite „Windows“ terminale - Open in Windows Terminal as administrator + Atidarykite „Windows“ terminale kaip administratorius - Save + IÅ¡saugoti - Multiselect + Keli pasirinkimai - Reorder sidebar items + Pertvarkykite šoninės juostos elementus - Hashes + Maišos - An error occurred during the calculation + Skaičiuojant įvyko klaida - Failed to calculate the hash, please close the file and try again. + Nepavyko apskaičiuoti maišos, uždarykite failą ir bandykite dar kartą. - Hashes aren't available for online files + Internetinių failų maišos negalima - Algorithm + Algoritmas - Hash value + Maišos vertė - Select hashes to show + Pasirinkite maišą, kurią norite rodyti - Calculating... + Apskaičiuojama... - Pinned items + Prisegti elementai - Decrease size + Sumažinti dydį - Increase size + Padidinti dydį - Toggle sort direction + Rūšiavimo kryptis - Invalid name + Neteisingas pavadinimas - Name must not be empty or start or end with a period. + Pavadinimo laukas negali būti tuščias ir negali prasidėti ar baigtis tašku. - Videos + Vaizdo įraÅ¡ai - Launch preview popup + Iššokantis peržiūros langas - Toggle compact overlay + Perjungti kompaktišką perdangą - Open online help page in browser + Naršyklėje atidarykite internetinį pagalbos puslapį - Toggle full screen + Perjungti viso ekrano režimą - Enter compact overlay + Įjunkite kompaktiško perdangos režimą - Exit compact overlay + Išjunkite kompaktiškos perdangos režimą - Toggle compact overlay + Perjungti kompaktišką perdangos režimą - Go to search box + Pradėkite paiešką „OmniBar“. - Toggle whether to show hidden items + Perjungti paslėptų elementų matomumą + + + Perjungti taškų failų matomumą - Toggle whether to show file extensions + Perjungti failų plėtinių matomumą - Toggle the preview pane to view file previews + Perjunkite peržiūros sritį, kad peržiūrėtumėte failų peržiūras - Toggle whether to show sidebar + Perjungti, ar rodyti šoninę juostą - Copy item(s) to clipboard + Kopijuoti selected {0, plural, one {item} other {items}} - Copy path of selected items to the clipboard + Nukopijuokite dabartinio katalogo kelią + + + Nukopijuokite pasirinktų elementų kelią + + + Nukopijuokite pasirinktų elementų kelią su kabutėmis - Copy path of selected items with quotes to the clipboard + Nukopijuokite dabartinio katalogo kelią su kabutėmis - Cut item(s) to clipboard + Iškirpti selected {0, plural, one {item} other {elementus}} - Paste item(s) from clipboard to current folder + Įklijuokite elementus į dabartinį aplanką + + + Įklijuokite elementus į dabartinį aplanką kaip nuorodas - Paste item(s) from clipboard to selected folder + Įklijuokite elementus į pasirinktą aplanką + + + Įklijuoti į pasirinktą aplanką - Delete item(s) + Ištrinti selected {0, plural, one {item} other {items}} - Create new folder + Sukurti naują aplanką - Create new shortcut(s) to selected item(s) + Create new {0, plural, one {shortcut} other {shortcuts}} to selected {0, plural, one {item} other {items}} - Create new shortcut to any item + Sukurkite naują bet kurio elemento nuorodą - Empty recycle bin + Ištuštinkite šiukšliadėžės turinį - Open "Format Drive" menu for selected item + Atidarykite pasirinkto elemento meniu „Formatuoti diską“. - Restore selected item(s) from recycle bin + Restore selected {0, plural, one {item} other {items}} from recycle bin - Restore all items from recycle bin + Atkurkite visus elementus iš šiukšliadėžės - Open item(s) + Open {0, plural, one {item} other {items}} - Open item(s) with selected application + Open {0, plural, one {item} other {items}} su pasirinkta programa - Open parent folder of searched item + Atidaryti pirminį ieškomo elemento aplanką - Refresh page contents + Atnaujinkite puslapio turinį - Rename selected item + Pervardykite pasirinktą elementą - Select all items + Pasirinkite visus elementus - Invert item selection + Apverskite pasirinktus elementus - Clear item selection + Išvalyti pasirinktus elementus - Toggle item selection + Perjungti elemento pasirinkimą - Share selected file(s) with others + Bendrinkite selected {0, plural, one {failą} other {failus}} su kitais - Pin item(s) to the Start Menu + Pin {0, plural, one {item} other {items}} į meniu Pradėti - Unpin item(s) from the Start Menu + Unpin {0, plural, one {item} other {items}} iš meniu Pradėti - Pin folder(s) to Sidebar + Pin {0, plural, one {folder} other {aplankai}} į šoninę juostą - Unpin folder(s) from Sidebar + Unpin {0, plural, one {aplankas} other {folders}} iš šoninės juostos - Set selected picture as desktop background + Nustatyti pasirinktą paveikslėlį kaip darbalaukio foną - Set selected pictures as desktop slideshow + Nustatyti pasirinktas nuotraukas kaip darbalaukio skaidrių demonstraciją - Set selected picture as lockscreen background + Nustatykite pasirinktą paveikslėlį kaip užrakinimo ekrano foną - Set selected picture as the app background + Nustatykite pasirinktą paveikslėlį kaip programos foną + + + Ä®diegti Å¡riftÄ… + + + Ä®diegti tvarkyklÄ™ + + + Ä®diegti sertifikatÄ… - Install selected font(s) + Įdiekite selected {0, plural, one {font} other {fonts}} - Install driver(s) using selected inf file(s) + Install {0, plural, one {driver} other {drivers}} using selected inf {0, plural, one {file} other {files}} - Install selected certificate(s) + Install selected {0, plural, one {certificate} other {certificates}} - Run selected application as administrator + Paleiskite pasirinktą programą kaip administratorių - Run selected application as another user + Paleiskite pasirinktą programą kaip kitą vartotoją - Run selected PowerShell script + Paleiskite pasirinktą „PowerShell“ scenarijų - Launch preview in popup window + Iššokančiajame lange paleiskite peržiūrą - Create archive with selected item(s) + Create archive with selected {0, plural, one {item} other {items}} - Create 7z archive instantly with selected item(s) + Create 7z archive with selected {0, plural, one {item} other {items}} - Create zip archive instantly with selected item(s) + Create zip archive with selected {0, plural, one {item} other {items}} - Extract items from selected archive(s) to any folder + Ištrauka selected {0, plural, one {archyvas} other {archives}} į bet kurį aplanką - Extract items from selected archive(s) to current folder + Ištrauka selected {0, plural, one {archyvas} other {archives}} į dabartinį aplanką - Extract items from selected archive(s) to new folder + Ištrauka selected {0, plural, one {archyvas} other {archives}} į naują aplanką - Rotate selected image(s) to the left + Rotate selected {0, plural, one {image} other {images}} to the left - Rotate selected image(s) to the right + Rotate selected {0, plural, one {image} other {images}} to the right - Open settings page + Atidaryti nustatymų puslapį - Open folder in terminal + Atidarykite aplanką terminale - Open folder in terminal as administrator + Atidarykite aplanką terminale kaip administratorius - Decrease item size in the current view + Sumažinti elemento dydį dabartiniame rodinyje - Increase item size in the current view + Padidinti elemento dydį dabartiniame rodinyje - Switch to details view + Perjungti į išsamios informacijos rodinį - - Switch to tiles view + + Perjungti į kortelių rodinį - Switch to list view + Perjungti į sąrašo rodinį - List + Sąrašas - Grid + Tinklelis - Switch to grid view + Perjungti į tinklelio rodinį - Switch to columns view + Perjungti į stulpelių rodinį - Switch views adaptively + Adaptyviai perjunkite rodinius - Sort items by name + Rūšiuoti elementus pagal pavadinimą - Sort items by date modified + Rūšiuoti elementus pagal modifikavimo datą - Sort items by date created + Rūšiuoti elementus pagal sukūrimo datą - Sort items by size + Rūšiuoti prekes pagal dydį - Sort items by type + Rūšiuoti elementus pagal tipą - Sort items by sync status + Rūšiuoti elementus pagal sinchronizavimo būseną - Sort items by tags + Rūšiuoti elementus pagal žymas - Sort items by original folder + Rūšiuoti elementus pagal pradinį aplanką - Sort items by date deleted + Rūšiuoti elementus pagal ištrynimo datą - Sort items in ascending order + Rūšiuoti elementus didėjančia tvarka - Sort items in descending order + Rūšiuoti elementus mažėjančia tvarka - Toggle item sort direction + Perjungti elementų rūšiavimo kryptį - List items without grouping + Išvardykite elementus be grupavimo - Group items by name + Grupuokite elementus pagal pavadinimą - Group items by date modified + Grupuokite elementus pagal modifikavimo datą - Group items by date created + Grupuokite elementus pagal sukūrimo datą - Group items by size + Grupuokite elementus pagal dydį - Group items by type + Grupuokite elementus pagal tipą - Group items by sync status + Grupuokite elementus pagal sinchronizavimo būseną - Group items by tags + Grupuokite elementus pagal žymas - Group items by original folder + Grupuokite elementus pagal pradinį aplanką - Group items by date deleted + Grupuokite elementus pagal ištrynimo datą - Group items by folder path + Grupuokite elementus pagal aplanko kelią - Sort groups in ascending order + Rūšiuoti grupes didėjančia tvarka - Sort groups in descending order + Rūšiuoti grupes mažėjančia tvarka - Toggle group sort direction + Perjungti grupės rūšiavimo kryptį - Open new tab + Atidaryti naujÄ… skirtukÄ… - Navigate backward in navigation history + Naršykite atgal - Navigate forward in navigation history + Eikite į priekį - Navigate up one directory + Eikite į vieną katalogą aukštyn - Duplicate current tab + Dubliuoti dabartinį skirtuką - Duplicate selected tab + Dubliuoti pasirinktą skirtuką - Close tabs to the left of current tab + Uždarykite skirtukus, esančius dabartinio skirtuko kairėje - Close tabs to the left of selected tab + Uždarykite skirtukus, esančius pasirinkto skirtuko kairėje - Close tabs to the right of current tab + Uždarykite skirtukus, esančius dabartinio skirtuko dešinėje - Close tabs to the right of selected tab + Uždarykite skirtukus, esančius pasirinkto skirtuko dešinėje - Close tabs other than current tab + Uždarykite kitus skirtukus nei dabartinis - Close tabs other than selected tab + Uždarykite kitus nei pasirinktus skirtukus + + + Uždarykite visus skirtukus, įskaitant dabartinį skirtuką - Reopen last closed tab + Iš naujo atidaryti neseniai uždarytą skirtuką - Move to the previous tab + Pereiti į ankstesnį skirtuką - Move to the next tab + Pereikite į kitą skirtuką - Close current tab + Uždaryti dabartinį skirtuką - Close active pane + Uždarykite aktyvią sritį - Focus other pane + Sufokusuokite kitą sritį - Switch focus to the non active pane + Perjunkite židinį į kitą sritį - Toggle the sidebar + Perjungti šoninę juostą Alt Key name for hotkeys in menus. Use abbreviation if possible. - Ctrl + Vald. Key name for hotkeys in menus. Use abbreviation if possible. @@ -2697,15 +2817,15 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Win + „Win“ Key name for hotkeys in menus. Use abbreviation if possible. - Enter + Įeikite Key name for hotkeys in menus. Use abbreviation if possible. - Space + Erdvė Key name for hotkeys in menus. Use abbreviation if possible. @@ -2717,7 +2837,7 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Tab + Skirtukas Key name for hotkeys in menus. Use abbreviation if possible. @@ -2729,15 +2849,15 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Left + KairÄ— Key name for hotkeys in menus. Use abbreviation if possible. - Right + DeÅ¡inÄ— Key name for hotkeys in menus. Use abbreviation if possible. - Down + Žemyn Key name for hotkeys in menus. Use abbreviation if possible. @@ -2745,15 +2865,15 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Home + Namai Key name for hotkeys in menus. Use abbreviation if possible. - End + Pabaiga Key name for hotkeys in menus. Use abbreviation if possible. - PageDown + Puslapis žemyn Key name for hotkeys in menus. Use abbreviation if possible. @@ -2761,51 +2881,51 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Sep + rugsėjis Key name for hotkeys in menus. Use abbreviation if possible. - Pause + Pauzė Key name for hotkeys in menus. Use abbreviation if possible. - Sleep + Miegoti Key name for hotkeys in menus. Use abbreviation if possible. - Clear + Aišku Key name for hotkeys in menus. Use abbreviation if possible. - Print + Spausdinti Key name for hotkeys in menus. Use abbreviation if possible. - Help + Pagalba Key name for hotkeys in menus. Use abbreviation if possible. - Mouse4 + Pelė 4 Key name for hotkeys in menus. Use abbreviation if possible. - Mouse5 + Pelė 5 Key name for hotkeys in menus. Use abbreviation if possible. - App + Programėlė Key name for hotkeys in menus. Use abbreviation if possible. - App1 + Programa1 Key name for hotkeys in menus. Use abbreviation if possible. - App2 + Programa2 Key name for hotkeys in menus. Use abbreviation if possible. - Mail + paštas Key name for hotkeys in menus. Use abbreviation if possible. @@ -2817,7 +2937,7 @@ Key name for hotkeys in menus. Use abbreviation if possible. - GoForward + Pirmyn Key name for hotkeys in menus. Use abbreviation if possible. @@ -2829,11 +2949,11 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Ieškoti + IeÅ¡koti Key name for hotkeys in menus. Use abbreviation if possible. - Favorites + Mėgstamiausi Key name for hotkeys in menus. Use abbreviation if possible. @@ -2841,7 +2961,7 @@ Key name for hotkeys in menus. Use abbreviation if possible. - MediaStop + „MediaStop“. Key name for hotkeys in menus. Use abbreviation if possible. @@ -2849,7 +2969,7 @@ Key name for hotkeys in menus. Use abbreviation if possible. - NextTrack + Kitas takelis Key name for hotkeys in menus. Use abbreviation if possible. @@ -2857,7 +2977,7 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Mute + Nutildyti Key name for hotkeys in menus. Use abbreviation if possible. @@ -2869,447 +2989,465 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Change + Keisti - Toggle selection + Perjungti pasirinkimą - Show warning when changing file extensions + Rodyti įspėjimą keičiant failų plėtinius - This PC + Šis kompiuteris - Recycle Bin + Šiukšliadėžė - Untagged + Nepažymėta - Moves to the previous tab + Ankstesnis skirtukas - Moves to the next tab + Kitas skirtukas - Closes current tab + Uždaryti skirtuką - Edit path + Redaguoti kelią - Redo + Perdaryti - Undo + Anuliuoti - Invalid location + Netinkama vieta - Are you sure you want to copy these files without their properties? + Ar tikrai norite kopijuoti šiuos failus be jų savybių? - These files have properties that can't be copied to the new location + Šie failai turi savybių, kurių negalima nukopijuoti į naują vietą - These files have properties that can't be moved to the new location + Šie failai turi savybių, kurių negalima perkelti į naują vietą - Are you sure you want to move these files without their properties? + Ar tikrai norite perkelti šiuos failus be jų savybių? - Show checkboxes when selecting items + Rodyti žymimuosius laukelius renkantis elementus - Focus path bar + Redaguokite kelią „OmniBar“. - Create new item + Sukurti naują elementą - No groups or users have permission to access this object. However, the owner of this object can assign permissions. + Jokios grupės ar vartotojai neturi leidimo pasiekti šį objektą. Tačiau šio objekto savininkas gali priskirti leidimus. - Open the item's location + Atidarykite elemento vietą - Delete permanently + Ištrinti visam laikui - Delete item(s) permanently + Ištrinti selected {0, plural, one {item} other {elementus}} visam laikui - Play the selected media files + Leisti pasirinktus medijos failus - Undo the last file operation + Anuliuoti paskutinę failo operaciją - Redo the last file operation + Pakartokite paskutinę failo operaciją - Location: + VietovÄ—: - Group items by month + Grupuokite elementus pagal mėnesį - Group items by year + Grupuokite elementus pagal metus - Month + Mėnuo - Day + Diena - Toggle unit for grouping by date + Perjungti grupavimo pagal datą vienetą - Toggle grouping unit + Perjungti grupavimo vienetą - Year + Metai - Group by date unit + Grupuoti pagal datos vienetą - Group items by day of date created + Grupuokite elementus pagal sukūrimo datą - Group items by month of date created + Grupuokite elementus pagal sukūrimo datos mėnesį - Group items by year of date created + Grupuokite elementus pagal sukūrimo datos metus - Group items by day of date deleted + Grupuokite elementus pagal ištrynimo datą - Group items by month of date deleted + Grupuokite elementus pagal ištrynimo datos mėnesį - Group items by year of date deleted + Grupuokite elementus pagal ištrynimo datos metus - Group items by day of date modified + Grupuokite elementus pagal pakeitimo datą - Group items by month of date modified + Grupuokite elementus pagal modifikavimo datos mėnesį - Group items by year of date modified + Grupuokite elementus pagal modifikavimo datos metus - Click 'Advanced permissions' to continue. + Norėdami tęsti, spustelėkite „Išplėstiniai leidimai“. - You must have Read permissions to view the properties of this item. + Norėdami peržiūrėti šio elemento ypatybes, turite turėti skaitymo teises. - To try taking ownership of the item, which includes permission to view its properties, click Change above. + Norėdami pabandyti perimti elemento nuosavybės teisę, įskaitant leidimą peržiūrėti jo ypatybes, aukščiau spustelėkite Keisti. - Unable to display permissions. + Neįmanoma parodyti leidimų. - Leave my changes on '{0}' + Palikite mano pakeitimus '{0}' - Discard my changes + Atmesti mano pakeitimus - Bring my changes to '{0}' + Atnešti mano pakeitimus į '{0}' - You have uncommitted changes on this branch. What would you like to do with them? + Šioje šakoje turite nepadarytų pakeitimų. Ką norėtum su jais veikti? + + + Jūs nuolat susiliejate su neišspręstais konfliktais. Išspręskite konfliktus arba nutraukite sujungimą, kad perjungtumėte filialus. + + + Nutraukti sujungimą ir perjungti į '{0}' + + + Laikykitės „{0}“ ir išspręskite konfliktus - Switch branch + Perjungti šaką - Branches + Filialai - Switch + Perjungti - New branch + Naujas filialas - Invalid branch name + Neteisingas filialo pavadinimas - Create branch + Sukurti filialą - Create branch + Sukurti filialą - Based on + Remiantis - Switch to new branch + Perjungti į naują filialą - Create a folder with the currently selected item(s) + Create a folder with the currently selected {0, plural, one {item} other {items}} - Open properties + YpatybÄ—s + + + Atidarykite „File Explorer“ ypatybes - Open properties window + Atidarykite savybių langą + + + Atidarykite „File Explorer“ ypatybių langą - Locals + Vietiniai - Remotes + Nuotolinio valdymo pultai - Translate on Crowdin + Verskite „Crowdin“ platformoje - Fetch + Atnešti - Pull + Traukti - Run git fetch + Paleiskite git fetch + + + Klonuoti git repo - Run git pull + Paleiskite git pull - Preview + Peržiūra - Git status + Git statusas - Git error + „Git“ klaida - git pull failed due to a timeout error + git pull nepavyko dėl skirtojo laiko klaidos - (multiple values) + (kelios reikšmės) Text indicating that multiple selected files have different metadata values. - Author + Autorius - Date committed + Įsipareigojimo data - Commit message + Įsipareigoti pranešimą - Commit SHA + Įsipareigokite SHA Git - Acrylic + Akrilas - Mica + Žėrutis - Mica Alt + Žėrutis Alt - Backdrop Material + Fonas - Solid + Tvirtas - Manage branches + Tvarkyti filialus - Push + Stumti - Run git push + Paleiskite git push - Sync + Sinchronizuoti - Run git pull and then git push + Vykdykite git pull ir tada git push - {0} outgoing / {1} incoming commits + {1} išeinantys / {1} gaunami įsipareigojimai - Connect to GitHub + Prisijunkite prie „GitHub“. - Enter the following code at the link below to start working with GitHub repositories. + Toliau pateiktoje nuorodoje įveskite šį kodą, kad pradėtumėte dirbti su „GitHub“ saugyklomis. - Files cannot access GitHub right now. + Šiuo metu failai negali pasiekti „GitHub“. - - Open folder in VS Code + + Atidaryti aplanką {0} - - Open the current directory in Visual Studio Code + + Atidarykite dabartinį katalogą {0} - - Open repo in VS Code + + Atidaryti atpirkimo sandorį {0} - - Open the root of the Git repo in Visual Studio Code + + Atidarykite „Git“ repo šaknį {0} - Copy code + Nukopijuokite kodą - Added + Pridėta - Deleted + Ištrinta - Modified + Modifikuota - Untracked + Nesekamas - Unable to display current owner. + Neįmanoma parodyti dabartinio savininko. - Great! You are now authorized. + Puiku! Dabar esate įgalioti. - Initialize repo + Inicijuoti atpirkimo sandorį - Initialize a Git repository + Inicijuoti dabartinį aplanką kaip git saugyklą - Remote Repository Name + Nuotolinės saugyklos pavadinimas - Current Branch + Dabartinis filialas - Path column + Kelio stulpelis - Sort items by path + Rūšiuoti elementus pagal kelią - Open directory in new pane + Atidarykite pasirinktą katalogą naujoje srityje - Open directory in new tab + Atidarykite pasirinktą katalogą naujame skirtuke - Open directory in new window + Atidarykite pasirinktą katalogą naujame lange - Open all + Atidaryti visus - Open all tagged items + Atidarykite visus pažymėtus elementus - Drive ({0}) + Vairuoti ({0}) {0} is the drive letter. - Invalid command + Neteisinga komanda - '{0}' is not recognized as a command. + '{0}' neatpažįstamas kaip komanda. - Command not executable + Komanda nevykdoma - The '{0}' command is not ready to be executed. + Komanda '{0}' neparengta vykdyti. - Command palette + Komandų paletė - Open command palette + Atidarykite komandų paletę „OmniBar“. - Start in: + Pradėti: - Turn on BitLocker + Įjunkite „BitLocker“. - Manage BitLocker + Tvarkykite „BitLocker“. - The following items are too large to be copied to this drive + Šie elementai yra per dideli, kad juos būtų galima nukopijuoti į šį diską - Format drive + Formatuoti diską - Don't show again + Daugiau nerodyti - Files is running as administrator + Failai veikia kaip administratorius - Due to a limitation with Windows and WinAppSdk, drag and drop isn't available when running Files as admin. If you wish to use drag and drop, you can work around this limitation by opening UAC (User Account Control) from the Start Menu and selecting the third level, and restarting Windows. Otherwise, you can keep using Files without drag & drop. + Dėl „Windows“ ir „WinAppSdk“ apribojimų vilkimas negalimas, kai paleidžiate failą kaip administratorių. Jei norite naudoti vilkimą, galite apeiti šį apribojimą atidarę UAC (vartotojo abonemento valdymą) iš meniu Pradėti ir pasirinkę trečiąjį lygį bei iš naujo paleisdami „Windows“. Kitu atveju galite toliau naudoti failus nevilkdami ir numesdami. - Leave app running in the background when the window is closed + Palikite programą veikti fone, kai langas uždarytas - Blue + MÄ—lyna/-s One of the custom color themes - Blue Gray + Mėlyna Pilka One of the custom color themes - Brick Red + Plytų raudona One of the custom color themes - Camouflage + Kamufliažas One of the custom color themes - Cool Blue Bright + Šalta mėlyna šviesi One of the custom color themes - Gray + Pilka One of the custom color themes - Gray Dark + Pilka Tamsi One of the custom color themes - Green + Žalia/-s One of the custom color themes - Iris Pastel + Iris Pastelė One of the custom color themes - Mint Light + Mėtų šviesa One of the custom color themes @@ -3317,589 +3455,926 @@ One of the custom color themes - Orange Bright + Oranžinė Ryški One of the custom color themes - Overcast + Debesuota One of the custom color themes - Red + Raudona/-s One of the custom color themes - Rose Bright + Ryški rožė One of the custom color themes - Seafoam + Jūros putos One of the custom color themes - Storm + Audra One of the custom color themes - Violet Red Light + Violetinė raudona šviesa One of the custom color themes - Yellow Gold + Geltonas Auksas One of the custom color themes - Clear completed + Išvalymas baigtas - Name: + Vardas: - {0} of {1} processed + Apdorota {1} iš {1} Shown in a StatusCenter card. Used as "64 MB of 1 GB processed" - items + daiktų - Files can't initialize this directory as a Git repository. + Failai negali inicijuoti šio katalogo kaip Git saugyklos. - Contributing Artist + Prisidedantis atlikėjas - Add page + Pridėti puslapį - Processing items... + Apdorojami elementai... + + + Atrandami daiktai... - Speed: + Greitis: - Canceled compressing {0} items to "{1}" + Atšauktas {1} elementų glaudinimas į "{1}" Shown in a StatusCenter card. - Canceled compressing {0} items from "{1}" to "{2}" + Atšauktas {2} elementų glaudinimas iš "{2}" į "{2}" Shown in a StatusCenter card. - Compressed {0} item(s) to "{1}" + Compressed {0, plural, one {# prekė} other {# elementai}} į "{1}" Shown in a StatusCenter card. - Compressed {0} item(s) from "{1}" to "{2}" + Compressed {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error compressing {0} item(s) to "{1}" + Error compressing {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to compress {0} item(s) from "{1}" to "{2}" + Failed to compress {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Compressing {0} item(s) to "{1}" + Compressing {0, plural, one {# prekė} other {# elementai}} į "{1}" Shown in a StatusCenter card. - Compressing {0} item(s) from "{1}" to "{2}" + Compressing {0, plural, one {# item} other {# items}} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Atšauktas klonavimas {1} į "{1}" + Shown in a StatusCenter card. + + + Atšauktas klonavimas {2} iš "{2}" į "{2}" + Shown in a StatusCenter card. + + + Klonuotas „{1}“ į „{1}“ + Shown in a StatusCenter card. + + + Klonuotas {2} iš "{2}" į "{2}" + Shown in a StatusCenter card. + + + Klaida klonuojant „{1}“ į „{1}“ + Shown in a StatusCenter card. + + + Nepavyko klonuoti {2} iš "{2}" į "{2}" + Shown in a StatusCenter card. + + + „{1}“ klonavimas į „{1}“ + Shown in a StatusCenter card. + + + Klonavimas {2} nuo "{2}" iki "{2}" + Shown in a StatusCenter card. + + + Atšauktas {0} šriftų diegimas + Shown in a StatusCenter card. + + + Canceled installing {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Įdiegta {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installed {0, plural, one {# šriftas} other {# šriftai}} iš "{1}" + Shown in a StatusCenter card. + + + Klaida diegiant {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Failed to install {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Diegiama {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installing {0, plural, one {# šriftas} other {# šriftai}} iš "{1}" Shown in a StatusCenter card. - Canceled copying {0} item(s) to "{1}" + Canceled copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Canceled copying {0} item(s) from "{1}" to "{2}" + Canceled copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Copied {0} item(s) to "{1}" + Copied {0, plural, one {# prekė} other {# elementai}} į "{1}" Shown in a StatusCenter card. - Copied {0} item(s) from "{1}" to "{2}" + Copied {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error copying {0} item(s) to "{1}" + Error copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to copy {0} item(s) from "{1}" to "{2}" + Failed to copy {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Copying {0} item(s) to "{1}" + Copying {0, plural, one {# prekė} other {# elementai}} į "{1}" Shown in a StatusCenter card. - Copying {0} item(s) from "{1}" to "{2}" + Copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. + + Discovered {0, plural, one {# item} other {# items}} + Shown in a StatusCenter card during file discovery phase. + - Canceled extracting "{0}" to "{1}" + Atšauktas „{1}“ ištraukimas į „{1}“ Shown in a StatusCenter card. - Canceled extracting "{0}" from "{1}" to "{2}" + Atšauktas „{2}“ išskyrimas iš „{2}“ į „{2}“ Shown in a StatusCenter card. - Extracted "{0}" to "{1}" + Išskirtas „{1}“ į „{1}“ Shown in a StatusCenter card. - Extracted "{0}" from "{1}" to "{2}" + Išskirta „{2}“ iš „{2}“ į „{2}“ Shown in a StatusCenter card. - Error extracting "{0}" to "{1}" + Klaida išskleidžiant „{1}“ į „{1}“ Shown in a StatusCenter card. - Failed to extract "{0}" from "{1}" to "{2}" + Nepavyko ištraukti "{2}" iš "{2}" į "{2}" Shown in a StatusCenter card. - Extracting "{0}" to "{1}" + „{1}“ išskleidžiama į „{1}“ Shown in a StatusCenter card. - Extracting "{0}" from "{1}" to "{2}" + „{2}“ ištraukimas iš „{2}“ į „{2}“ Shown in a StatusCenter card. - Canceled deleting {0} item(s) from "{1}" + Canceled deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Deleted {0} item(s) from "{1}" + Deleted {0, plural, one {# prekė} other {# prekės}} iš "{1}" Shown in a StatusCenter card. - Error deleting {0} item(s) from "{1}" + Error deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Failed to delete {0} item(s) from "{1}" + Failed to delete {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Deleting {0} item(s) from "{1}" + Deleting {0, plural, one {# prekė} other {# prekės}} iš "{1}" Shown in a StatusCenter card. - Canceled moving {0} item(s) to "{1}" + Canceled moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Canceled moving {0} item(s) from "{1}" to "{2}" + Canceled moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Moved {0} item(s) to "{1}" + Moved {0, plural, one {# prekė} other {# elementai}} į "{1}" Shown in a StatusCenter card. - Moved {0} item(s) from "{1}" to "{2}" + Moved {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Moving {0} item(s) to "{1}" + Moving {0, plural, one {# prekė} other {# elementai}} į "{1}" Shown in a StatusCenter card. - Moving {0} item(s) from "{1}" to "{2}" + Moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error moving {0} item(s) to "{1}" + Error moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to move {0} item(s) from "{1}" to "{2}" + Failed to move {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Canceled emptying Recycle Bin + Šiukšlinės ištuštinimas atšauktas Shown in a StatusCenter card. - Emptied Recycle Bin + Ištuštinta šiukšliadėžė Shown in a StatusCenter card. - Emptying Recycle Bin + Šiukšlių dėžės ištuštinimas Shown in a StatusCenter card. - Error emptying Recycle Bin + Ištuštinant šiukšlinę įvyko klaida Shown in a StatusCenter card. - Failed to empty Recycle Bin + Nepavyko ištuštinti šiukšlinės Shown in a StatusCenter card. - Preparing the operation... + Ruošiamasi operacijai... Shown in a StatusCenter card. - {0}/{1} item(s) processed + {0}/{1, plural, one {# item} other {# items}} processed Shown in a StatusCenter card. Used as "8/20 items processed" - Unblock downloaded file + Atblokuoti atsisiųstą failą - No ongoing file operations + Jokių vykdomų failų operacijų - The option to open Files on Windows Startup is not available due to your system settings or group policy. To re-enable, open the startup page in Task Manager. + Parinktis atidaryti failus Windows paleisties metu nepasiekiama dėl sistemos nustatymų arba grupės strategijos. Norėdami vėl įjungti, užduočių tvarkytuvėje atidarykite paleisties puslapį. - Failed to restore items from Recycle Bin + Nepavyko atkurti elementų iš šiukšlinės - Failed to set the background wallpaper + Nepavyko nustatyti fono fono + + + Nepavyko atidaryti nustatymų failo - Delete Git branch + Ištrinkite Git filialą - Are you sure you want to permanently delete "{0}" branch? + Ar tikrai norite visam laikui ištrinti "{0}" šaką? - Connected to GitHub + Prisijungta prie GitHub - Logout + Atsijungti - Connect to GitHub + Prisijunkite prie „GitHub“. - Login + Prisijungti - Tags are currently only compatible on drives formatted as NTFS. + Žymos šiuo metu suderinamos tik su diskais, suformatuotais kaip NTFS. - There was an error applying this tag + Taikant šią žymą įvyko klaida - Extract items from selected archive(s) to current folder for single-item archive, or to new folder for multi-item archive + Ištrauka selected {0, plural, one {archyvas} other {archives}} čia vienam elementui arba į naują aplanką keliems elementams - Extract here (Smart) + Ištraukite čia (išmanusis) - Sort files and folders together + Rūšiuoti failus ir aplankus kartu - Sort files and folders together + Rūšiuokite failus ir aplankus tame pačiame sąraše - Sort files first + Pirmiausia surūšiuokite failus - Sort files first then folders + Pirmiausia rūšiuokite failus, tada aplankus - Sort folders first + Pirmiausia surūšiuokite aplankus - Sort folders first then files + Pirmiausia rūšiuokite aplankus, tada failus - Sort priority + Rūšiavimo prioritetas - Failed to rotate the image + Nepavyko pasukti vaizdo - Restart + Paleisti iš naujo - Quit + Išeik - Failed to share items + Nepavyko bendrinti elementų - Scroll to previous folder when navigating up + Naršydami aukštyn slinkite į ankstesnį aplanką - Open new window + Atidaryti naują langą - Change album cover + Keisti albumo viršelį - Failed to rename item + Nepavyko pervardyti elemento - Editing "{0}" requires additional permissions + Norint redaguoti „{0}“, reikia papildomų leidimų - Edit permissions + Redaguoti leidimus - Files is still running in the background to improve startup performance. + Failai vis dar veikia fone, kad pagerintų paleidimo našumą. - Where did Files go? + Kur dingo failai? - Questions & discussions - - - Additional sizes are not yet available for the Tiles View. + Klausimai ir diskusijos - Compact + Kompaktiškas Used to describe layout sizes - Small + Mažas Used to describe layout sizes - Medium + Vidutinis Used to describe layout sizes - Medium + + Vidutinis + Used to describe layout sizes - Medium ++ + Vidutinis ++ Used to describe layout sizes - Medium +++ + Vidutinis +++ Used to describe layout sizes - Medium ++++ + Vidutinis ++++ Used to describe layout sizes - Medium +++++ + Vidutinis +++++ Used to describe layout sizes - Large + Didelis Used to describe layout sizes - Large + + Didelis + Used to describe layout sizes - Large ++ + Didelis ++ Used to describe layout sizes - Large +++ + Didelis +++ Used to describe layout sizes - Extra large + Itin didelis Used to describe layout sizes - Details view + Išsamios informacijos vaizdas - Layout type + Išdėstymo tipas Veiksmai - Commands + Komandos - Add command + Pridėti komandą - Restore defaults + Atkurti numatytuosius nustatymus - Choose an action + Pasirinkite veiksmą - Are you sure you want to restore the default key bindings? This action cannot be undone. + Ar tikrai norite atkurti numatytuosius raktų surišimus? Šio veiksmo anuliuoti negalima. - This key binding is already being used, please choose a different key binding to continue. + Šis raktų susiejimas jau naudojamas. Jei norite tęsti, pasirinkite kitą rakto susiejimą. - The key binding you choose cannot be used, please try again using a different key binding. + Pasirinkto rakto susiejimo naudoti negalima. Bandykite dar kartą naudodami kitą rakto susiejimą. - Customized + Pritaikyta - Adaptive layout is not available when preferences are synced, the default layout has been changed to Details. + Prisitaikantis išdėstymas nepasiekiamas, kai nuostatos sinchronizuojamos, numatytasis išdėstymas pakeistas į Išsami informacija. - Background image + Fono vaizdas - Bottom + Apačia Image alignment type - Center + centras Image alignment type - Fill + Užpildykite Image stretch type - Horizontal alignment + Horizontalus lygiavimas - Image fit + Tinkamas vaizdas - Left + KairÄ— Image alignment type - Opacity + Neskaidrumas - Right + DeÅ¡inÄ— Image alignment type - Top + Į viršų Image alignment type - Uniform + Uniforma Image stretch type - Uniform to fill + Uniforma užpildyti Image stretch type - Vertical alignment + Vertikalus lygiavimas - Thin Acrylic + Plonas akrilas This is a type of backdrop for the application background - Show for all locations + Rodyti visose vietose Setting where users can choose to display "Open IDE" button for Git Repos - Developer tools + Kūrėjo įrankiai - Configure the "Open IDE" button on the status bar + Būsenos juostoje sukonfigūruokite mygtuką „Atidaryti IDE“. - Show for Git repos + Rodyti Git atpirkimo sandorius Setting where users can choose to display "Open IDE" button for all locations. - Application extension + Programos plėtinys This is the friendly name for DLL files. - ICO File + „ICO“ failas This is the friendly name for ICO files. + + „ICL“ failas + This is the friendly name for ICL files. + - Zip File + „Zip“ failas This is the friendly name for ZIP files. - Bitmap Files + Bitmap failai This is the friendly name for bitmap files. + + Vaizdo failai + This is the friendly name for image files. + - Num + Nr - Network locations + Tinklo vietos - There are no network locations. If you don't use network locations, you can disable the widget. + Nėra tinklo vietų. Jei nenaudojate tinklo vietų, galite išjungti valdiklį. - Disable + Išjungti - Edit in Notepad + Redaguoti užrašų knygelėje - Edit the selected file in Notepad + Redaguokite pasirinktą failą Notepad - Dimensions: + Matmenys: - Status: + Būsena: - Show Toolbar - Setting that controls if the Toolbar is shown in the main view + Rodyti įrankių juostą + Setting that controls if the toolbar is shown in the main view - Tab actions menu + Skirtuko veiksmų meniu - - Add vertical pane + + Padalinti vertikaliai - Add a vertical pane + Padalinkite langą vertikaliai - - Add horizontal pane + + Padalinti horizontaliai - - Add a horizontal pane + + Padalinkite langą horizontaliai - Arrange vertically + Išdėstykite vertikaliai - Arrange panes vertically + Išdėstykite stiklus vertikaliai - Arrange horizontally + Išdėstykite horizontaliai - Arrange panes horizontally + Išdėstykite plokštes horizontaliai - - Add pane + + Padalintas skydelis - Show tab actions button in the title bar + Rodyti skirtuko veiksmų mygtuką pavadinimo juostoje - Arrange panes + Išdėstykite stiklus + + + Numatytoji dviejų langų padalijimo kryptis - - Default pane arrangement + + Dviejų langų režimas - Horizontal + Horizontaliai - Vertical + Vertikalus + + + Rodyti failų piktogramą sistemos dėkle + + + CPU gijos + + + Eikite į pagrindinį puslapį + + + Įrankių juostos + + + Vartotojo ID + + + Masinis pervadinimas + + + Suspausti turinį + + + Rodyti alternatyvaus duomenų srauto kūrimo parinktį + + + Sukurkite alternatyvų duomenų srautą + + + Create alternate data stream for the selected {0, plural, one {item} other {items}} + + + Įveskite duomenų srauto pavadinimą + + + Kuriant alternatyvų duomenų srautą įvyko klaida + + + Atminkite, kad alternatyvūs duomenų srautai veikia tik diskuose, suformatuotuose kaip NTFS. + + + Šiuo metu alternatyvūs duomenų srautai yra paslėpti + + + Ar norėtumėte rodyti alternatyvius duomenų srautus? Šį nustatymą galite bet kada pakeisti failų ir aplankų nustatymų puslapyje. + + + Tvarkyti žymas + + + Galima + + + Iš viso + + + Visada perjunkite židinį į naujai sukurtą skirtuką + + + Perjungti lentynos sritį + + + Perjungti lentynos srities matomumą + + + Rodyti lentynos srities perjungimą adreso juostoje + + + Lentynėlė + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + Išvalyti elementus + + + Išimkite iš lentynos + + + Pridėti į lentyną + Tooltip that displays when dragging items to the Shelf Pane + + + Norėdami palyginti, įveskite maišą + Placeholder that appears in the compare hash text box + + + Atitinka {0} + Appears when two compared hashes match, e.g. "Matches SHA256" + + + Atitikčių nerasta + Appears when two compared hashes don't match + + + Kelias arba slapyvardis + + + Neteisingas kelias + + + Bandomasis integravimas + + + {0} nepavyko rasti. Patikrinkite nustatymus ir bandykite dar kartą. + + + Nepavyko rasti sukonfigūruoto IDE + + + Atidaryti nustatymus + + + Visual Studio kodas + + + Įveskite kelią arba paleidimo slapyvardį + + + Įveskite IDE pavadinimą + + + Klonuoti atpirkimą + Clone repo dialog title + + + Klonuoti + Primary action button in the clone repo dialog + + + Saugyklos URL + URL textbox header in the clone repo dialog + + + Negalima klonuoti atpirkimo sandorio + Cannot clone repo dialog title + + + Palyginti failÄ… + Button that appears in file hash properties that allows the user to compare two files + + + Dydžio formatas + + + Dvejetainis + + + Dešimtainė + + + Skilčių galite pridėti prie šoninės juostos dešiniuoju pelės mygtuku spustelėdami ir pasirinkdami norimas pridėti dalis + + + Nuvilkite failus ar aplankus čia, kad galėtumėte su jais sąveikauti skirtinguose skirtukuose + + + Ä®veskite keliÄ…, į kurį norite narÅ¡yti... + + + Raskite funkcijas ir komandas... + + + Ieškokite failų ir aplankų... + + + Failų operacijų metu + + + Rodyti būsenos centrinį mygtuką + + + Būsenos centro progreso žiedas + Screen reader name for the status center progress ring + + + Piktogramų failai + This is the friendly name for a variety of different icon files. + + + Rodyti sutrauktus kelio naršymo kelius + + + Rodyti aplankus {0} + + + Rodyti aplankus pagrindiniame puslapyje + + + Nėra komandų su {0} + + + ŽiÅ«rÄ—ti daugiau + + + Filtravimas už + + + Failo pavadinimas + + + Parašai + + + Parašų sąrašas + + + Išdavė: + + + Išduota: + + + Galioja nuo: + + + Galioja: + + + Parašo nerasta. + + + Rodyti aplankų atidarymo parinktį Windows terminale + + + Atidaryti žurnalo failą + + + Atidarykite žurnalo failą numatytajame redaktoriuje + + + Atidarykite žurnalo failo vietą numatytojoje failų tvarkyklėje + + + Nepavyko atidaryti žurnalo failo + + + Rodyti būsenos juostą + + + Tik stulpelių rodinyje + + + Naujas {0} + + + Įgalinkite sklandų slinkimą + + + Slinkimas + + + Rodyti parinktį Prisegti prie šoninės juostos + + + Rodyti parinktį Prisegti prie meniu Pradėti \ No newline at end of file diff --git a/src/Files.App/Strings/lv-LV/Resources.resw b/src/Files.App/Strings/lv-LV/Resources.resw index 355768f36b92..0554db208084 100644 --- a/src/Files.App/Strings/lv-LV/Resources.resw +++ b/src/Files.App/Strings/lv-LV/Resources.resw @@ -123,9 +123,15 @@ Copy path + + Copy item path + Copy path with quotes + + Copy selected item path with quotes + Pārlūkot @@ -160,7 +166,7 @@ Izlaist - Select All + Select all Invert selection @@ -211,7 +217,7 @@ Rādīt slēptos failus un mapes - Rādīt punktu failus + Show dot files Izskats @@ -220,7 +226,7 @@ Background color - Paplašināti + Papildus Turpināt, kur beidzāt @@ -249,6 +255,9 @@ Paste + + Paste shortcut + Jauns @@ -291,8 +300,8 @@ Enter an item name - - Set name + + Create new {0} Gaišs @@ -300,6 +309,9 @@ Tumšs + + Use system setting + Lietojumprogramma @@ -379,7 +391,7 @@ Fails jau tiek lietots - Layout + Izkārtojums Tikai lasāms @@ -515,9 +527,6 @@ Status - - Windows noklusējums - Nav rezultātu @@ -530,6 +539,9 @@ Kopēt uz {0} + + Clone to {0} + Create shortcut @@ -654,10 +666,10 @@ Atsvaidzināt mapi - Language + Valoda - Pārvietot lietotnes paplašīnājumu uz apakšizvēlni + Pārvietot čaulas paplašinājumus uz apakšizvēlni Rādīt apstiprinājuma logu pirms vienuma dzēšanas @@ -998,6 +1010,9 @@ Meklēšanas rezultāti mapē {1} vienumam {0} + + Search results for `{0}` + Detaļas @@ -1047,14 +1062,11 @@ Open in new pane - Files & folders + Faili un mapes Detalizēts skats (Ctrl+Shift+1) - - Mozaīkas skats (Ctrl+Shift+2) - Dzēšanas datums @@ -1080,10 +1092,25 @@ Toggle the info pane - Toggle the info pane to view the detail/preview panes + Toggle visibility of the detail/preview panes - Toggle the Toolbar + Toggle toolbar + + + Toggle visibility of the toolbar + + + Toggle filter header + + + Toggle visibility of the filter header + + + Toggle dual pane + + + Toggle dual pane mode Priekšskatījums nav pieejams @@ -1238,6 +1265,12 @@ Open Storage Sense + + Open the Storage Sense page in Windows Settings + + + Cleanup + Kopēt @@ -1380,7 +1413,7 @@ {0} vienumi - Atkārtoti atvērt aizvērto cilni + Reopen tab Rename @@ -1587,7 +1620,7 @@ Aizvietot visas bērnu objektu atļaujas ar iedzimtajām atļaujām no šī objekta - Close active pane + Close pane Atvērt kompakto pārklāju @@ -1646,15 +1679,6 @@ Mājas - - Ctrl+Shift+1 - - - Ctrl+Shift+2 - - - Ctrl+Shift+6 - Arhīva izvilkšana veiksmīgi pabeigta. @@ -1712,8 +1736,8 @@ Kolonnas - - Mozaīka + + Cards Open folders in new tab @@ -1749,7 +1773,7 @@ Birka - Atvērt vienumu ar vienu klikšķi + Open files with a single click Folder path @@ -1826,6 +1850,18 @@ Register this program for restart + + Start window + + + Normal window + + + Minimized + + + Maximized + Palaist kā administratoram @@ -1853,6 +1889,9 @@ Šis iestatījums modificē sistēmas reģistru un var ierīcei radīt nevēlamus blakusefektus. Turpinot, uzņemieties atbildību par riskiem! + + The flatten operations are permanent and not recommended. Continue at your own risk. + Izveidot bibliotēku @@ -1931,6 +1970,9 @@ Aizvērt pārējās cilnes + + Close all tabs + Mūzika @@ -1985,11 +2027,23 @@ Uzvedības - - Pārskatīt Failus + + Hello! + + + Enjoying Files? Please consider reviewing in the Microsoft Store. + + + Enjoying Files? Please consider supporting the project on GitHub. - - Vai vēlies pārskaīt Failus? + + Sponsor + + + Rate us + + + Dismiss Iestatīt ka fonu @@ -2018,8 +2072,8 @@ Bloķēšanas ekrāns - - Atvērt direktoriju ar vienu klikšķi Kolonas Izkārtojumā + + Open folders with a single click Atver vienumu @@ -2027,6 +2081,21 @@ Compress + + Move all contents from subfolders into the selected location + + + Flatten folder + + + Flatten + + + Flattening a folder will move all contents from its subfolders to the selected location. This operation is permanent and cannot be undone. By using this experimental feature, you acknowledge the risk and agree not to hold the Files team responsible for any data loss. + + + Show flatten options + Atzīmēt failus un direktorijas kad uzvirza peli @@ -2037,7 +2106,7 @@ Atjaunot visus vienumus - Vai tiešām vēlies atjanunot {0} atlasītos vienumus? + Do you want to restore the {0, plural, one {selected item} other {{0} selected items}}? Atjaunot atlasīto @@ -2060,6 +2129,12 @@ Arhīva parole + + Encoding + + + {0} (detected) + Ceļš @@ -2102,8 +2177,23 @@ Dalīšanas izmērs - - Nedalīt + + Do not split + + + Auto + + + Dictionary size + + + Word size + + + Estimated memory usage: {0} + + + Available memory: {0} CD @@ -2159,8 +2249,14 @@ Edit settings file - - What's new + + Open settings file in your default editor + + + Release Notes + + + Open Release Notes Lai izveidotu saīsni uz šo mērķi ir nepieciešamas administrātora tiešības @@ -2238,28 +2334,25 @@ Loading... - Context menu options + Konteksta izvēlnes iestatījumi - Show file extensions - - - Show hidden items + File extensions - Format... + Formāts Help - Toggle full screen + Full screen Are you sure you want to delete this tag? - Play all + Play Height @@ -2319,7 +2412,7 @@ Increase size - Toggle sort direction + Sort direction Invalid name @@ -2331,34 +2424,37 @@ Video - Launch preview popup + Preview popup Toggle compact overlay - Open online help page in browser + Open the online help page in your browser - Toggle full screen + Toggle full screen mode - Atvērt kompakto pārklāju + Enter compact overlay mode - Iziet kompakto pārklājumu + Exit compact overlay mode - Toggle compact overlay + Toggle compact overlay mode - Go to search box + Start search in the OmniBar - Toggle whether to show hidden items + Toggle visibility of hidden items + + + Toggle visibility of dot files - Toggle whether to show file extensions + Toggle visibility of file extensions Toggle the preview pane to view file previews @@ -2367,52 +2463,64 @@ Toggle whether to show sidebar - Copy item(s) to clipboard + Copy selected {0, plural, one {item} other {items}} - Copy path of selected items to the clipboard + Copy path of the current directory + + + Copy path of selected items + + + Copy path of selected items with quotes - Copy path of selected items with quotes to the clipboard + Copy path of the current directory with quotes - Cut item(s) to clipboard + Cut selected {0, plural, one {item} other {items}} - Paste item(s) from clipboard to current folder + Paste items to the current folder + + + Paste items to the current folder as shortcuts - Paste item(s) from clipboard to selected folder + Paste items to the selected folder + + + Paste to selected folder - Dzēst vienumu(s) + Delete selected {0, plural, one {item} other {items}} Create new folder - Create new shortcut(s) to selected item(s) + Create new {0, plural, one {shortcut} other {shortcuts}} to selected {0, plural, one {item} other {items}} Create new shortcut to any item - Iztukšot atkritni + Empty the contents of Recycle Bin Open "Format Drive" menu for selected item - Restore selected item(s) from recycle bin + Restore selected {0, plural, one {item} other {items}} from recycle bin Restore all items from recycle bin - Open item(s) + Open {0, plural, one {item} other {items}} - Open item(s) with selected application + Open {0, plural, one {item} other {items}} with selected application Open parent folder of searched item @@ -2427,28 +2535,28 @@ Select all items - Invert item selection + Invert selected items - Clear item selection + Clear selected items Toggle item selection - Share selected file(s) with others + Share selected {0, plural, one {file} other {files}} with others - Pin item(s) to the Start Menu + Pin {0, plural, one {item} other {items}} to the Start Menu - Unpin item(s) from the Start Menu + Unpin {0, plural, one {item} other {items}} from the Start Menu - Pin folder(s) to Sidebar + Pin {0, plural, one {folder} other {folders}} to Sidebar - Unpin folder(s) from Sidebar + Unpin {0, plural, one {folder} other {folders}} from Sidebar Set selected picture as desktop background @@ -2462,14 +2570,23 @@ Set selected picture as the app background + + Install font + + + Install driver + + + Install certificate + - Install selected font(s) + Install selected {0, plural, one {font} other {fonts}} - Install driver(s) using selected inf file(s) + Install {0, plural, one {driver} other {drivers}} using selected inf {0, plural, one {file} other {files}} - Install selected certificate(s) + Install selected {0, plural, one {certificate} other {certificates}} Run selected application as administrator @@ -2484,28 +2601,28 @@ Launch preview in popup window - Create archive with selected item(s) + Create archive with selected {0, plural, one {item} other {items}} - Create 7z archive instantly with selected item(s) + Create 7z archive with selected {0, plural, one {item} other {items}} - Create zip archive instantly with selected item(s) + Create zip archive with selected {0, plural, one {item} other {items}} - Extract items from selected archive(s) to any folder + Extract selected {0, plural, one {archive} other {archives}} to any folder - Extract items from selected archive(s) to current folder + Extract selected {0, plural, one {archive} other {archives}} to the current folder - Extract items from selected archive(s) to new folder + Extract selected {0, plural, one {archive} other {archives}} to new folder - Rotate selected image(s) to the left + Rotate selected {0, plural, one {image} other {images}} to the left - Rotate selected image(s) to the right + Rotate selected {0, plural, one {image} other {images}} to the right Open settings page @@ -2525,8 +2642,8 @@ Switch to details view - - Switch to tiles view + + Switch to cards view Switch to list view @@ -2625,13 +2742,13 @@ Toggle group sort direction - Open new tab + Atvērt jaunu cilni - Navigate backward in navigation history + Navigate backward - Navigate forward in navigation history + Navigate forward Navigate up one directory @@ -2660,8 +2777,11 @@ Close tabs other than selected tab + + Close all tabs including the current tab + - Reopen last closed tab + Reopen recently closed tab Move to the previous tab @@ -2673,13 +2793,13 @@ Close current tab - Close active pane + Close the active pane Focus other pane - Switch focus to the non active pane + Switch focus to the other pane Toggle the sidebar @@ -2887,13 +3007,13 @@ Untagged - Moves to the previous tab + Previous tab - Moves to the next tab + Next tab - Closes current tab + Close tab Edit path @@ -2923,7 +3043,7 @@ Show checkboxes when selecting items - Focus path bar + Edit path in the OmniBar Create new item @@ -2938,7 +3058,7 @@ Delete permanently - Delete item(s) permanently + Delete selected {0, plural, one {item} other {items}} permanently Play the selected media files @@ -3027,6 +3147,15 @@ You have uncommitted changes on this branch. What would you like to do with them? + + You have an ongoing merge with unresolved conflicts. Please resolve the conflicts or abort the merge to switch branches. + + + Abort merge and switch to '{0}' + + + Stay on '{0}' and resolve conflicts + Switch branch @@ -3055,14 +3184,20 @@ Switch to new branch - Create a folder with the currently selected item(s) + Create a folder with the currently selected {0, plural, one {item} other {items}} - Open properties + Properties + + + Open File Explorer properties Open properties window + + Open File Explorer properties window + Locals @@ -3081,6 +3216,9 @@ Run git fetch + + Clone a git repo + Run git pull @@ -3125,7 +3263,7 @@ Mica Alt - Backdrop Material + Backdrop Solid @@ -3157,17 +3295,17 @@ Files cannot access GitHub right now. - - Open folder in VS Code + + Open folder in {0} - - Open the current directory in Visual Studio Code + + Open the current directory in {0} - - Open repo in VS Code + + Open repo in {0} - - Open the root of the Git repo in Visual Studio Code + + Open the root of the Git repo in {0} Copy code @@ -3194,7 +3332,7 @@ Initialize repo - Initialize a Git repository + Initialize current folder as a git repository Remote Repository Name @@ -3209,13 +3347,13 @@ Sort items by path - Open directory in new pane + Open selected directory in a new pane - Open directory in new tab + Open selected directory in a new tab - Open directory in new window + Open selected directory in a new window Open all @@ -3240,10 +3378,10 @@ The '{0}' command is not ready to be executed. - Command palette + Command Palette - Open command palette + Open Command Palette in the OmniBar Start in: @@ -3373,6 +3511,9 @@ Processing items... + + Discovering items... + Speed: @@ -3385,61 +3526,129 @@ Shown in a StatusCenter card. - Compressed {0} item(s) to "{1}" + Compressed {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Compressed {0} item(s) from "{1}" to "{2}" + Compressed {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error compressing {0} item(s) to "{1}" + Error compressing {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to compress {0} item(s) from "{1}" to "{2}" + Failed to compress {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Compressing {0} item(s) to "{1}" + Compressing {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Compressing {0} item(s) from "{1}" to "{2}" + Compressing {0, plural, one {# item} other {# items}} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Canceled cloning {0} to "{1}" + Shown in a StatusCenter card. + + + Canceled cloning {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Cloned "{0}" to "{1}" + Shown in a StatusCenter card. + + + Cloned {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Error cloning "{0}" to "{1}" + Shown in a StatusCenter card. + + + Failed to clone {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Cloning "{0}" to "{1}" + Shown in a StatusCenter card. + + + Cloning {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Canceled installing {0} fonts + Shown in a StatusCenter card. + + + Canceled installing {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Error installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Failed to install {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} from "{1}" Shown in a StatusCenter card. - Canceled copying {0} item(s) to "{1}" + Canceled copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Canceled copying {0} item(s) from "{1}" to "{2}" + Canceled copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Copied {0} item(s) to "{1}" + Copied {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Copied {0} item(s) from "{1}" to "{2}" + Copied {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error copying {0} item(s) to "{1}" + Error copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to copy {0} item(s) from "{1}" to "{2}" + Failed to copy {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Copying {0} item(s) to "{1}" + Copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Copying {0} item(s) from "{1}" to "{2}" + Copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. + + Discovered {0, plural, one {# item} other {# items}} + Shown in a StatusCenter card during file discovery phase. + Canceled extracting "{0}" to "{1}" Shown in a StatusCenter card. @@ -3473,55 +3682,55 @@ Shown in a StatusCenter card. - Canceled deleting {0} item(s) from "{1}" + Canceled deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Deleted {0} item(s) from "{1}" + Deleted {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Error deleting {0} item(s) from "{1}" + Error deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Failed to delete {0} item(s) from "{1}" + Failed to delete {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Deleting {0} item(s) from "{1}" + Deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Canceled moving {0} item(s) to "{1}" + Canceled moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Canceled moving {0} item(s) from "{1}" to "{2}" + Canceled moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Moved {0} item(s) to "{1}" + Moved {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Moved {0} item(s) from "{1}" to "{2}" + Moved {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Moving {0} item(s) to "{1}" + Moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Moving {0} item(s) from "{1}" to "{2}" + Moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error moving {0} item(s) to "{1}" + Error moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to move {0} item(s) from "{1}" to "{2}" + Failed to move {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. @@ -3549,7 +3758,7 @@ Shown in a StatusCenter card. - {0}/{1} item(s) processed + {0}/{1, plural, one {# item} other {# items}} processed Shown in a StatusCenter card. Used as "8/20 items processed" @@ -3567,6 +3776,9 @@ Failed to set the background wallpaper + + Failed to open the settings file + Delete Git branch @@ -3592,7 +3804,7 @@ There was an error applying this tag - Extract items from selected archive(s) to current folder for single-item archive, or to new folder for multi-item archive + Extract selected {0, plural, one {archive} other {archives}} here for single-item or to new folder for multi-item Extract here (Smart) @@ -3601,7 +3813,7 @@ Sort files and folders together - Sort files and folders together + Sort files and folders in the same list Sort files first @@ -3657,9 +3869,6 @@ Questions & discussions - - Additional sizes are not yet available for the Tiles View. - Compact Used to describe layout sizes @@ -3719,7 +3928,7 @@ Layout type - Actions + Darbības Commands @@ -3821,6 +4030,10 @@ ICO File This is the friendly name for ICO files. + + ICL File + This is the friendly name for ICL files. + Zip File This is the friendly name for ZIP files. @@ -3829,6 +4042,10 @@ Bitmap Files This is the friendly name for bitmap files. + + Image Files + This is the friendly name for image files. + Num @@ -3854,23 +4071,23 @@ Status: - Show Toolbar - Setting that controls if the Toolbar is shown in the main view + Show toolbar + Setting that controls if the toolbar is shown in the main view Tab actions menu - - Add vertical pane + + Split vertically - Add a vertical pane + Split pane vertically - - Add horizontal pane + + Split horizontally - - Add a horizontal pane + + Split pane horizontally Arrange vertically @@ -3884,8 +4101,8 @@ Arrange panes horizontally - - Add pane + + Split pane Show tab actions button in the title bar @@ -3893,8 +4110,11 @@ Arrange panes - - Default pane arrangement + + Default dual pane split direction + + + Dual pane mode Horizontal @@ -3902,4 +4122,259 @@ Vertical + + Show Files icon in the System Tray + + + CPU threads + + + Navigate to the home page + + + Toolbars + + + User ID + + + Bulk rename + + + Compress contents + + + Show option to create alternate data stream + + + Create alternate data stream + + + Create alternate data stream for the selected {0, plural, one {item} other {items}} + + + Enter data stream name + + + There was an error creating the alternate data stream + + + Please note that alternate data streams only work on drives formatted as NTFS. + + + Alternate data streams are currently hidden + + + Would you like to display alternate data streams? You can modify this setting anytime from the files and folders settings page. + + + Manage tags + + + Available + + + Total + + + Always switch focus to newly created tab + + + Toggle shelf pane + + + Toggle the visibility of the shelf pane + + + Show shelf pane toggle in address bar + + + Shelf + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + Clear items + + + Remove from shelf + + + Add to shelf + Tooltip that displays when dragging items to the Shelf Pane + + + Enter a hash to compare + Placeholder that appears in the compare hash text box + + + Matches {0} + Appears when two compared hashes match, e.g. "Matches SHA256" + + + No matches found + Appears when two compared hashes don't match + + + Path or alias + + + Invalid path + + + Test integration + + + {0} could not be located. Please check your settings and try again. + + + The configured IDE could not be located + + + Open settings + + + Visual Studio Code + + + Enter a path or launch alias + + + Please enter a name for the IDE + + + Clone repo + Clone repo dialog title + + + Clone + Primary action button in the clone repo dialog + + + Repository URL + URL textbox header in the clone repo dialog + + + Cannot clone repo + Cannot clone repo dialog title + + + Compare a file + Button that appears in file hash properties that allows the user to compare two files + + + Size format + + + Binary + + + Decimal + + + You can add sections to the sidebar by right-clicking and selecting the sections you want to add + + + Drag files or folders here to interact with them across different tabs + + + Enter a path to navigate to... + + + Find features and commands... + + + Search for files and folders... + + + During file operations + + + Show status center button + + + Status center progress ring + Screen reader name for the status center progress ring + + + Icon files + This is the friendly name for a variety of different icon files. + + + Show collapsed path breadcrumbs + + + Show folders in {0} + + + Show folders in Home + + + There are no commands containing {0} + + + See more + + + Filtering for + + + Filename + + + Signatures + + + Signature list + + + Issued by: + + + Issued to: + + + Valid from: + + + Valid to: + + + No signature was found. + + + Show option to open folders in Windows Terminal + + + Open log file + + + Open log file in your default editor + + + Open log file location in your default file manager + + + Unable to open the log file + + + Show status bar + + + Only in Columns View + + + New {0} + + + Enable smooth scrolling + + + Scrolling + + + Show option to Pin to Sidebar + + + Show option to Pin to the Start Menu + \ No newline at end of file diff --git a/src/Files.App/Strings/ms-MY/Resources.resw b/src/Files.App/Strings/ms-MY/Resources.resw index 948caf3372e4..45480862fcc8 100644 --- a/src/Files.App/Strings/ms-MY/Resources.resw +++ b/src/Files.App/Strings/ms-MY/Resources.resw @@ -118,14 +118,20 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Tetingkap baru + Tetingkap baharu Salin laluan + + Salin laluan item + Salin laluan dengan petikan + + Copy selected item path with quotes + Semak imbas @@ -160,7 +166,7 @@ Langkau - Pilih Semua + Pilih semua Terbalikkan pilihan @@ -181,7 +187,7 @@ Masukkan laluan untuk navigasi atau taip ">" untuk membuka palet arahan - Cari + Carian Fail yang telah anda akses sebelum ini akan dipaparkan di sini @@ -211,10 +217,10 @@ Tunjukkan fail dan folder tersembunyi - Tunjukkan fail yang bermula dengan titik + Show dot files - Rupa + Penampilan Warna latar belakang @@ -223,7 +229,7 @@ Lanjutan - Sambung dari tempat anda berhenti + Teruskan di mana anda berhenti Buka tab baru @@ -238,7 +244,7 @@ Dokumen - Muat turun + Muat Turun Nama @@ -249,6 +255,9 @@ Tampal + + Tampal pintasan + Baru @@ -259,10 +268,10 @@ Buka - Buka dalam tab baru + Buka dalam tab baharu - Buka dalam tetingkap baru + Buka dalam tetingkap baharu Kongsi @@ -274,7 +283,7 @@ Padam - Pin ke Sisi Bar + Semat ke Bar Sisi Selamat datang ke Files! @@ -291,8 +300,8 @@ Masukkan nama item - - Tetapkan satu nama + + Hasilkan {0} baharu Cerah @@ -300,6 +309,9 @@ Gelap + + Guna tetapan sistem + Aplikasi @@ -453,7 +465,7 @@ Adakah anda pasti anda ingin memadamkan semua item ini secara kekal? - Empty recycle bin + Kosongkan tong kitar semula bait @@ -483,13 +495,13 @@ {0, number} {0, plural, one {file} other {files}}, {1, number} {1, plural, one {folder} other {folders}} from {2, number} {2, plural, one {location} other {locations}} - Run as another user + Jalankan sebagai pengguna lain - All type of {0} + Semua jenis {0} - Different types + Jenis yang berbeza Semua dalam {0} @@ -515,14 +527,11 @@ Status - - Tetapan lalai Windows - Tiada hasil ditemukan - Can't access any items to display + Tidak dapat mengakses apa-apa item untuk dipaparkan Pindahkan ke {0} @@ -530,8 +539,11 @@ Salin ke {0} + + Clone to {0} + - Buat pintasan + Cipta pintasan Buka lokasi fail @@ -573,19 +585,19 @@ Item yang dirujuk tidak sah atau tidak dapat diakses.{0}Mesej ralat:{0}{1} - Invalid item + Item tidak sah Versi: - There's nothing to share right now... + Tiada apa-apa untuk dikongsi sekarang... - The items you've selected will be shared + Item yang anda pilih akan dikongsi - The selected item will be shared + Item yang dipilih akan dikongsi Berkongsi {0} @@ -609,22 +621,22 @@ Nama item yang ditetapkan melebihi had maksimum. Sila semak nama yang dihendaki dan cuba lagi. - Item name was too long + Nama item terlalu panjang - Show more options + Tunjuk lebih banyak pilihan - The application needs to be restarted in order to apply these settings, would you like to restart the app? + Aplikasi perlu dimulakan semula untuk menerapkan tetapan ini, adakah anda mahu memulakan semula aplikasi? - Sponsor us on GitHub + Taja kami di GitHub - We weren't able to create this item + Kami tidak dapat membuat item ini - Access Denied + Akses Ditolak Tetapkan sebagai @@ -651,34 +663,34 @@ Cipta folder kosong - Refresh the directory + Segarkan direktori Bahasa - Move shell extensions into a sub menu + Pindahkan sambungan shell ke dalam submenu - Tunjuk dialog pengesahan ketika memadam fail + Tunjukkan dialog pengesahan semasa memadam item - There was an issue saving some properties. + Terdapat masalah dalam menyimpan "Properties". Ralat - Close anyway + Tutup juga - Rating + Penilaian Item Path - Item Type + Jenis Item Tajuk @@ -696,7 +708,7 @@ Tarikh Diubah suai - Bit Depth + Kedalaman Bit Dimensi @@ -750,22 +762,22 @@ Bukaan - People Names + Nama orang - Channel Count + Bilangan saluran Format - Sample Rate + Kadar Sampel - Album Artist + Album Artis - Album Title + Tajuk Album Artis @@ -774,58 +786,58 @@ Beats Per Minute - Composer + Penggubah - Conductor + Konduktor - Disc Number + Nombor cakera Genre - Track Number + Nombor trek Durasi - Frame Count + Bilangan Bingkai - Protection Type + Jenis Perlindungan - Author Url + Url Pengarang - Content Distributor + Pengedar Kandungan - Date Released + Tarikh Dikeluarkan - Series Name + Name siri - Season Number + Nombor musim - Episode Number + Nombor Episod - Producer + Penerbit - Promotion Url + Url Promosi - Provider Style + Gaya Penyedia - Publisher + Penerbit Thumbnail Large Path @@ -840,64 +852,64 @@ Thumbnail Small Uri - User Web Url + Url Web Pengguna - Writer + Penulis Tahun - Contributor + Para Penyumbang Last Author - Revision Number + Nombor penyemakan - Version + Versi - Date Created + Tarikh Dicipta - Total Editing Time + Jumlah Masa Penyuntingan - Template + Templat - Word Count + Bilangan Perkataan - Character Count + Bilangan Aksara - Line Count + Bilangan Baris - Paragraph Count + Bilangan Perenggan - Page Count + Bilangan Halaman - Slide Count + Bilangan Slaid - Frame Rate + Kadar Bingkai Encoding Bitrate - Audio Encoding Bitrate + Kadar Bit Pengekodan Audio - Video Encoding Bitrate + Kadar Bit Pengekodan Video Compression @@ -915,10 +927,10 @@ Core - Image + Gambar - Photo + Foto GPS @@ -930,22 +942,22 @@ Audio - Music + Muzik Video - Document + Dokumen - Address + Alamat Selection options - Select + Pilih Recycle bin item @@ -954,10 +966,10 @@ Shortcut item - Drives + Pemacu - Move here + Alih ke sini Safe to remove hardware @@ -975,13 +987,13 @@ Eject - Duplicate tab + Gandakan tab - Move tab to new window + Pindahkan tab ke tetingkap baharu - New tab + Tab baharu Some properties may contain personal information. @@ -993,76 +1005,76 @@ Check the status of file operations here - Status center + Pusat status Search results in {1} for {0} + + Search results for `{0}` + - Details + Butiran - No + Tidak - Show protected system files + Paparkan fail sistem yang dilindungi - Sync layout and sorting preferences across directories + Selaraskan susun atur dan keutamaan pengisihan di semua direktori - Customize the right click context menu + Ubah suai menu konteks klik kanan Original path - Add + Tambah - Edit + Sunting - Remove + Alih keluar - Open tabs in dual pane mode + Buka tab dalam mod dwipanel - Show option to open folders in a new pane + Tunjukkan pilihan buka dalam anak tetingkap baharu - Show option to copy path + Tunjukkan pilihan salin laluan - Show option to create folder with selection + Tunjukkan pilihan untuk buat folder dengan pilihan - Show option to create shortcut + Tunjukkan pilihan buat pintasan - New pane + Anak tetingkap baharu Open in new pane - Files & folders + Fail & Folder - Details (Ctrl+Shift+1) - - - Tiles (Ctrl+Shift+2) + Butiran (Ctrl+Shift+1) - Date deleted + Tarikh Dihapuskan - Cloud Drives + Pemacu Awan - Confirm + Sahkan Desired name @@ -1080,49 +1092,64 @@ Toggle the info pane - Toggle the info pane to view the detail/preview panes + Toggle visibility of the detail/preview panes - Toggle the Toolbar + Toggle toolbar + + + Toggle visibility of the toolbar + + + Toggle filter header + + + Toggle visibility of the filter header + + + Toggle dual pane + + + Toggle dual pane mode No preview available - Item Name + Nama Item Unpin from Sidebar - Network + Rangkaian - File details + Butiran fail - File preview + Pratonton fail Selected file preview pane - Feedback + Maklum balas Available when online - Documentation + Dokumentasi - Available offline + Tersedia di luar talian Partially available offline - Syncing + Menyegerak Excluded from sync @@ -1134,7 +1161,7 @@ Unknown - If you change a file extension, the file might become unusable. Are you sure you want to change it? + Jika anda menukar sambungan fail, fail mungkin tidak boleh digunakan. Adakah anda pasti mahu menukarnya? CD ROM Drive @@ -1176,10 +1203,10 @@ Map network drive - Pinned + Disemat - Libraries + Pusaka No item selected @@ -1191,7 +1218,7 @@ Arguments - Default + Lalai Item count @@ -1230,7 +1257,7 @@ The name must not contain the following characters: \ / : * ? " < > | - Using this name is not allowed! + Penggunaan nama ini tidak dibenarkan! Library with the same name already exists! @@ -1238,6 +1265,12 @@ Open Storage Sense + + Open the Storage Sense page in Windows Settings + + + Cleanup + Copy @@ -1287,7 +1320,7 @@ Generate new name - Create folder with selection + Cipta folder dengan pilihan Type: @@ -1320,10 +1353,10 @@ Date modified - Original folder + Folder asal - Descending + Menurun Huge @@ -1332,13 +1365,13 @@ Very large - Large + Besar - Medium + Sederhana - Small + Kecil Tiny @@ -1380,10 +1413,10 @@ {0} items - Reopen closed tab + Buka semula tab - Rename + Namakan semula Privacy @@ -1398,46 +1431,46 @@ Clear - Widgets + Widget - Sync status + Status penyegerakan Security - Advanced permissions + Kebenaran lanjutan - Allow + Benarkan Deny - Full control + Kawalan penuh List directory contents - Modify + Ubah suai Permissions for {0} - Read and execute + Baca dan laksanakan - Read + Baca Group or user names - Write + Tulis Unknown account @@ -1458,7 +1491,7 @@ Open destination folder when complete - Extract to {0}\ + Ekstrak ke {0}\ Create new library @@ -1587,7 +1620,7 @@ Replace all child object permissions entries with inheritable permission entries from this object - Close active pane + Close pane Enter compact overlay @@ -1626,7 +1659,7 @@ WSL - Hide {0} section + Sembunyikan bahagian {0} Add file @@ -1644,16 +1677,7 @@ Update - Home - - - Ctrl+Shift+1 - - - Ctrl+Shift+2 - - - Ctrl+Shift+6 + Rumah The archive extraction completed successfully. @@ -1710,16 +1734,16 @@ Create link in {0} - Columns + Kolum - - Tiles + + Cards - Open folders in new tab + Buka folder dalam tab baharu - Status center + Pusat status Date created column @@ -1728,7 +1752,7 @@ Item size column - Experimental feature flags + Ciri eksperimental Help and support @@ -1740,7 +1764,7 @@ Submit bug report - Set Files as the default file manager + Tetapkan Files sebagai tetapan lalai untuk pengurusan fail Use Files as Open File Dialog @@ -1749,16 +1773,16 @@ Tag - Open items with a single click + Open files with a single click Folder path - Export settings + Eksport tetapan - Import settings + Import tetapan Couldn't import settings. The settings file is corrupted. @@ -1782,22 +1806,22 @@ Restore default - Open tab in existing instance when opening Files from another app + Buka tab dalam instance sedia ada apabila membuka Files dari aplikasi lain - Startup settings + Tetapan permulaan Compatibility - None + Tiada - On Windows login + Semasa log masuk Windows - On this program start + Semasa program ini dimulakan Sistem @@ -1818,7 +1842,7 @@ Run in 640 x 480 screen resolution - Override high DPI scaling behavior + Gantikan tingkah laku penskalaan DPI Reduced color mode @@ -1826,6 +1850,18 @@ Register this program for restart + + Start window + + + Normal window + + + Minimized + + + Maximized + Run as administrator @@ -1833,10 +1869,10 @@ Run compatibility troubleshooter - Use DPI settings of the main monitor + Gunakan tetapan DPI monitor utama - Do not adjust DPI + Jangan laraskan DPI Do not override DPI @@ -1851,7 +1887,10 @@ Third party libraries - This option modifies the system registry and can have unexpected side effects on your device. Continue at your own risk. + Pilihan ini mengubah suai pendaftaran sistem dan boleh mempunyai kesan sampingan yang tidak dijangka pada peranti anda. Teruskan atas risiko anda sendiri. + + + The flatten operations are permanent and not recommended. Continue at your own risk. Create Library @@ -1863,10 +1902,10 @@ Calculate folder sizes - Recent files + Fail terkini - Open Files on Windows startup + Buka Files semasa Windows bermula Attributes @@ -1884,7 +1923,7 @@ Cleanup your drive contents - Type + Jenis Bytes @@ -1893,10 +1932,10 @@ Extract - Extract files + Ekstrak fail - Extract here + Ekstrak di sini Ctrl+E @@ -1908,7 +1947,7 @@ Run script - Run with PowerShell + Jalankan dengan PowerShell Calculating folder sizes is resource intensive and may cause your CPU usage to increase. @@ -1920,28 +1959,31 @@ Rotate right - Install + Pasang - Close tabs to the left + Tutup tab di sebelah kiri - Close tabs to the right + Tutup tab di sebelah kanan - Close other tabs + Tutup tab lain + + + Close all tabs - Music + Muzik - Pictures + Gambar Update Files - Tags + Tag Universal @@ -1965,13 +2007,13 @@ Custom - Set as desktop slideshow + Tetapkan sebagai tayangan slaid desktop Size all columns to fit - Adaptive layout + Susun atur penyesuaian Adaptive layout (Ctrl+Shift+7) @@ -1985,14 +2027,26 @@ Behaviors - - Review Files + + Hello! + + + Enjoying Files? Please consider reviewing in the Microsoft Store. + + + Enjoying Files? Please consider supporting the project on GitHub. + + + Sponsor - - Would you like to review Files? + + Rate us + + + Dismiss - Set as background + Tetapkan sebagai latar belakang Set as desktop background @@ -2001,31 +2055,46 @@ Set as default - Date column + Tarikh kolum - Date created column + Tarikh kolum dibuat - Size column + Saiz Kolum - Tag column + Kolum Tag - Type column + Kolum Taip - Lockscreen + Skrin Kunci - - Open folders with a single click in the Columns Layout + + Open folders with a single click - Opening items + Membuka item - Compress + Mampatkan + + + Move all contents from subfolders into the selected location + + + Flatten folder + + + Flatten + + + Flattening a folder will move all contents from its subfolders to the selected location. This operation is permanent and cannot be undone. By using this experimental feature, you acknowledge the risk and agree not to hold the Files team responsible for any data loss. + + + Show flatten options Select files and folders when hovering over them @@ -2037,7 +2106,7 @@ Restore all items - Do you want to restore the {0} selected item(s)? + Do you want to restore the {0, plural, one {selected item} other {{0} selected items}}? Restore selection @@ -2060,6 +2129,12 @@ Archive password + + Encoding + + + {0} (detected) + Path @@ -2097,14 +2172,29 @@ Fast - None + Tiada Splitting size - + Do not split + + Auto + + + Dictionary size + + + Word size + + + Estimated memory usage: {0} + + + Available memory: {0} + CD @@ -2130,16 +2220,16 @@ Sync status column - Group by + Kumpul ikut - Sort by + Isih ikut Group in descending order - Sort in descending order + Isih mengikut susunan menurun Create a new shortcut @@ -2157,10 +2247,16 @@ Recently used files is currently disabled in Windows File Explorer. - Edit settings file + Edit fail tetapan + + + Open settings file in your default editor - - What's new + + Release Notes + + + Open Release Notes Creating a shortcut in this location requires administrator privileges @@ -2181,22 +2277,22 @@ View more - Show edit tags flyout + Tunjukkan panel sunting tag - Show compression options + Tunjukkan pilihan pemampatan - Show send to menu + Tunjukkan menu Hantar ke - Show option to open folders in a new tab + Tunjukkan pilihan buka dalam tab baharu - Show option to open folders in a new window + Tunjukkan pilihan buka dalam tetingkap baharu - Quick access + Akses pantas Enter your credentials to connect to: {0} @@ -2217,19 +2313,19 @@ Windows version - Always + Selalu - Permanent deletion only + Hanya pemadaman kekal - Never + Jangan akan Tag color - New tag + Tag baharu Create new tag @@ -2238,28 +2334,25 @@ Loading... - Context menu options + Pilihan menu konteks - Show file extensions - - - Show hidden items + File extensions - Format... + Format - Help + Bantuan - Toggle full screen + Full screen Are you sure you want to delete this tag? - Play all + Play Height @@ -2319,7 +2412,7 @@ Increase size - Toggle sort direction + Sort direction Invalid name @@ -2328,37 +2421,40 @@ Name must not be empty or start or end with a period. - Videos + Video - Launch preview popup + Preview popup Toggle compact overlay - Open online help page in browser + Buka halaman bantuan atas talian melalui pelayar anda - Toggle full screen + Togol mod skrin penuh - Enter compact overlay + Masuk mod tindihan padat - Exit compact overlay + Keluar mod tindihan padat - Toggle compact overlay + Togol mod tindihan padat - Go to search box + Mulakan carian dalam OmniBar - Toggle whether to show hidden items + Toggle visibility of hidden items + + + Toggle visibility of dot files - Toggle whether to show file extensions + Toggle visibility of file extensions Toggle the preview pane to view file previews @@ -2367,52 +2463,64 @@ Toggle whether to show sidebar - Copy item(s) to clipboard + Copy selected {0, plural, one {item} other {items}} - Copy path of selected items to the clipboard + Copy path of the current directory + + + Copy path of selected items + + + Copy path of selected items with quotes - Copy path of selected items with quotes to the clipboard + Copy path of the current directory with quotes - Cut item(s) to clipboard + Cut selected {0, plural, one {item} other {items}} - Paste item(s) from clipboard to current folder + Paste items to the current folder + + + Paste items to the current folder as shortcuts - Paste item(s) from clipboard to selected folder + Paste items to the selected folder + + + Paste to selected folder - Delete item(s) + Delete selected {0, plural, one {item} other {items}} Create new folder - Create new shortcut(s) to selected item(s) + Create new {0, plural, one {shortcut} other {shortcuts}} to selected {0, plural, one {item} other {items}} Create new shortcut to any item - Empty recycle bin + Empty the contents of Recycle Bin Open "Format Drive" menu for selected item - Restore selected item(s) from recycle bin + Restore selected {0, plural, one {item} other {items}} from recycle bin Restore all items from recycle bin - Open item(s) + Open {0, plural, one {item} other {items}} - Open item(s) with selected application + Open {0, plural, one {item} other {items}} with selected application Open parent folder of searched item @@ -2427,28 +2535,28 @@ Select all items - Invert item selection + Invert selected items - Clear item selection + Clear selected items Toggle item selection - Share selected file(s) with others + Share selected {0, plural, one {file} other {files}} with others - Pin item(s) to the Start Menu + Pin {0, plural, one {item} other {items}} to the Start Menu - Unpin item(s) from the Start Menu + Unpin {0, plural, one {item} other {items}} from the Start Menu - Pin folder(s) to Sidebar + Pin {0, plural, one {folder} other {folders}} to Sidebar - Unpin folder(s) from Sidebar + Unpin {0, plural, one {folder} other {folders}} from Sidebar Set selected picture as desktop background @@ -2462,14 +2570,23 @@ Set selected picture as the app background + + Install font + + + Install driver + + + Install certificate + - Install selected font(s) + Install selected {0, plural, one {font} other {fonts}} - Install driver(s) using selected inf file(s) + Install {0, plural, one {driver} other {drivers}} using selected inf {0, plural, one {file} other {files}} - Install selected certificate(s) + Install selected {0, plural, one {certificate} other {certificates}} Run selected application as administrator @@ -2484,28 +2601,28 @@ Launch preview in popup window - Create archive with selected item(s) + Create archive with selected {0, plural, one {item} other {items}} - Create 7z archive instantly with selected item(s) + Create 7z archive with selected {0, plural, one {item} other {items}} - Create zip archive instantly with selected item(s) + Create zip archive with selected {0, plural, one {item} other {items}} - Extract items from selected archive(s) to any folder + Extract selected {0, plural, one {archive} other {archives}} to any folder - Extract items from selected archive(s) to current folder + Extract selected {0, plural, one {archive} other {archives}} to the current folder - Extract items from selected archive(s) to new folder + Extract selected {0, plural, one {archive} other {archives}} to new folder - Rotate selected image(s) to the left + Rotate selected {0, plural, one {image} other {images}} to the left - Rotate selected image(s) to the right + Rotate selected {0, plural, one {image} other {images}} to the right Open settings page @@ -2525,14 +2642,14 @@ Switch to details view - - Switch to tiles view + + Switch to cards view Switch to list view - List + Senarai Grid @@ -2625,13 +2742,13 @@ Toggle group sort direction - Open new tab + Buka tab baru - Navigate backward in navigation history + Navigate backward - Navigate forward in navigation history + Navigate forward Navigate up one directory @@ -2660,8 +2777,11 @@ Close tabs other than selected tab + + Close all tabs including the current tab + - Reopen last closed tab + Reopen recently closed tab Move to the previous tab @@ -2673,13 +2793,13 @@ Close current tab - Close active pane + Close the active pane Focus other pane - Switch focus to the non active pane + Switch focus to the other pane Toggle the sidebar @@ -2745,7 +2865,7 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Home + Rumah Key name for hotkeys in menus. Use abbreviation if possible. @@ -2781,7 +2901,7 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Help + Bantuan Key name for hotkeys in menus. Use abbreviation if possible. @@ -2881,22 +3001,22 @@ This PC - Recycle Bin + Tong Kitar Semula Untagged - Moves to the previous tab + Previous tab - Moves to the next tab + Next tab - Closes current tab + Close tab - Edit path + Sunting laluan Redo @@ -2923,7 +3043,7 @@ Show checkboxes when selecting items - Focus path bar + Sunting laluan dalam OmniBar Create new item @@ -2938,7 +3058,7 @@ Delete permanently - Delete item(s) permanently + Delete selected {0, plural, one {item} other {items}} permanently Play the selected media files @@ -3027,6 +3147,15 @@ You have uncommitted changes on this branch. What would you like to do with them? + + You have an ongoing merge with unresolved conflicts. Please resolve the conflicts or abort the merge to switch branches. + + + Abort merge and switch to '{0}' + + + Stay on '{0}' and resolve conflicts + Switch branch @@ -3055,13 +3184,19 @@ Switch to new branch - Create a folder with the currently selected item(s) + Create a folder with the currently selected {0, plural, one {item} other {items}} - Open properties + Sifat + + + Open File Explorer properties - Open properties window + Buka tetingkap sifat + + + Buka tetingkap sifat Penjelajah Fail Locals @@ -3081,6 +3216,9 @@ Run git fetch + + Clone a git repo + Run git pull @@ -3116,7 +3254,7 @@ Git - Acrylic + Akrilik Mica @@ -3125,7 +3263,7 @@ Mica Alt - Backdrop Material + Backdrop Solid @@ -3157,17 +3295,17 @@ Files cannot access GitHub right now. - - Open folder in VS Code + + Open folder in {0} - - Open the current directory in Visual Studio Code + + Open the current directory in {0} - - Open repo in VS Code + + Open repo in {0} - - Open the root of the Git repo in Visual Studio Code + + Open the root of the Git repo in {0} Copy code @@ -3194,7 +3332,7 @@ Initialize repo - Initialize a Git repository + Initialize current folder as a git repository Remote Repository Name @@ -3209,13 +3347,13 @@ Sort items by path - Open directory in new pane + Open selected directory in a new pane - Open directory in new tab + Open selected directory in a new tab - Open directory in new window + Open selected directory in a new window Open all @@ -3240,10 +3378,10 @@ The '{0}' command is not ready to be executed. - Command palette + Command Palette - Open command palette + Open Command Palette in the OmniBar Start in: @@ -3270,86 +3408,86 @@ Due to a limitation with Windows and WinAppSdk, drag and drop isn't available when running Files as admin. If you wish to use drag and drop, you can work around this limitation by opening UAC (User Account Control) from the Start Menu and selecting the third level, and restarting Windows. Otherwise, you can keep using Files without drag & drop. - Leave app running in the background when the window is closed + Biarkan aplikasi berjalan di latar belakang apabila tertingkap ditutup - Blue + Biru One of the custom color themes - Blue Gray + Kelabu Biru One of the custom color themes - Brick Red + Merah Batu One of the custom color themes - Camouflage + Samar One of the custom color themes - Cool Blue Bright + Biru Sejuk Cerah One of the custom color themes - Gray + Kelabu One of the custom color themes - Gray Dark + Kelabu Gelap One of the custom color themes - Green + Hijau One of the custom color themes - Iris Pastel + Pastel Iris One of the custom color themes - Mint Light + Pudina Cerah One of the custom color themes - Mod Red + Merah mod One of the custom color themes - Orange Bright + Oren Cerah One of the custom color themes - Overcast + Mendung One of the custom color themes - Red + Merah One of the custom color themes - Rose Bright + Ros Cerah One of the custom color themes - Seafoam + Buih Laut One of the custom color themes - Storm + Ribut One of the custom color themes - Violet Red Light + Merah Lembayung Cerah One of the custom color themes - Yellow Gold + Kuning Emas One of the custom color themes - Clear completed + Kosongkan yang telah selesai Name: @@ -3359,7 +3497,7 @@ Shown in a StatusCenter card. Used as "64 MB of 1 GB processed" - items + item Files can't initialize this directory as a Git repository. @@ -3373,6 +3511,9 @@ Processing items... + + Discovering items... + Speed: @@ -3385,61 +3526,129 @@ Shown in a StatusCenter card. - Compressed {0} item(s) to "{1}" + Compressed {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Compressed {0} item(s) from "{1}" to "{2}" + Compressed {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error compressing {0} item(s) to "{1}" + Error compressing {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to compress {0} item(s) from "{1}" to "{2}" + Failed to compress {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Compressing {0} item(s) to "{1}" + Compressing {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Compressing {0} item(s) from "{1}" to "{2}" + Compressing {0, plural, one {# item} other {# items}} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Canceled cloning {0} to "{1}" + Shown in a StatusCenter card. + + + Canceled cloning {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Cloned "{0}" to "{1}" + Shown in a StatusCenter card. + + + Cloned {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Error cloning "{0}" to "{1}" + Shown in a StatusCenter card. + + + Failed to clone {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Cloning "{0}" to "{1}" + Shown in a StatusCenter card. + + + Cloning {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Canceled installing {0} fonts + Shown in a StatusCenter card. + + + Canceled installing {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Error installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Failed to install {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} from "{1}" Shown in a StatusCenter card. - Canceled copying {0} item(s) to "{1}" + Canceled copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Canceled copying {0} item(s) from "{1}" to "{2}" + Canceled copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Copied {0} item(s) to "{1}" + Copied {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Copied {0} item(s) from "{1}" to "{2}" + Copied {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error copying {0} item(s) to "{1}" + Error copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to copy {0} item(s) from "{1}" to "{2}" + Failed to copy {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Copying {0} item(s) to "{1}" + Copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Copying {0} item(s) from "{1}" to "{2}" + Copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. + + Discovered {0, plural, one {# item} other {# items}} + Shown in a StatusCenter card during file discovery phase. + Canceled extracting "{0}" to "{1}" Shown in a StatusCenter card. @@ -3473,55 +3682,55 @@ Shown in a StatusCenter card. - Canceled deleting {0} item(s) from "{1}" + Canceled deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Deleted {0} item(s) from "{1}" + Deleted {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Error deleting {0} item(s) from "{1}" + Error deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Failed to delete {0} item(s) from "{1}" + Failed to delete {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Deleting {0} item(s) from "{1}" + Deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Canceled moving {0} item(s) to "{1}" + Canceled moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Canceled moving {0} item(s) from "{1}" to "{2}" + Canceled moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Moved {0} item(s) to "{1}" + Moved {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Moved {0} item(s) from "{1}" to "{2}" + Moved {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Moving {0} item(s) to "{1}" + Moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Moving {0} item(s) from "{1}" to "{2}" + Moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error moving {0} item(s) to "{1}" + Error moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to move {0} item(s) from "{1}" to "{2}" + Failed to move {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. @@ -3549,14 +3758,14 @@ Shown in a StatusCenter card. - {0}/{1} item(s) processed + {0}/{1, plural, one {# item} other {# items}} processed Shown in a StatusCenter card. Used as "8/20 items processed" Unblock downloaded file - No ongoing file operations + Tiada operasi fail yang sedang dijalankan The option to open Files on Windows Startup is not available due to your system settings or group policy. To re-enable, open the startup page in Task Manager. @@ -3567,6 +3776,9 @@ Failed to set the background wallpaper + + Failed to open the settings file + Delete Git branch @@ -3592,31 +3804,31 @@ There was an error applying this tag - Extract items from selected archive(s) to current folder for single-item archive, or to new folder for multi-item archive + Extract selected {0, plural, one {archive} other {archives}} here for single-item or to new folder for multi-item - Extract here (Smart) + Ekstrak di sini (Pintar) - Sort files and folders together + Isih fail dan folder bersama - Sort files and folders together + Sort files and folders in the same list - Sort files first + Isih fail dahulu Sort files first then folders - Sort folders first + Isih folder dahulu Sort folders first then files - Sort priority + Keutamaan pengisihan Failed to rotate the image @@ -3637,7 +3849,7 @@ Open new window - Change album cover + Tukar kulit album Failed to rename item @@ -3657,59 +3869,56 @@ Questions & discussions - - Additional sizes are not yet available for the Tiles View. - - Compact + Padat Used to describe layout sizes - Small + Kecil Used to describe layout sizes - Medium + Sederhana Used to describe layout sizes - Medium + + Sederhana + Used to describe layout sizes - Medium ++ + Sederhana ++ Used to describe layout sizes - Medium +++ + Sederhana +++ Used to describe layout sizes - Medium ++++ + Sederhana ++++ Used to describe layout sizes - Medium +++++ + Sederhana +++++ Used to describe layout sizes - Large + Besar Used to describe layout sizes - Large + + Besar + Used to describe layout sizes - Large ++ + Besar ++ Used to describe layout sizes - Large +++ + Besar +++ Used to describe layout sizes - Extra large + Sangat besar Used to describe layout sizes @@ -3719,7 +3928,7 @@ Layout type - Actions + Tindakan Commands @@ -3731,7 +3940,7 @@ Restore defaults - Choose an action + Pilih tindakan Are you sure you want to restore the default key bindings? This action cannot be undone. @@ -3749,51 +3958,51 @@ Adaptive layout is not available when preferences are synced, the default layout has been changed to Details. - Background image + Imej latar belakang - Bottom + Bawah Image alignment type - Center + Tengah Image alignment type - Fill + Isi Image stretch type - Horizontal alignment + Penjajaran mendatar - Image fit + Padanan imej - Left + Kiri Image alignment type - Opacity + Kelegapan - Right + Kanan Image alignment type - Top + Atas Image alignment type - Uniform + Seragam Image stretch type - Uniform to fill + Isi penuh secara seragam Image stretch type - Vertical alignment + Penjajaran menegak Thin Acrylic @@ -3821,6 +4030,10 @@ ICO File This is the friendly name for ICO files. + + ICL File + This is the friendly name for ICL files. + Zip File This is the friendly name for ZIP files. @@ -3829,6 +4042,10 @@ Bitmap Files This is the friendly name for bitmap files. + + Image Files + This is the friendly name for image files. + Num @@ -3854,23 +4071,23 @@ Status: - Show Toolbar - Setting that controls if the Toolbar is shown in the main view + Show toolbar + Setting that controls if the toolbar is shown in the main view Tab actions menu - - Add vertical pane + + Split vertically - Add a vertical pane + Split pane vertically - - Add horizontal pane + + Split horizontally - - Add a horizontal pane + + Split pane horizontally Arrange vertically @@ -3884,8 +4101,8 @@ Arrange panes horizontally - - Add pane + + Split pane Show tab actions button in the title bar @@ -3893,8 +4110,11 @@ Arrange panes - - Default pane arrangement + + Default dual pane split direction + + + Dual pane mode Horizontal @@ -3902,4 +4122,259 @@ Vertical + + Show Files icon in the System Tray + + + CPU threads + + + Navigate to the home page + + + Toolbars + + + User ID + + + Bulk rename + + + Compress contents + + + Show option to create alternate data stream + + + Create alternate data stream + + + Create alternate data stream for the selected {0, plural, one {item} other {items}} + + + Enter data stream name + + + There was an error creating the alternate data stream + + + Please note that alternate data streams only work on drives formatted as NTFS. + + + Alternate data streams are currently hidden + + + Would you like to display alternate data streams? You can modify this setting anytime from the files and folders settings page. + + + Manage tags + + + Available + + + Total + + + Always switch focus to newly created tab + + + Toggle shelf pane + + + Toggle the visibility of the shelf pane + + + Show shelf pane toggle in address bar + + + Shelf + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + Clear items + + + Remove from shelf + + + Add to shelf + Tooltip that displays when dragging items to the Shelf Pane + + + Enter a hash to compare + Placeholder that appears in the compare hash text box + + + Matches {0} + Appears when two compared hashes match, e.g. "Matches SHA256" + + + No matches found + Appears when two compared hashes don't match + + + Path or alias + + + Invalid path + + + Test integration + + + {0} could not be located. Please check your settings and try again. + + + The configured IDE could not be located + + + Open settings + + + Visual Studio Code + + + Enter a path or launch alias + + + Please enter a name for the IDE + + + Clone repo + Clone repo dialog title + + + Clone + Primary action button in the clone repo dialog + + + Repository URL + URL textbox header in the clone repo dialog + + + Cannot clone repo + Cannot clone repo dialog title + + + Compare a file + Button that appears in file hash properties that allows the user to compare two files + + + Size format + + + Binary + + + Decimal + + + You can add sections to the sidebar by right-clicking and selecting the sections you want to add + + + Drag files or folders here to interact with them across different tabs + + + Enter a path to navigate to... + + + Find features and commands... + + + Search for files and folders... + + + During file operations + + + Show status center button + + + Status center progress ring + Screen reader name for the status center progress ring + + + Icon files + This is the friendly name for a variety of different icon files. + + + Show collapsed path breadcrumbs + + + Show folders in {0} + + + Show folders in Home + + + There are no commands containing {0} + + + See more + + + Filtering for + + + Filename + + + Signatures + + + Signature list + + + Issued by: + + + Issued to: + + + Valid from: + + + Valid to: + + + No signature was found. + + + Show option to open folders in Windows Terminal + + + Open log file + + + Open log file in your default editor + + + Open log file location in your default file manager + + + Unable to open the log file + + + Show status bar + + + Only in Columns View + + + New {0} + + + Enable smooth scrolling + + + Scrolling + + + Show option to Pin to Sidebar + + + Show option to Pin to the Start Menu + \ No newline at end of file diff --git a/src/Files.App/Strings/nb-NO/Resources.resw b/src/Files.App/Strings/nb-NO/Resources.resw index 9f3af692f53a..8c47a8436cad 100644 --- a/src/Files.App/Strings/nb-NO/Resources.resw +++ b/src/Files.App/Strings/nb-NO/Resources.resw @@ -123,9 +123,15 @@ Kopier filbanen + + Copy item path + Kopier sti med tilbud + + Copy selected item path with quotes + Bla gjennom @@ -160,7 +166,7 @@ Hopp over - Velg alle + Select all Inverter utvalg @@ -211,7 +217,7 @@ Vis skjulte filer og mapper - Vis punktum-filer + Show dot files Utseende @@ -249,6 +255,9 @@ Lim inn + + Lim inn snarvei + Ny @@ -291,8 +300,8 @@ Angi et navn - - Angi navn + + Opprett ny {0} Lyst @@ -300,11 +309,14 @@ Mørkt + + Use system setting + Applikasjon - systemet + System Ny mappe @@ -337,7 +349,7 @@ Elementet finnes allerede - Sett som låseskjerm bakgrunn + Sett som bakgrunn for låseskjerm Bruk som app bakgrunn @@ -385,40 +397,40 @@ Skrivebeskyttet - {0, plural, one {# day ago} other {# days ago}} + {0, plural, one {# dag siden} other {# dager siden}} - 1 day ago + 1 dag siden - {0} days ago + {0} dager siden - {0, plural, one {# hour ago} other {# hours ago}} + {0, plural, one {# time siden} other {# timer siden}} - 1 hour ago + 1 time siden - {0} hours ago + {0} timer siden - {0, plural, one {# minute ago} other {# minutes ago}} + {0, plural, one{# minutt siden} other {# minutter siden}} - 1 minute ago + 1 minutt siden - {0} minutes ago + {0} minutter siden - {0, plural, one {# second ago} other {# seconds ago}} + {0, plural, one {# sekund siden} other {# sekunder siden}} - 1 second ago + 1 sekund siden - {0} seconds ago + {0} sekunder siden @@ -477,10 +489,10 @@ B - {0, number} {0, plural, one {file} other {files}}, {1, number} {1, plural, one {folder} other {folders}} + {0, number} {0, plural, one {fil} other {filer}}, {1, number} {1, plural, one {mappe} other {mapper}} - {0, number} {0, plural, one {file} other {files}}, {1, number} {1, plural, one {folder} other {folders}} from {2, number} {2, plural, one {location} other {locations}} + {0, number} {0, plural, one {fil} other {filer}}, {1, number} {1, plural, one {mappe} other {mapper}} fra {2, number} {2, plural, one {lokasjon} other {lokasjoner}} Kjør som en annen bruker @@ -515,9 +527,6 @@ Status - - Windows-standard - Ingen resultater @@ -530,6 +539,9 @@ Kopier til {0} + + Clone to {0} + Opprett snarvei @@ -621,7 +633,7 @@ Støtt oss på GitHub - Vi kunne ikke opprettet dette elementet + Vi kunne ikke opprette dette elementet Ingen tilgang @@ -998,6 +1010,9 @@ Søkeresultater i {1} for {0} + + Search results for `{0}` + Detaljer @@ -1052,9 +1067,6 @@ Detaljer (Ctrl+Skift+1) - - Fliser (Ctrl+Skift+2) - Dato slettet @@ -1080,10 +1092,25 @@ Vis/Skjul info-ruten - Skru av/på info-ruten for å se detaljer/forhåndsvisning av rutene + Toggle visibility of the detail/preview panes - Toggle the Toolbar + Toggle toolbar + + + Toggle visibility of the toolbar + + + Toggle filter header + + + Toggle visibility of the filter header + + + Toggle dual pane + + + Toggle dual pane mode Forhåndsvisning ikke tilgjengelig @@ -1238,26 +1265,32 @@ Åpne Storage Sense + + Åpne lagringssensor-siden i Windows Innstillinger + + + Cleanup + Kopier - Copy {0, plural, one {item} other {items}} + Kopier {0, plural, one {element} other {elementer}} - Delete {0, plural, one {item} other {items}} + Slett {0, plural, one {element} other {elementer}} Flytt - {0, plural, one {One item will be moved} other {# items will be moved}} + {0, plural, one {Ett element vil bli flyttet} other {# elementer vil bli flyttet}} - Move {0, plural, one {item} other {items}} + Flytt {0, plural, one {element} other {elementer}} - {0, plural, one {One item will be deleted} other {# items will be deleted}} + {0, plural, one {Ett element vil bli slettet} other {# elementer vil bli slettet}} Fortsett @@ -1380,7 +1413,7 @@ {0} elementer - Åpne lukket fane igjen + Reopen tab Gi nytt navn @@ -1587,7 +1620,7 @@ Erstatt alle rettigheter for underobjekter med arvelige rettigheter fra dette objektet - Close active pane + Close pane Bytt til kompakt visning @@ -1646,15 +1679,6 @@ Hjem - - Ctrl+Skift+1 - - - Ctrl+Skift+2 - - - Ctrl+Skift+6 - Utpakkinger av filer fra arkiv fullført. @@ -1671,7 +1695,7 @@ Ukjent - Rediger fil-merkelapper + Rediger merkelapper Del av sett @@ -1712,8 +1736,8 @@ Kolonner - - Fliser + + Cards Åpne mapper i ny fane @@ -1749,7 +1773,7 @@ Fil-merkelapp - Åpne filer med enkeltklikk + Open files with a single click Mappesti @@ -1794,7 +1818,7 @@ Ingen - Ved Windows innlogging + Ved innlogging i Windows Ved dette programmet start @@ -1826,6 +1850,18 @@ Registrer dette programmet for omstart + + Start window + + + Normal window + + + Minimert + + + Maksimert + Kjør som administrator @@ -1853,6 +1889,9 @@ Denne innstillingen gjør endringer på systemfiler og kan ha uventede sideeffekter på din enhet. Utviklerne tar ikke ansvar dersom det resulterer i problemer. Ved å fortsette med denne innstillingen anerkjenner du risikoen involvert ved å gjøre det. + + The flatten operations are permanent and not recommended. Continue at your own risk. + Opprett bibliotek @@ -1931,6 +1970,9 @@ Lukk andre faner + + Lukk alle faner + Musikk @@ -1985,11 +2027,23 @@ Oppførsel - - Anmeld Files + + Hello! + + + Enjoying Files? Please consider reviewing in the Microsoft Store. + + + Enjoying Files? Please consider supporting the project on GitHub. - - Har du lyst å gi Files en anmeldelse? + + Sponsor + + + Rate us + + + Dismiss Bruk som bakgrunn @@ -2018,8 +2072,8 @@ Låseskjerm - - Åpne mapper med et enkelt klikk i Kolonneoppsettet + + Open folders with a single click Åpner elementer @@ -2027,6 +2081,21 @@ Komprimer + + Move all contents from subfolders into the selected location + + + Flatten folder + + + Flatten + + + Flattening a folder will move all contents from its subfolders to the selected location. This operation is permanent and cannot be undone. By using this experimental feature, you acknowledge the risk and agree not to hold the Files team responsible for any data loss. + + + Show flatten options + Velg filer og mapper ved å holde musepekeren over dem @@ -2037,7 +2106,7 @@ Gjenopprett alle elementer - Vil du gjenopprette de valgte {0} elementen(e)? + Do you want to restore the {0, plural, one {selected item} other {{0} selected items}}? Gjenopprett utvalgte @@ -2058,7 +2127,13 @@ Du har ikke tilgang til denne mappen. - Arkiver passord + Arkivpassord + + + Encoding + + + {0} (detected) Sti @@ -2102,8 +2177,23 @@ Dele størrelse - - Ikke del + + Do not split + + + Auto + + + Dictionary size + + + Word size + + + Estimated memory usage: {0} + + + Available memory: {0} CD @@ -2159,8 +2249,14 @@ Rediger innstillingsfilen - - Hva er nytt + + Open settings file in your default editor + + + Release Notes + + + Open Release Notes Oppretting av en snarvei på denne plasseringen krever administratorrettigheter @@ -2181,7 +2277,7 @@ Vis mer - Vis menyen for redigering av tagger + Vis menyen for redigering av merkelapper Vis komprimeringsalternativer @@ -2211,7 +2307,7 @@ Feil i nettverksmappen - App versjon + App-versjon Windows versjon @@ -2241,25 +2337,22 @@ Sammenhengsmeny alternativer - Vis filtyper - - - Vis skjulte elementer + File extensions - Formatering... + Format Hjelp - Skru av/på Fullskjerm + Full screen Er du sikker på at du vil slette denne taggen? - Spill av alle + Spill av Høyde @@ -2295,7 +2388,7 @@ Klarte ikke beregne hash, lukk filen og prøv på nytt. - Hashes er ikke tilgjengelig for online filer + Hasher er ikke tilgjengelig for online filer Algoritme @@ -2319,7 +2412,7 @@ Øk størrelse - Velg sorteringsrekkefølge + Sort direction Ugyldig navn @@ -2331,34 +2424,37 @@ Videoer - Start forhåndsvisnings popup + Preview popup Kompakt overlegg av/på - Åpne hjelpeside i nettleseren + Åpne online hjelpeside i nettleseren - Skru av/på Fullskjerm + Toggle full screen mode - Bytt til kompakt visning + Enter compact overlay mode - Avslutt kompakt visning + Exit compact overlay mode - Kompakt overlegg av/på + Toggle compact overlay mode - Gå til søkefeltet + Start search in the OmniBar - Velg om du vil vise skjulte elementer + Toggle visibility of hidden items + + + Toggle visibility of dot files - Velg om filendelser skal vises + Toggle visibility of file extensions Veksle forhåndsvisningsruten for å vise forhåndsvisninger av filen @@ -2367,52 +2463,64 @@ Velg om du vil vise sidepanelet - Kopier objekt(er) til utklippstavlen + Copy selected {0, plural, one {item} other {items}} - Kopier banen til de valgte elementene til utklippstavlen + Copy path of the current directory + + + Copy path of selected items + + + Copy path of selected items with quotes - Kopier banen til de valgte elementene med sitater til utklippstavlen + Copy path of the current directory with quotes - Klipp ut element(er) til utklippstavlen + Cut selected {0, plural, one {item} other {items}} - Lim inn element(er) fra utklippstavle til gjeldende mappe + Lim inn elementer i den gjeldende mappen + + + Lim elementer inn i den gjeldende mappen som snarveier - Lim inn element(er) fra utklippstavle til valgt mappe + Lim inn elementer i den valgte mappen + + + Lim inn i valgt mappe - Slett element(er) + Delete selected {0, plural, one {item} other {items}} Lag ny mappe - Lag nye snarveier til valgt(e) element(er) + Create new {0, plural, one {shortcut} other {shortcuts}} to selected {0, plural, one {item} other {items}} Lag ny snarvei til et hvilket som helst element - Tøm papirkurven + Tøm innholdet i papirkurven Åpne "Formater stasjon" meny for valgt element - Gjenopprett valgte element(er) fra papirkurven + Gjenopprett {0, plural, one {valgt element} other {valgte elementer}} fra papirkurven Gjenopprett alle elementer fra papirkurven - Åpne element(er) + Åpne {0, plural, one {element} other {elementer}} - Åpne element(er) med valgt program + Åpne {0, plural, one {element} other {elementer}} med valgt applikasjon Åpne overordnet mappe for søkt element @@ -2427,28 +2535,28 @@ Velg alle elementer - Inverter element valg + Invert selected items - Tøm element valg + Clear selected items Veksle elementvalg - Del valgte fil(er) med andre + Share selected {0, plural, one {file} other {files}} with others - Sideveksling(er) til Startmenyen + Pin {0, plural, one {item} other {items}} to the Start Menu - Løsne elementer fra Startmenyen + Unpin {0, plural, one {item} other {items}} from the Start Menu - Fest mappe(r) til sidestolpe + Pin {0, plural, one {folder} other {folders}} to Sidebar - Løsne mappe(r) fra sidestolpe + Unpin {0, plural, one {folder} other {folders}} from Sidebar Angi valgt bilde som skrivebordsbakgrunn @@ -2462,14 +2570,23 @@ Angi valgt bilde som appbakgrunn + + Install font + + + Install driver + + + Install certificate + - Installer valgte skrift(er) + Install selected {0, plural, one {font} other {fonts}} - Installer driver(e) ved hjelp av valgte fil(er) + Install {0, plural, one {driver} other {drivers}} using selected inf {0, plural, one {file} other {files}} - Installer valgte sertifikat(er) + Install selected {0, plural, one {certificate} other {certificates}} Kjør valgt program som administrator @@ -2484,28 +2601,28 @@ Start forhåndsvisning i popup-vindu - Opprett arkiv med valgte element(er) + Create archive with selected {0, plural, one {item} other {items}} - Lag 7z arkiv umiddelbart med valgte element(er) + Create 7z archive with selected {0, plural, one {item} other {items}} - Lag zip-arkiv umiddelbart med valgte element(er) + Create zip archive with selected {0, plural, one {item} other {items}} - Pakk ut elementer fra valgte arkiv(er) til en hvilken som helst mappe + Extract selected {0, plural, one {archive} other {archives}} to any folder - Pakk ut elementer fra valgte arkiv(er) til gjeldende mappe + Extract selected {0, plural, one {archive} other {archives}} to the current folder - Pakk ut elementer fra valgte arkiv(er) til ny mappe + Extract selected {0, plural, one {archive} other {archives}} to new folder - Roter markerte bilde(r) mot venstre + Rotate selected {0, plural, one {image} other {images}} to the left - Roter markerte bilde(r) til høyre + Rotate selected {0, plural, one {image} other {images}} to the right Åpne innstillingssiden @@ -2525,8 +2642,8 @@ Bytt til detaljvisning - - Bytt til flisvisning + + Switch to cards view Bytt til listevisning @@ -2565,7 +2682,7 @@ Sorter elementer etter synkroniseringsstatus - Sorter elementer etter tagger + Sorter elementer etter merkelapper Sorter elementer etter originalmappe @@ -2604,7 +2721,7 @@ Gruppere elementer etter synkroniseringsstatus - Gruppere elementer etter tagger + Gruppere elementer etter merkelapper Gruppere elementer etter originalmappe @@ -2625,13 +2742,13 @@ Velg sorteringsrekkefølge - Åpne ny fane + Åpne en ny fane - Gå bakover i navigasjonshistorikken + Gå tilbake - Gå forover i navigasjonshistorikken + Gå fremover Naviger opp en mappe @@ -2649,19 +2766,22 @@ Lukk faner til venstre for valgte fane - Lukk faner til høyre for denne fane + Lukk faner til høyre for gjeldende fane Lukk faner til høyre for valgte fane - Lukk faner fra nåværende + Lukk faner bortsett fra gjeldende fane - Lukk faner fra valgte fane + Lukk faner bortsett fra valgte fane + + + Lukk alle faner inkludert gjeldende fane - Åpne sist lukkede fane + Åpne nylig lukket fane Flytt til forrige fane @@ -2670,16 +2790,16 @@ Flytt til neste fane - Lukk nåværende fane + Lukk gjeldende fane - Close active pane + Lukk den aktive ruten - Focus other pane + Fokuser på annen rute - Switch focus to the non active pane + Flytt fokuset til annen rute Slå av/på sidepanelet @@ -2878,22 +2998,22 @@ Vis advarsel ved endring av filendelser - Denne PC + Denne PCen - Resirkuler tkort + Papirkurv Umerket - Flytter til forrige fane + Forrige fane - Flytter til neste fane + Neste fane - Lukk nåværende fane + Lukk fane Endre sti @@ -2923,7 +3043,7 @@ Vis avmerkingsbokser når du velger elementer - Fokus sti linje + Edit path in the OmniBar Lag nytt element @@ -2938,7 +3058,7 @@ Slett permanent - Slett produkt(er) permanent + Delete selected {0, plural, one {item} other {items}} permanently Spill av de valgte mediafilene @@ -3027,6 +3147,15 @@ Har du ubegått endringer på denne grenen. Hva vil du gjøre med dem? + + You have an ongoing merge with unresolved conflicts. Please resolve the conflicts or abort the merge to switch branches. + + + Abort merge and switch to '{0}' + + + Stay on '{0}' and resolve conflicts + Bytt gren @@ -3055,14 +3184,20 @@ Bytt til ny gren - Opprett en mappe med valgte element(er) + Create a folder with the currently selected {0, plural, one {item} other {items}} - Åpne egenskaper + Egenskaper + + + Open File Explorer properties Åpne egenskaper vindu + + Open File Explorer properties window + Lokaler @@ -3081,6 +3216,9 @@ Kjør git henting + + Clone a git repo + Run git pull @@ -3125,7 +3263,7 @@ Mica Alt - Bakdråpe materiale + Bakgrunnsmateriale Solid @@ -3157,23 +3295,23 @@ Filer kan ikke få tilgang til GitHub akkurat nå. - - Åpne mappe i VS-kode + + Åpne mappe i {0} - - Åpne gjeldende mappe i Visual Studio Kode + + Åpne gjeldende mappe i {0} - - Åpne repoen i VS-kode + + Open repo in {0} - - Åpne roten av Git repo i Visual Studio Code + + Open the root of the Git repo in {0} Kopier koden - Lagt + Lagt til Slettet @@ -3194,7 +3332,7 @@ Aktiver repot - Start et Git kodelager + Initialize current folder as a git repository Ekstern datakatalog navn @@ -3209,13 +3347,13 @@ Sorter elementer etter sti - Åpne mappe i nytt område + Open selected directory in a new pane - Åpne mappe i ny fane + Open selected directory in a new tab - Åpne mappe i nytt vindu + Åpne valgt mappe i nytt vindu Åpne alle sider @@ -3240,10 +3378,10 @@ Kommandoen '{0}er ikke klar for å bli utført. - Kommando palett + Command Palette - Åpne kommandoppalett + Open Command Palette in the OmniBar Begynn i: @@ -3373,6 +3511,9 @@ Behandler elementer... + + Discovering items... + Hastighet: @@ -3385,61 +3526,129 @@ Shown in a StatusCenter card. - Compressed {0} item(s) to "{1}" + Compressed {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Compressed {0} item(s) from "{1}" to "{2}" + Compressed {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error compressing {0} item(s) to "{1}" + Error compressing {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to compress {0} item(s) from "{1}" to "{2}" + Failed to compress {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Compressing {0} item(s) to "{1}" + Compressing {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Compressing {0} item(s) from "{1}" to "{2}" + Compressing {0, plural, one {# item} other {# items}} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Canceled cloning {0} to "{1}" + Shown in a StatusCenter card. + + + Canceled cloning {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Cloned "{0}" to "{1}" + Shown in a StatusCenter card. + + + Cloned {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Error cloning "{0}" to "{1}" + Shown in a StatusCenter card. + + + Failed to clone {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Cloning "{0}" to "{1}" + Shown in a StatusCenter card. + + + Cloning {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Canceled installing {0} fonts + Shown in a StatusCenter card. + + + Canceled installing {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Error installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Failed to install {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} from "{1}" Shown in a StatusCenter card. - Kopierte {0} element(er) til "{1}" + Canceled copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Kopierte {0} element(er) fra "{1}til "{2}" + Canceled copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Kopierte {0} element(er) til "{1}" + Copied {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Copied {0} item(s) from "{1}" to "{2}" + Copied {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error copying {0} item(s) to "{1}" + Error copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to copy {0} item(s) from "{1}" to "{2}" + Failed to copy {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Kopierer {0} element(er) til "{1}" + Copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Copying {0} item(s) from "{1}" to "{2}" + Copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. + + Discovered {0, plural, one {# item} other {# items}} + Shown in a StatusCenter card during file discovery phase. + Avbrutt utpakking "{0}" til "{1}" Shown in a StatusCenter card. @@ -3473,55 +3682,55 @@ Shown in a StatusCenter card. - Canceled deleting {0} item(s) from "{1}" + Canceled deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Deleted {0} item(s) from "{1}" + Deleted {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Error deleting {0} item(s) from "{1}" + Error deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Kunne ikke slette {0} element(er) fra "{1}" + Failed to delete {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Sletter {0} element(er) fra "{1}" + Deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Canceled moving {0} item(s) to "{1}" + Canceled moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Canceled moving {0} item(s) from "{1}" to "{2}" + Canceled moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Flyttet {0} element(er) til "{1}" + Moved {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Flyttet {0} element(er) fra "{1}" til "{2}" + Moved {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Moving {0} item(s) to "{1}" + Moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Moving {0} item(s) from "{1}" to "{2}" + Moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error moving {0} item(s) to "{1}" + Error moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Kunne ikke flytte {0} element(er) fra "{1}" til "{2}" + Failed to move {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. @@ -3549,7 +3758,7 @@ Shown in a StatusCenter card. - {0}/{1} element(er) behandlet + {0}/{1, plural, one {# item} other {# items}} processed Shown in a StatusCenter card. Used as "8/20 items processed" @@ -3567,6 +3776,9 @@ Kunne ikke angi bakgrunnsbilde + + Failed to open the settings file + Slett Git gren @@ -3583,16 +3795,16 @@ Koble til GitHub - Innlogging + Logg inn - Merker er for tiden bare kompatible på drivverk formatert som NTFS. + Merkelapper er for tiden bare kompatible med disker formatert som NTFS. Det oppstod en feil ved å legge til denne taggen - Pakk ut elementer fra valgte arkiv(er) til gjeldende mappe for enkeltelementsarkiv, eller til ny mappe for flerelementsarkiv + Extract selected {0, plural, one {archive} other {archives}} here for single-item or to new folder for multi-item Pakk ut her (mart) @@ -3601,7 +3813,7 @@ Sorter filer og mapper sammen - Sorter filer og mapper sammen + Sort files and folders in the same list Sorter filer først @@ -3649,17 +3861,14 @@ Redigere tillatelser - Filene kjører fortsatt i bakgrunnen for at ytelsen kan forbedres. + Files kjører fortsatt i bakgrunnen for raskere oppstart. - Hvor gikk filene? + Hvor ble det av Files? Spørsmål og diskusjoner - - Ytterligere størrelser er ennå ikke tilgjengelig for Flisvisning. - Kompakt Used to describe layout sizes @@ -3821,6 +4030,10 @@ ICO fil This is the friendly name for ICO files. + + ICL File + This is the friendly name for ICL files. + Pakk fil This is the friendly name for ZIP files. @@ -3829,6 +4042,10 @@ Bitmap filer This is the friendly name for bitmap files. + + Image Files + This is the friendly name for image files. + Num @@ -3854,23 +4071,23 @@ Status: - Show Toolbar - Setting that controls if the Toolbar is shown in the main view + Show toolbar + Setting that controls if the toolbar is shown in the main view Tab actions menu - - Add vertical pane + + Split vertically - Add a vertical pane + Split pane vertically - - Add horizontal pane + + Split horizontally - - Add a horizontal pane + + Split pane horizontally Arrange vertically @@ -3884,8 +4101,8 @@ Arrange panes horizontally - - Add pane + + Split pane Show tab actions button in the title bar @@ -3893,8 +4110,11 @@ Arrange panes - - Default pane arrangement + + Default dual pane split direction + + + Dual pane mode Horizontal @@ -3902,4 +4122,259 @@ Vertical + + Vis Files-ikon i systemstatusfeltet + + + CPU threads + + + Navigate to the home page + + + Toolbars + + + User ID + + + Gi nytt navn til flere elementer + + + Compress contents + + + Show option to create alternate data stream + + + Create alternate data stream + + + Create alternate data stream for the selected {0, plural, one {item} other {items}} + + + Enter data stream name + + + There was an error creating the alternate data stream + + + Please note that alternate data streams only work on drives formatted as NTFS. + + + Alternate data streams are currently hidden + + + Would you like to display alternate data streams? You can modify this setting anytime from the files and folders settings page. + + + Administrer merkelapper + + + Available + + + Total + + + Always switch focus to newly created tab + + + Toggle shelf pane + + + Toggle the visibility of the shelf pane + + + Show shelf pane toggle in address bar + + + Shelf + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + Clear items + + + Remove from shelf + + + Add to shelf + Tooltip that displays when dragging items to the Shelf Pane + + + Enter a hash to compare + Placeholder that appears in the compare hash text box + + + Matches {0} + Appears when two compared hashes match, e.g. "Matches SHA256" + + + No matches found + Appears when two compared hashes don't match + + + Sti eller alias + + + Invalid path + + + Test integration + + + {0} ble ikke funnet. Sjekk innstillingene og prøv igjen. + + + Den konfigurerte IDE-en ble ikke funnet + + + Open settings + + + Visual Studio Code + + + Skriv inn en sti eller startalias + + + Angi et navn for IDE-en + + + Clone repo + Clone repo dialog title + + + Clone + Primary action button in the clone repo dialog + + + Repository URL + URL textbox header in the clone repo dialog + + + Cannot clone repo + Cannot clone repo dialog title + + + Compare a file + Button that appears in file hash properties that allows the user to compare two files + + + Size format + + + Binary + + + Decimal + + + You can add sections to the sidebar by right-clicking and selecting the sections you want to add + + + Drag files or folders here to interact with them across different tabs + + + Angi en sti å navigere til... + + + Finn funksjoner og kommandoer... + + + Søk etter filer og mapper... + + + During file operations + + + Show status center button + + + Status center progress ring + Screen reader name for the status center progress ring + + + Icon files + This is the friendly name for a variety of different icon files. + + + Show collapsed path breadcrumbs + + + Show folders in {0} + + + Show folders in Home + + + There are no commands containing {0} + + + Se mer + + + Filtering for + + + Filename + + + Signatures + + + Signature list + + + Issued by: + + + Issued to: + + + Valid from: + + + Valid to: + + + No signature was found. + + + Show option to open folders in Windows Terminal + + + Open log file + + + Open log file in your default editor + + + Open log file location in your default file manager + + + Unable to open the log file + + + Show status bar + + + Only in Columns View + + + New {0} + + + Enable smooth scrolling + + + Scrolling + + + Show option to Pin to Sidebar + + + Show option to Pin to the Start Menu + \ No newline at end of file diff --git a/src/Files.App/Strings/nl-NL/Resources.resw b/src/Files.App/Strings/nl-NL/Resources.resw index 5299ed526344..ed70e6ea49ea 100644 --- a/src/Files.App/Strings/nl-NL/Resources.resw +++ b/src/Files.App/Strings/nl-NL/Resources.resw @@ -123,8 +123,14 @@ Pad kopiëren + + Als pad kopiëren + - Pad kopiëren tussen aanhalingstekens + Pad kopiëren met aanhalingstekens + + + Geselecteerde item pad kopiëren inclusief aanhalingstekens Bladeren @@ -184,7 +190,7 @@ Zoeken - Bestanden die u eerder heeft geopend zullen hier worden weergegeven + Bestanden die je eerder hebt geopend zullen hier worden weergegeven Dit item verwijderen @@ -205,25 +211,25 @@ Thema - Extensies tonen voor bekende bestandstypes + Extensies weergeven voor populaire bestandstypes Verborgen bestanden en mappen weergeven - Toon punt bestanden + Dot files weergeven Vormgeving - Achtergrond kleur + Achtergrondkleur Geavanceerd - Verdergaan waar u was gebleven + Verdergaan waar je was gebleven Nieuw tabblad openen @@ -249,6 +255,9 @@ Plakken + + Snelkoppeling plakken + Nieuw @@ -280,10 +289,10 @@ Welkom bij Files! - Verleen machtiging + Toestemming geven - Om aan de slag te gaan, moet u ons machtigen geven om uw bestanden weer te geven. Hiermee wordt er een pagina in Instellingen geopend waar deze machtiging kunt geven. U moet de app hierna opnieuw starten. + Om aan de slag te gaan, moet je ons toestemming geven om je bestanden weer te geven. Hiermee wordt er een pagina in Instellingen geopend waar je deze machtiging kunt geven. Je moet de app hierna opnieuw starten. Weergave @@ -291,8 +300,8 @@ Voer een itemnaam in - - Set naam + + Nieuwe {0} aanmaken Licht @@ -300,6 +309,9 @@ Donker + + Gebruik systeeminstelling + Toepassing @@ -352,28 +364,28 @@ Schijf ontkoppeld - Het bestand dat u probeert te openen, is mogelijk verplaatst of verwijderd. + Het bestand dat je probeert te openen, is mogelijk verplaatst of verwijderd. Kan eigenschappen van dit bestand niet openen - Het bestand dat u probeert te openen, is mogelijk verplaatst of verwijderd. + Het bestand dat je probeert te openen, is mogelijk verplaatst of verwijderd. Bestand is niet gevonden - De map die u probeert te openen, is mogelijk verplaatst of verwijderd. + De map die je probeert te openen, is mogelijk verplaatst of verwijderd. Is deze map verwijderd? - Het bestand wat je probeert te openen, wordt op dit moment gebruikt door {0} + Het bestand dat je probeert te openen, wordt op dit moment gebruikt door {0} - Het bestand wat je probeert te openen, wordt gebruikt door een andere applicatie + Het bestand dat je probeert te openen, wordt gebruikt door een andere applicatie Bestand is in gebruik @@ -450,7 +462,7 @@ Ja - Weet u zeker dat u al deze items permanent verwijderen? + Weet je zeker dat je al deze items permanent verwijderen? Prullenbak leegmaken @@ -515,9 +527,6 @@ Status - - Windows-standaard - Geen resultaten @@ -530,6 +539,9 @@ Kopiëren naar {0} + + Klonen naar {0} + Snelkoppeling maken @@ -540,7 +552,7 @@ Argumenten: - Bestemming: + Doel: Snelkoppelingstype: @@ -594,7 +606,7 @@ {0} {1} delen - Het item dat u probeert te hernoemen, bestaat niet meer. Controleer de locatie van dit item. + Het item dat je probeert te hernoemen, bestaat niet meer. Controleer de locatie van dit item. Item bestaat niet meer @@ -612,10 +624,10 @@ Itemnaam te lang - Meer opties tonen + Meer opties weergeven - De toepassing moet worden herstart om deze instellingen toe te passen. Wilt u dit nu doen? + De toepassing moet worden herstart om deze instellingen toe te passen. Wil je dit nu doen? Sponsor ons op GitHub @@ -660,7 +672,7 @@ Verplaats shell-extensies naar een submenu - Toon bevestigingsvenster bij het verwijderen van items + Bevestigingsvenster weergeven bij het verwijderen van items Er is een probleem opgetreden bij het opslaan van enkele eigenschappen. @@ -998,6 +1010,9 @@ Zoekresultaten in + + Zoekresultaten voor `{0}` + Details @@ -1029,16 +1044,16 @@ Tabbladen in dubbele paneelmodus openen - Toon optie om mappen in een nieuw paneel te openen + Optie weergeven om mappen in een nieuw paneel te openen - Toon optie om pad te kopiëren + Optie weergeven om pad te kopiëren - Toon optie om map aan te maken met selectie + Optie weergeven om map aan te maken met selectie - Toon optie om een snelkoppeling te maken + Optie weergeven om snelkoppeling te maken Nieuw paneel @@ -1052,9 +1067,6 @@ Details (Ctrl+Shift+1) - - Tegels (Ctrl+Shift+2) - Datum verwijderd @@ -1074,16 +1086,31 @@ Detailpaneel in-/uitschakelen - Detailpaneel in- of uitschakelen om standaard bestandseigenschappen te zien + Detailpaneel in-/uitschakelen om standaard bestandseigenschappen weer te geven Infopaneel in-/uitschakelen - Infopaneel in- of uitschakelen om het detail-/voorbeeldvenster te kunnen zien + Detail‑/voorbeeldvensters tonen/verbergen - Toggle the Toolbar + Werkbalk in-/uitschakelen + + + Toggle visibility of the toolbar + + + Toggle filter header + + + Toggle visibility of the filter header + + + Toggle dual pane + + + Toggle dual pane mode Geen voorbeeld beschikbaar @@ -1134,7 +1161,7 @@ Onbekend - Als u een bestandsnaamextensie wijzigt, wordt het bestand mogelijk onbruikbaar. Weet u zeker dat u de extensie wilt wijzigen? + Als je een bestandsextensie wijzigt, wordt het bestand mogelijk onbruikbaar. Weet je zeker dat je de extensie wilt wijzigen? Cd-rom-station @@ -1238,6 +1265,12 @@ Opslaginzicht openen + + Open de Storage Sense pagina in Windows instellingen + + + Opruimen + Kopiëren @@ -1290,7 +1323,7 @@ Maak map met selectie - Soort: + Type: Datum gewijzigd: @@ -1380,7 +1413,7 @@ {0} items - Gesloten tabblad opnieuw openen + Gesloten tab heropenen Hernoemen @@ -1443,7 +1476,7 @@ Onbekend account - U heeft geen machtiging om de beveiligingseigenschappen van dit item te bekijken. Klik op "Geavanceerde machtigingen" om door te gaan. + Je hebt geen toestemming om de beveiligingseigenschappen van dit item te bekijken. Klik op "Geavanceerde machtigingen" om door te gaan. Eigenaar: @@ -1455,7 +1488,7 @@ Archief uitpakken - Open doelmap na voltooiing + Open doelmap na voltooien Uitpakken naar {0}\ @@ -1467,7 +1500,7 @@ Herstel standaard bibliotheken - Weet u zeker dat u de standaard bibliotheken wilt herstellen? Al uw bestanden blijven op uw opslag staan. + Weet je zeker dat je de standaard bibliotheken wilt herstellen? Al je bestanden blijven op je opslag staan. Herstel bibliotheken @@ -1503,7 +1536,7 @@ Machtigingen - U heeft geen machtigingen om de beveiligingseigenschappen van dit item te bekijken. U kunt proberen eigenaar van dit object te worden. + Je hebt geen machtigingen om de beveiligingseigenschappen van dit item te bekijken. Je kan proberen eigenaar van dit object te worden. Deze map en bestanden @@ -1587,7 +1620,7 @@ Vervang alle onderliggende object machtigingen met erfbare machtigingsgegevens van dit object - Close active pane + Paneel sluiten Open compacte overlay @@ -1646,15 +1679,6 @@ Start - - Ctrl+Shift+1 - - - Ctrl+Shift+2 - - - Ctrl+Shift+6 - Het uitpakken van het archief is voltooid. @@ -1712,8 +1736,8 @@ Kolommen - - Tegels + + Kaarten Open mappen in nieuw tabblad @@ -1749,10 +1773,10 @@ Label - Open items met één klik + Open bestanden met één klik - Mappad + Map pad Exporteer instellingen @@ -1826,6 +1850,18 @@ Registreer dit programma voor herstart + + Start venster + + + Normaal venster + + + Geminimaliseerd + + + Gemaximaliseerd + Als administrator uitvoeren @@ -1848,16 +1884,19 @@ Geen gereduceerde kleur - Third party libraries + Bibliotheken van derden - Deze instelling wijzigt het register en kan onverwachte bijwerkingen op uw apparaat hebben. De ontwikkelaars nemen geen verantwoordelijkheid in het geval er zich een probleem voordoet. Doorgaan met deze optie is een erkenning van de risico's die aan deze actie zijn verbonden. + Deze instelling wijzigt het register en kan onverwachte bijwerkingen op je apparaat hebben. De ontwikkelaars nemen geen verantwoordelijkheid in het geval er zich een probleem voordoet. Doorgaan met deze optie is een erkenning van de risico's die aan deze actie zijn verbonden. + + + De afvlak-bewerkingen zijn permanent en niet aanbevolen. Ga verder op eigen risico. Maak Bibliotheek - Vul de Bibliotheek naam in + Vul de Bibliotheeknaam in Bereken bestandsgrootte @@ -1884,7 +1923,7 @@ Schoon de inhoud van je schijf op - Soort + Type Bytes @@ -1911,7 +1950,7 @@ Uitvoeren met PowerShell - Het berekenen van mapgrootte is een bron die intensief is en kan uw CPU-gebruik doen toenemen. + Het berekenen van mapgrootte is een bron die intensief is en kan je CPU-gebruik doen toenemen. Linksom draaien @@ -1923,14 +1962,17 @@ Installeren - Tabbladen aan de linkerkant sluiten + Tabbladen aan linkerkant sluiten - Tabbladen rechts sluiten + Tabbladen aan rechterkant sluiten Andere tabbladen sluiten + + Alle tabbladen sluiten + Muziek @@ -1980,16 +2022,28 @@ Ctrl+Shift+7 - Toon alternatieve data streams + Alternatieve data streams weergeven Gedragingen - - Files beoordelen + + Hallo! - - Wilt u Files beoordelen? + + Enjoying Files? Please consider reviewing in the Microsoft Store. + + + Enjoying Files? Please consider supporting the project on GitHub. + + + Sponsor + + + Rate us + + + Dismiss Als achtergrond instellen @@ -2018,8 +2072,8 @@ Vergrendelingsscherm - - Open mappen met één klik in de kolommen lay-out + + Open mappen met één klik Items openen @@ -2027,6 +2081,21 @@ Comprimeren + + Verplaats alle inhoud van submappen naar de geselecteerde locatie + + + Map afvlakken + + + Afvlakken + + + Een map afvlakken verplaatst alle inhoud van de submappen naar de geselecteerde locatie. Deze bewerking is permanent en kan niet ongedaan worden gemaakt. Door deze experimentele functie te gebruiken, erken je het risico en ga je ermee akkoord om het Files team niet verantwoordelijk te stellen voor enig dataverlies. + + + Afvlak-opties weergeven + Selecteer bestanden en mappen wanneer je over hen beweegt @@ -2037,7 +2106,7 @@ Alle items herstellen - Wilt u de {0} geselecteerde item(s) herstellen? + Do you want to restore the {0, plural, one {selected item} other {{0} selected items}}? Herstel selectie @@ -2052,14 +2121,20 @@ Snelkoppeling kan niet worden geopend - De bestemming kan niet worden gevonden {0}. Wilt u deze snelkoppeling verwijderen? + De doelbestemming kan niet worden gevonden {0}. Wilt u deze snelkoppeling verwijderen? - U heeft geen machtiging voor toegang tot deze map. + Je hebt geen machtiging voor toegang tot deze map. Archief wachtwoord + + Codering + + + {0} (gedetecteerd) + Pad @@ -2102,9 +2177,24 @@ Splits grootte - + Niet splitsen + + Auto + + + Dictionary size + + + Word size + + + Estimated memory usage: {0} + + + Available memory: {0} + CD @@ -2130,7 +2220,7 @@ Synchronisatiestatus kolom - Groepeer + Groeperen op Sorteren op @@ -2159,8 +2249,14 @@ Bewerk instellingenbestand - - Wat is er nieuw + + Instellingenbestand openen in je standaard editor + + + Uitgave opmerkingen + + + Uitgave opmerkingen openen Een snelkoppeling maken op deze locatie vereist beheerdersrechten @@ -2181,25 +2277,25 @@ Meer weergeven - Toon Bewerk Labels menu + Bewerk labels menu weergeven - Toon compressie opties + Compressie opties weergeven - Toon versturen naar menu + Versturen naar menu weergeven - Toon optie om mappen in een nieuw tabblad te openen + Optie weergeven om mappen in nieuw tabblad te openen - Toon optie om mappen in een nieuw venster te openen + Optie weergeven om mappen in nieuw venster te openen Snelle toegang - Voer uw inloggegevens in om verbinding te maken met: {0} + Voer je inloggegevens in om verbinding te maken met: {0} Voer netwerk inloggegevens in @@ -2241,25 +2337,22 @@ Opties contextmenu - Toon bestandsnaamextensies - - - Toon verborgen items + Bestandsextensies weergeven - Formatteren... + Formaat Help - Volledig scherm in-/uitschakelen + Volledig scherm - Weet u zeker dat u dit label wilt verwijderen? + Weet je zeker dat je dit label wilt verwijderen? - Alles afspelen + Afspelen Hoogte @@ -2304,7 +2397,7 @@ Hash waarde - Selecteer hashes om te laten zien + Selecteer hashes om weer te geven Berekenen... @@ -2319,7 +2412,7 @@ Vergroten - Wissel sorteerrichting + Sort direction Ongeldige naam @@ -2331,67 +2424,82 @@ Video's - Open voorbeeld popup + Preview popup Compacte overlay in-/uitschakelen - Open de online help pagina in de browser + Open the online help page in your browser Volledig scherm in-/uitschakelen - Open compacte overlay + Ga naar compacte overlay - Sluit compacte overlay + Exit compact overlay mode - Schakel compacte overlay + Toggle compact overlay mode - Ga naar zoekveld + Start search in the OmniBar - In- of uitschakelen om verborgen items te tonen + Verborgen items tonen/verbergen + + + Dot files tonen/verbergen - In- of uitschakelen om bestandsextensies te tonen + Bestandsextensies tonen/verbergen - Voorbeeldpaneel in- of uitschakelen om bestandsvoorbeelden te zien + Voorbeeldpaneel in-/uitschakelen om bestandsvoorbeelden weer te geven - In- of uitschakelen om de zijbalk te tonen + In-/uitschakelen om zijbalk weer te geven - Kopieer item(s) naar klembord + Geselecteerde {0, plural, one {item} other {items}} kopiëren - Het pad van de geselecteerde items naar het klembord kopiëren + Copy path of the current directory + + + Copy path of selected items + + + Copy path of selected items with quotes - Het pad van de geselecteerde items inclusief aanhalingstekens naar het klembord kopiëren + Copy path of the current directory with quotes - Knip item(s) naar klembord + Geselecteerde {0, plural, one {item} other {items}} knippen - Plak item(s) van klembord naar huidige map + Paste items to the current folder + + + Paste items to the current folder as shortcuts - Plak item(s) van klembord naar geselecteerde map + Paste items to the selected folder + + + Paste to selected folder - Item(s) verwijderen + Geselecteerde {0, plural, one {item} other {items}} verwijderen Nieuwe map maken - Maak nieuwe snelkoppeling(en) aan voor geselecteerde item(s) + Create new {0, plural, one {shortcut} other {shortcuts}} to selected {0, plural, one {item} other {items}} Maak een nieuwe snelkoppeling naar een item @@ -2400,19 +2508,19 @@ Prullenbak leegmaken - Open het "Formatteer Schijf" menu voor geselecteerde item + Open het "Schijf formatteren" menu voor geselecteerde item - Herstel geselecteerde item(s) uit prullenbak + Restore selected {0, plural, one {item} other {items}} from recycle bin Herstel alle items vanuit de prullenbak - Open item(s) + Open {0, plural, one {item} other {items}} - Open item(s) met geselecteerde toepassing + Open {0, plural, one {item} other {items}} with selected application Open de bovenliggende map van gezochte item @@ -2427,28 +2535,28 @@ Selecteer alle items - Selectie item omkeren + Invert selected items - Selectie item leegmaken + Clear selected items - Items selecteren in-/uitschakelen + Items selectie in-/uitschakelen - Deel geselecteerde bestand(en) met anderen + Share selected {0, plural, one {file} other {files}} with others - Aan Start vastmaken + Pin {0, plural, one {item} other {items}} to the Start Menu - Van Start losmaken + Unpin {0, plural, one {item} other {items}} from the Start Menu - Map(pen) vastmaken in zijbalk + Pin {0, plural, one {folder} other {folders}} to Sidebar - Map(pen) losmaken van zijbalk + Unpin {0, plural, one {folder} other {folders}} from Sidebar Stel geselecteerde afbeelding in als bureaubladachtergrond @@ -2462,14 +2570,23 @@ Stel geselecteerde afbeelding in als app-achtergrond + + Install font + + + Install driver + + + Install certificate + - Geselecteerde lettertype(s) installeren + Install selected {0, plural, one {font} other {fonts}} - Installeer de driver(s) met behulp van geselecteerde inf bestand(en) + Install {0, plural, one {driver} other {drivers}} using selected inf {0, plural, one {file} other {files}} - Geselecteerde certificaat(en) installeren + Install selected {0, plural, one {certificate} other {certificates}} Geselecteerde toepassing uitvoeren als beheerder @@ -2484,28 +2601,28 @@ Voorbeeld openen in pop-upvenster - Archief maken met geselecteerde item(s) + Create archive with selected {0, plural, one {item} other {items}} - Maak 7z-archief direct met geselecteerde item(s) + Create 7z archive with selected {0, plural, one {item} other {items}} - Maak zip-archief direct met geselecteerde item(s) + Create zip archive with selected {0, plural, one {item} other {items}} - Items uit geselecteerde archieven naar een willekeurige map uitpakken + Extract selected {0, plural, one {archive} other {archives}} to any folder - Items uit geselecteerde archieven naar de huidige map uitpakken + Extract selected {0, plural, one {archive} other {archives}} to the current folder - Items uit geselecteerde archieven naar nieuwe map uitpakken + Extract selected {0, plural, one {archive} other {archives}} to new folder - Draai geselecteerde afbeelding(en) linksom + Rotate selected {0, plural, one {image} other {images}} to the left - Draai geselecteerde afbeelding(en) rechtsom + Rotate selected {0, plural, one {image} other {images}} to the right Open instellingen pagina @@ -2525,8 +2642,8 @@ Overschakelen naar detailweergave - - Overschakelen naar tegelweergave + + Overschakelen naar kaartenweergave Overschakelen naar lijstweergave @@ -2580,10 +2697,10 @@ Sorteer items in aflopende volgorde - Wissel sorteerrichting + Sorteerrichting omkeren - Items tonen zonder groepering + Items weergeven zonder groepering Items groeperen op naam @@ -2622,16 +2739,16 @@ Sorteer groepen in aflopende volgorde - Wissel groep sorteerrichting + Groep sorteerrichting omkeren - Open nieuw tabblad + Nieuw tabblad openen - Navigeer achteruit in de navigatiegeschiedenis + Navigate backward - Navigeer vooruit in de navigatiegeschiedenis + Navigate forward Navigeer één map naar boven @@ -2655,13 +2772,16 @@ Tabbladen aan de rechterkant van geselecteerde tabblad sluiten - Sluit andere tabbladen dan huidige tabblad + Tabbladen sluiten anders dan huidige tabblad - Sluit tabbladen anders dan geselecteerd tabblad + Tabbladen sluiten anders dan geselecteerd tabblad + + + Alle tabbladen sluiten inclusief het huidige tabblad - Laatst gesloten tabblad heropenen + Reopen recently closed tab Verplaats naar het vorige tabblad @@ -2673,13 +2793,13 @@ Huidig tabblad sluiten - Close active pane + Close the active pane - Focus other pane + Focus op ander paneel - Switch focus to the non active pane + Switch focus to the other pane Zijbalk in-/uitschakelen @@ -2887,13 +3007,13 @@ Zonder label - Verplaatst naar vorige tabblad + Previous tab - Verplaatst naar volgend tabblad + Next tab - Sluit huidig tabblad + Close tab Pad bewerken @@ -2923,7 +3043,7 @@ Selectievakjes weergeven bij het selecteren van items - Focus padenbalk + Edit path in the OmniBar Maak nieuw item @@ -2938,7 +3058,7 @@ Permanent verwijderen - Item(s) permanent verwijderen + Delete selected {0, plural, one {item} other {items}} permanently De geselecteerde mediabestanden afspelen @@ -2965,10 +3085,10 @@ Dag - Schakel eenheid voor groeperen op datum + Eenheid in-/uitschakelen voor groeperen op datum - Wissel groeperingseenheid + Groeperingseenheid in-/uitschakelen Jaar @@ -3007,10 +3127,10 @@ Klik op 'Geavanceerde machtigingen' om door te gaan. - U moet leesmachtigingen hebben om de eigenschappen van dit item te bekijken. + Je moet leesmachtigingen hebben om de eigenschappen van dit item te bekijken. - Om te proberen eigenaar te worden van het item, dat machtigingen bevat om de eigenschappen ervan te bekijken, klikt u hierboven op Wijzigen. + Om te proberen eigenaar te worden van het item, dat machtigingen bevat om de eigenschappen ervan te bekijken, klik je hierboven op Wijzigen. Kan machtigingen niet weergeven. @@ -3027,6 +3147,15 @@ Je hebt niet-gecommitte wijzigingen in deze branche. Wat wil je ermee doen? + + You have an ongoing merge with unresolved conflicts. Please resolve the conflicts or abort the merge to switch branches. + + + Abort merge and switch to '{0}' + + + Stay on '{0}' and resolve conflicts + Wissel van branch @@ -3055,14 +3184,20 @@ Overschakelen naar nieuwe branch - Maak een map met de momenteel geselecteerde item(s) + Create a folder with the currently selected {0, plural, one {item} other {items}} - Open eigenschappen + Eigenschappen + + + Open Verkenner eigenschappen Open eigenschappen venster + + Open Verkenner eigenschappen venster + Locals @@ -3081,6 +3216,9 @@ Start git fetch + + Git repo klonen + Start git pull @@ -3125,7 +3263,7 @@ Mica Alt - Achtergrond materiaal + Achtergrond Effen @@ -3157,17 +3295,17 @@ Files kan nu geen toegang krijgen tot GitHub. - - Open map in VS code + + Map openen in {0} - - Open de huidige map in Visual Studio Code + + Open de huidige map in {0} - - Open repo in VS Code + + Repo openen in {0} - - Open de hoofdmap van de Git repo in Visual Studio Code + + Open de root van de Git repo in {0} Kopieer code @@ -3194,7 +3332,7 @@ Repo initialiseren - Initialiseer een Git repository + Initialize current folder as a git repository Naam externe repository @@ -3209,13 +3347,13 @@ Sorteer items op pad - Map in nieuw paneel openen + Open selected directory in a new pane - Open map in nieuw tabblad + Open selected directory in a new tab - Open map in nieuw venster + Open selected directory in a new window Open alles @@ -3240,10 +3378,10 @@ Het '{0}' commando is niet klaar om uitgevoerd te worden. - Commandopaneel + Command Palette - Commandopaneel openen + Open Command Palette in the OmniBar Start in: @@ -3373,6 +3511,9 @@ Items verwerken... + + Discovering items... + Snelheid: @@ -3385,61 +3526,129 @@ Shown in a StatusCenter card. - {0} item(s) naar "{1}" gecomprimeerd + Compressed {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - {0} item(s) van "{1}" naar "{2}" gecomprimeerd + Compressed {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Fout bij het comprimeren van {0} item(s) naar "{1}" + Error compressing {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Comprimeren van {0} item(s) van "{1}" naar "{2}" mislukt + Failed to compress {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - {0} item(s) naar "{1}" comprimeren + Compressing {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - {0} item(s) van "{1}" naar "{2}" comprimeren + Compressing {0, plural, one {# item} other {# items}} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + {0} klonen naar "{1}" geannuleerd + Shown in a StatusCenter card. + + + {0} klonen van "{1}" naar "{2}" geannuleerd + Shown in a StatusCenter card. + + + "{0}" gekloond naar "{1}" + Shown in a StatusCenter card. + + + "{0}" gekloond van "{1}" naar "{2}" + Shown in a StatusCenter card. + + + "{0}" klonen naar "{1}" mislukt + Shown in a StatusCenter card. + + + "{0}" klonen van "{1}" naar "{2}" mislukt + Shown in a StatusCenter card. + + + "{0}" klonen naar "{1}" + Shown in a StatusCenter card. + + + "{0}" klonen van "{1}" naar "{2}" + Shown in a StatusCenter card. + + + {0} lettertypes installeren geannuleerd + Shown in a StatusCenter card. + + + Canceled installing {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Error installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Failed to install {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} from "{1}" Shown in a StatusCenter card. - Kopiëren {0} item(s) naar "{1}" geannuleerd + Canceled copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Kopiëren {0} item(s) van "{1}" naar "{2}" geannuleerd + Canceled copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - {0} item(s) naar "{1}" gekopieerd + Copied {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - {0} item(s) van "{1}" naar "{2}" gekopieerd + Copied {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Fout bij het kopiëren van {0} item(s) naar "{1}" + Error copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Kopiëren van {0} item(s) van "{1}" naar "{2}" mislukt + Failed to copy {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - {0} item(s) naar "{1}" aan het kopiëren + Copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - {0} item(s) van "{1}" naar "{2}" aan het kopiëren + Copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. + + Discovered {0, plural, one {# item} other {# items}} + Shown in a StatusCenter card during file discovery phase. + Uitpakken "{0}" naar "{1}" geannuleerd Shown in a StatusCenter card. @@ -3473,55 +3682,55 @@ Shown in a StatusCenter card. - Verwijderen van {0} item(s) van "{1}" geannuleerd + Canceled deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - {0} Item(s) verwijderd van "{1}" + Deleted {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Fout bij verwijderen van {0} item(s) van "{1}" + Error deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Kan {0} item(s) niet verwijderen van "{1}" + Failed to delete {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - {0} item(s) verwijderen van "{1}" + Deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Verplaatsen {0} items(s) uit "{1}" geannuleerd + Canceled moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Verplaatsen {0} items(s) van "{1}" naar "{2}" geannuleerd + Canceled moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - {0} item(s) verplaatst naar "{1}" + Moved {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - {0} item(s) van "{1}" naar "{2}" verplaatst + Moved {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - {0} item(s) verplaatsen naar "{1}" + Moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - "{0}" item(s) van "{1}" naar "{2}" verplaatsen + Moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Fout tijdens verplaatsen van "{0}" item(s) naar "{1}" + Error moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Verplaatsen van {0} item(s) van "{1}" naar "{2}" mislukt + Failed to move {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. @@ -3549,7 +3758,7 @@ Shown in a StatusCenter card. - {0}/{1} item(s) verwerkt + {0}/{1, plural, one {# item} other {# items}} processed Shown in a StatusCenter card. Used as "8/20 items processed" @@ -3567,6 +3776,9 @@ Instellen van achtergrondafbeelding mislukt + + Instellingenbestand openen mislukt + Git branch verwijderen @@ -3592,7 +3804,7 @@ Er is een fout opgetreden tijdens het toewijzen van dit label - Bestanden uitpakken voor geselecteerde archieven naar de huidige map, voor een archief met één item, of naar een nieuwe map voor een archief met meerdere items + Extract selected {0, plural, one {archive} other {archives}} here for single-item or to new folder for multi-item Hier uitpakken (slim) @@ -3601,7 +3813,7 @@ Bestanden en mappen samen sorteren - Bestanden en mappen samen sorteren + Sort files and folders in the same list Bestanden eerst sorteren @@ -3657,9 +3869,6 @@ Vragen & discussies - - Extra formaten zijn nog niet beschikbaar voor de tegelweergave. - Compact Used to describe layout sizes @@ -3719,7 +3928,7 @@ Type lay-out - acties + Acties Opdrachten @@ -3734,13 +3943,13 @@ Kies een actie - Weet u zeker dat u de standaard sneltoetsen wilt herstellen? Deze actie kan niet ongedaan gemaakt worden. + Weet je zeker dat je de standaard sneltoetsen wilt herstellen? Deze actie kan niet ongedaan gemaakt worden. Deze sneltoets is al in gebruik, kies een andere koppeling om door te gaan. - De sneltoets die u koos kan niet worden gebruikt, gelieve opnieuw te proberen met een andere sneltoets. + De sneltoets die je koos kan niet worden gebruikt, gelieve opnieuw te proberen met een andere sneltoets. Aangepast @@ -3749,7 +3958,7 @@ Adaptieve lay-out is niet beschikbaar wanneer voorkeuren zijn gesynchroniseerd, de standaard lay-out is gewijzigd in Details. - Achtergrond afbeelding + Achtergrondafbeelding Onderaan @@ -3800,7 +4009,7 @@ This is a type of backdrop for the application background - Toon voor alle locaties + Weergeven voor alle locaties Setting where users can choose to display "Open IDE" button for Git Repos @@ -3810,7 +4019,7 @@ Configureer de "Open IDE" knop op de statusbalk - Toon voor Git repos + Weergeven voor Git repos Setting where users can choose to display "Open IDE" button for all locations. @@ -3821,6 +4030,10 @@ ICO bestand This is the friendly name for ICO files. + + ICL bestand + This is the friendly name for ICL files. + Zip bestand This is the friendly name for ZIP files. @@ -3829,11 +4042,15 @@ Bitmap bestanden This is the friendly name for bitmap files. + + Image Files + This is the friendly name for image files. + Numeriek - Netwerk locaties + Netwerklocaties Er zijn geen netwerklocaties. Als u geen netwerklocaties gebruikt, kunt u de widget uitschakelen. @@ -3848,58 +4065,316 @@ Bewerk het geselecteerde bestand in Kladblok - Dimensions: + Afmetingen: Status: - Show Toolbar - Setting that controls if the Toolbar is shown in the main view + Werkbalk weergeven + Setting that controls if the toolbar is shown in the main view - Tab actions menu + Tabblad acties-menu - - Add vertical pane + + Split vertically - Add a vertical pane + Split pane vertically - - Add horizontal pane + + Split horizontally - - Add a horizontal pane + + Split pane horizontally - Arrange vertically + Verticaal ordenen - Arrange panes vertically + Panelen verticaal ordenen - Arrange horizontally + Horizontaal ordenen - Arrange panes horizontally + Panelen horizontaal ordenen - - Add pane + + Split pane - Show tab actions button in the title bar + Tabblad acties knop weergeven in titelbalk - Arrange panes + Panelen ordenen + + + Default dual pane split direction - - Default pane arrangement + + Dual pane mode - Horizontal + Horizontaal - Vertical + Verticaal + + + Files pictogram weergeven in systeemvak + + + CPU-threads + + + Navigeer naar de homepage + + + Werkbalken + + + Gebruiker-ID + + + Bulk hernoemen + + + Inhoud comprimeren + + + Optie weergeven om alternatieve gegevensstream aan te maken + + + Alternatieve gegevensstream aanmaken + + + Create alternate data stream for the selected {0, plural, one {item} other {items}} + + + Voer gegevensstream naam in + + + Er is een fout opgetreden bij het maken van de alternatieve gegevensstream + + + Houd er rekening mee dat alternatieve gegevensstreams alleen werken op schijven geformatteerd als NTFS. + + + Alternatieve gegevensstreams zijn momenteel verborgen + + + Wil je alternatieve gegevensstreams weergeven? Je kan deze instelling op elk gewenst moment wijzigen vanaf de bestanden en mappen instellingenpagina. + + + Tags beheren + + + Beschikbaar + + + Totaal + + + Focus altijd schakelen naar nieuw gemaakte tabblad + + + Toggle shelf pane + + + Toggle the visibility of the shelf pane + + + Plankpaneel toggle weergeven in adresbalk + + + Plank + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + Items wissen + + + Verwijderen van plank + + + Toevoegen aan plank + Tooltip that displays when dragging items to the Shelf Pane + + + Voer een hash in om te vergelijken + Placeholder that appears in the compare hash text box + + + {0} overeenkomsten + Appears when two compared hashes match, e.g. "Matches SHA256" + + + Geen overeenkomsten gevonden + Appears when two compared hashes don't match + + + Pad of alias + + + Ongeldig pad + + + Integratie testen + + + {0} kon niet gevonden worden. Controleer je instellingen en probeer opnieuw. + + + De geconfigureerde IDE kon niet worden gevonden + + + Instellingen openen + + + Visual Studio Code + + + Voer een pad in of begin alias + + + Voer een naam in voor de IDE + + + Repo klonen + Clone repo dialog title + + + Klonen + Primary action button in the clone repo dialog + + + Repository-URL + URL textbox header in the clone repo dialog + + + Kan repo niet klonen + Cannot clone repo dialog title + + + Bestand vergelijken + Button that appears in file hash properties that allows the user to compare two files + + + Grootte formaat + + + Binair + + + Decimaal + + + You can add sections to the sidebar by right-clicking and selecting the sections you want to add + + + Drag files or folders here to interact with them across different tabs + + + Enter a path to navigate to... + + + Find features and commands... + + + Search for files and folders... + + + During file operations + + + Show status center button + + + Status center progress ring + Screen reader name for the status center progress ring + + + Icon files + This is the friendly name for a variety of different icon files. + + + Show collapsed path breadcrumbs + + + Show folders in {0} + + + Show folders in Home + + + There are no commands containing {0} + + + See more + + + Filteren voor + + + Bestandsnaam + + + Handtekeningen + + + Signature list + + + Verleend door: + + + Verleend aan: + + + Geldig van: + + + Geldig tot: + + + No signature was found. + + + Show option to open folders in Windows Terminal + + + Open log file + + + Open log file in your default editor + + + Open log file location in your default file manager + + + Unable to open the log file + + + Show status bar + + + Only in Columns View + + + New {0} + + + Enable smooth scrolling + + + Scrolling + + + Show option to Pin to Sidebar + + + Show option to Pin to the Start Menu \ No newline at end of file diff --git a/src/Files.App/Strings/or-IN/Resources.resw b/src/Files.App/Strings/or-IN/Resources.resw deleted file mode 100644 index b981a627e1eb..000000000000 --- a/src/Files.App/Strings/or-IN/Resources.resw +++ /dev/null @@ -1,3905 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - New window - - - Copy path - - - Copy path with quotes - - - Browse - - - Size - - - Created: - - - Path - - - Size: - - - Size on disk: - - - Uncompressed size: - - - This action cannot be done - - - The destination folder - - - is a subfolder of the source folder - - - Skip - - - Select All - - - Invert selection - - - Clear selection - - - Modified: - - - Accessed: - - - Clear all items - - - Enter a path to navigate to or type ">" to open the command palette - - - Search - - - Files you've previously accessed will show up here - - - Remove this item - - - GitHub repository - - - About - - - Open source - - - Date format - - - Theme - - - Show extensions for known file types - - - Show hidden files and folders - - - Show dot files - - - Appearance - - - Background color - - - Advanced - - - Continue where you left off - - - Open a new tab - - - Open a specific page or pages - - - Desktop - - - Documents - - - Downloads - - - Name - - - Ascending - - - Paste - - - New - - - Properties - - - Open - - - Open in new tab - - - Open in new window - - - Share - - - Cut - - - Delete - - - Pin to Sidebar - - - Welcome to Files! - - - Grant permission - - - To get started, you'll need to grant us permission to display your files. This will open a Settings page where you can grant us this permission. You'll need to reopen the app once you've completed this step. - - - Display - - - Enter an item name - - - Set name - - - Light - - - Dark - - - Application - - - System - - - New folder - - - New file - - - This folder is empty. - - - Back - - - Forward - - - Up - - - Refresh - - - An item with this name already exists in this directory. - - - Replace existing item - - - Item already exists - - - Set as lockscreen background - - - Set as app background - - - We weren't able to delete this item - - - Please insert the necessary drive to access this item. - - - Drive unplugged - - - The file you are attempting to access may have been moved or deleted. - - - Cannot open properties for this file - - - The file you are attempting to access may have been moved or deleted. - - - File Not Found - - - The folder you are attempting to access may have been moved or deleted. - - - Did you delete this folder? - - - The file you are attempting to access is currently being used by {0} - - - The file you are attempting to access is currently being used by another application - - - File is in use - - - Layout - - - Read-only - - - {0, plural, one {# day ago} other {# days ago}} - - - 1 day ago - - - {0} days ago - - - {0, plural, one {# hour ago} other {# hours ago}} - - - 1 hour ago - - - {0} hours ago - - - {0, plural, one {# minute ago} other {# minutes ago}} - - - 1 minute ago - - - {0} minutes ago - - - {0, plural, one {# second ago} other {# seconds ago}} - - - 1 second ago - - - {0} seconds ago - - - Now - - - The requested operation is not supported - - - {0} free of {1} - - - Send to - - - The item name must not contain the following characters: \ / : * ? " < > | - - - The item name specified is invalid - - - {0, plural, one {# item selected} other {# items selected}} - ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural - - - {0, plural, one {item} other {items}} - ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural - - - Yes - - - Are you sure you want to permanently delete all these items? - - - Empty recycle bin - - - bytes - - - KB - - - MB - - - GB - - - TB - - - PB - - - B - - - {0, number} {0, plural, one {file} other {files}}, {1, number} {1, plural, one {folder} other {folders}} - - - {0, number} {0, plural, one {file} other {files}}, {1, number} {1, plural, one {folder} other {folders}} from {2, number} {2, plural, one {location} other {locations}} - - - Run as another user - - - All type of {0} - - - Different types - - - All in {0} - - - Used space: - - - Free space: - - - Capacity: - - - File system: - - - Recent - - - Move tab here - - - Status - - - Windows default - - - No results - - - Can't access any items to display - - - Move to {0} - - - Copy to {0} - - - Create shortcut - - - Open file location - - - Arguments: - - - Destination: - - - Shortcut type: - - - Web link - - - General - - - Shortcut - - - Internet shortcut - - - {0} - shortcut - - - Files ran into a problem that the developers didn't prepare for yet. - - - Something went wrong! - - - Report this issue - - - The item referenced is either invalid or inaccessible.{0}Error message:{0}{1} - - - Invalid item - - - Version: - - - There's nothing to share right now... - - - The items you've selected will be shared - - - The selected item will be shared - - - Sharing {0} - - - Sharing {0} {1} - - - The item you're attempting to rename no longer exists. Please verify the correct location of the item you need to rename. - - - Item no longer exists - - - The name specified was invalid. Please check the desired item name and try again. - - - Invalid item name - - - The length of the item name specified exceeds the maximum limit. Please check the desired name and try again. - - - Item name was too long - - - Show more options - - - The application needs to be restarted in order to apply these settings, would you like to restart the app? - - - Sponsor us on GitHub - - - We weren't able to create this item - - - Access Denied - - - Set as - - - Cancel - - - Create a new item - - - Choose a type for this new item below - - - File - - - Creates an empty file - - - Folder - - - Creates an empty folder - - - Refresh the directory - - - Language - - - Move shell extensions into a sub menu - - - Show confirmation dialog when deleting items - - - There was an issue saving some properties. - - - Error - - - Close anyway - - - Rating - - - Item Path - - - Item Type - - - Title - - - Subject - - - Comment - - - Copyright - - - Date Modified - - - Bit Depth - - - Dimensions - - - Horizontal Size - - - Vertical Size - - - Horizontal Resolution - - - Vertical Resolution - - - Color Space - - - sRGB - - - Unspecified - - - Longitude Decimal - - - Latitude Decimal - - - Altitude - - - Date Taken - - - Camera Manufacturer - - - Camera Model - - - Exposure Time - - - Focal Length - - - Aperture - - - People Names - - - Channel Count - - - Format - - - Sample Rate - - - Album Artist - - - Album Title - - - Artist - - - Beats Per Minute - - - Composer - - - Conductor - - - Disc Number - - - Genre - - - Track Number - - - Duration - - - Frame Count - - - Protection Type - - - Author Url - - - Content Distributor - - - Date Released - - - Series Name - - - Season Number - - - Episode Number - - - Producer - - - Promotion Url - - - Provider Style - - - Publisher - - - Thumbnail Large Path - - - Thumbnail Large Uri - - - Thumbnail Small Path - - - Thumbnail Small Uri - - - User Web Url - - - Writer - - - Year - - - Contributor - - - Last Author - - - Revision Number - - - Version - - - Date Created - - - Total Editing Time - - - Template - - - Word Count - - - Character Count - - - Line Count - - - Paragraph Count - - - Page Count - - - Slide Count - - - Frame Rate - - - Encoding Bitrate - - - Audio Encoding Bitrate - - - Video Encoding Bitrate - - - Compression - - - Frame Width - - - Frame Height - - - Orientation - - - Core - - - Image - - - Photo - - - GPS - - - Media - - - Audio - - - Music - - - Video - - - Document - - - Address - - - Selection options - - - Select - - - Recycle bin item - - - Shortcut item - - - Drives - - - Move here - - - Safe to remove hardware - - - The device can now be safely removed from the computer. - - - Problem Ejecting Device - - - This device is currently in use. Close any programs, windows or tabs that might be using the device, and then try again. - - - Eject - - - Duplicate tab - - - Move tab to new window - - - New tab - - - Some properties may contain personal information. - - - Clear All Properties - - - Check the status of file operations here - - - Status center - - - Search results in {1} for {0} - - - Details - - - No - - - Show protected system files - - - Sync layout and sorting preferences across directories - - - Customize the right click context menu - - - Original path - - - Add - - - Edit - - - Remove - - - Open tabs in dual pane mode - - - Show option to open folders in a new pane - - - Show option to copy path - - - Show option to create folder with selection - - - Show option to create shortcut - - - New pane - - - Open in new pane - - - Files & folders - - - Details (Ctrl+Shift+1) - - - Tiles (Ctrl+Shift+2) - - - Date deleted - - - Cloud Drives - - - Confirm - - - Desired name - - - Toggle the preview pane - - - Toggle the details pane - - - Toggle the details pane to view basic file properties - - - Toggle the info pane - - - Toggle the info pane to view the detail/preview panes - - - Toggle the Toolbar - - - No preview available - - - Item Name - - - Unpin from Sidebar - - - Network - - - File details - - - File preview - - - Selected file preview pane - - - Feedback - - - Available when online - - - Documentation - - - Available offline - - - Partially available offline - - - Syncing - - - Excluded from sync - - - Not calculated - - - Unknown - - - If you change a file extension, the file might become unusable. Are you sure you want to change it? - - - CD ROM Drive - - - Cloud Drive - - - Fixed Disk Drive - - - Floppy Disk Drive - - - Network Drive - - - Unmounted Drive - - - RAM Disk Drive - - - Removable Storage Device - - - Unknown - - - Virtual Drive - - - Date created - - - More options... - - - Map network drive - - - Pinned - - - Libraries - - - No item selected - - - Target - - - Arguments - - - Default - - - Item count - - - This action requires administrator rights - - - Would you like to continue as administrator? - - - Columns (Ctrl+Shift+6) - - - Pin to the Start Menu - - - Unpin from the Start Menu - - - Library - - - Locations: - - - Set as default save path - - - No locations - - - Input field cannot be empty! - - - The name must not contain the following characters: \ / : * ? " < > | - - - Using this name is not allowed! - - - Library with the same name already exists! - - - Open Storage Sense - - - Copy - - - Copy {0, plural, one {item} other {items}} - - - Delete {0, plural, one {item} other {items}} - - - Move - - - {0, plural, one {One item will be moved} other {# items will be moved}} - - - Move {0, plural, one {item} other {items}} - - - {0, plural, one {One item will be deleted} other {# items will be deleted}} - - - Continue - - - {0, plural, one {There is one conflicting file name} other {There are # conflicting file names}} - - - {0, plural, one {There is one conflicting file name} other {There are # conflicting file names}}, and {1, plural, one {one outgoing item} other {# outgoing items}} - - - Conflicting {0, plural, one {file name} other {file names}} - - - {0, plural, one {One item will be copied} other {# items will be copied}} - - - Permanently delete - - - ISO speed - - - Replace existing - - - Generate new name - - - Create folder with selection - - - Type: - - - Date modified: - - - Open with - - - File icon - - - Original path column - - - Item type column - - - Date modified column - - - Date deleted column - - - Sort - - - Date modified - - - Original folder - - - Descending - - - Huge - - - Very large - - - Large - - - Medium - - - Small - - - Tiny - - - Future - - - Today - - - Yesterday - - - Earlier this week - - - Last week - - - Earlier this month - - - Last month - - - Earlier this year - - - Last year - - - Year {0} - - - {0} item - - - {0} items - - - Reopen closed tab - - - Rename - - - Privacy - - - the Recycle Bin - - - Canceling - - - Clear - - - Widgets - - - Sync status - - - Security - - - Advanced permissions - - - Allow - - - Deny - - - Full control - - - List directory contents - - - Modify - - - Permissions for {0} - - - Read and execute - - - Read - - - Group or user names - - - Write - - - Unknown account - - - You do not have permissions to view the security properties of this object. Click "Advanced permissions" to proceed. - - - Owner: - - - Unknown owner - - - Extract archive - - - Open destination folder when complete - - - Extract to {0}\ - - - Create new library - - - Restore default libraries - - - Are you sure you want to restore the default libraries? All your files will remain on your storage. - - - Restore Libraries - - - Owner - - - Special - - - Access - - - Applies to - - - Principal - - - files - - - this folder - - - subfolders - - - Inherited - - - Permissions - - - You do not have permissions to view the security properties of this object. You can try to take ownership of this object. - - - This folder and files - - - This folder, subfolders and files - - - Only files - - - Only subfolders - - - Only subfolders and files - - - Only this folder - - - This folder and subfolders - - - Append data - - - Change permission - - - Create folders - - - Create files - - - Delete subdirectories and files - - - Execute files - - - Read attributes - - - Read data - - - Read extended attributes - - - Read permissions - - - Take ownership - - - Visit folder - - - Write attributes - - - Write data - - - Write extended attributes - - - Convert inherited permissions into explicit permissions - - - Enable inheritance - - - Remove all inherited permissions - - - Reset child permissions - - - Replace all child object permissions entries with inheritable permission entries from this object - - - Close active pane - - - Enter compact overlay - - - Exit compact overlay - - - File description - - - Company - - - Language - - - Trademarks - - - File version - - - Load full preview - - - Downloads the item from the cloud and loads the preview - - - {0, plural, one {# item} other {# items}} ({1, plural, one {# file} other {# files}}, {2, plural, one {# folder} other {# folders}}) - - - Uncompressed size - - - WSL - - - Hide {0} section - - - Add file - - - Updates available - - - Updates are ready to install - - - Close - - - Update - - - Home - - - Ctrl+Shift+1 - - - Ctrl+Shift+2 - - - Ctrl+Shift+6 - - - The archive extraction completed successfully. - - - Extracting archive - - - Extracting complete! - - - Open parent folder - - - Unknown - - - Edit tags - - - Part of set - - - Open drive - - - Please insert a disc into drive {0} - - - Insert a disc - - - OK - - - Credential required - - - Anonymous - - - Provide your credential: - - - UserName - - - No items found - - - Open log location - - - Create link in {0} - - - Columns - - - Tiles - - - Open folders in new tab - - - Status center - - - Date created column - - - Item size column - - - Experimental feature flags - - - Help and support - - - Submit feature request - - - Submit bug report - - - Set Files as the default file manager - - - Use Files as Open File Dialog - - - Tag - - - Open items with a single click - - - Folder path - - - Export settings - - - Import settings - - - Couldn't import settings. The settings file is corrupted. - - - Error importing settings - - - Empty Recycle Bin - - - Sidebar - - - Choose a custom folder icon - - - Customization - - - Restore default - - - Open tab in existing instance when opening Files from another app - - - Startup settings - - - Compatibility - - - None - - - On Windows login - - - On this program start - - - System - - - System (Enhanced) - - - 16-bit (65536) color - - - 8-bit (256) color - - - Disable full-screen optimizations - - - Run in 640 x 480 screen resolution - - - Override high DPI scaling behavior - - - Reduced color mode - - - Register this program for restart - - - Run as administrator - - - Run compatibility troubleshooter - - - Use DPI settings of the main monitor - - - Do not adjust DPI - - - Do not override DPI - - - Compatibility mode - - - No reduced color - - - Third party libraries - - - This option modifies the system registry and can have unexpected side effects on your device. Continue at your own risk. - - - Create Library - - - Enter Library name - - - Calculate folder sizes - - - Recent files - - - Open Files on Windows startup - - - Attributes - - - Hidden - - - More details - - - Read only - - - Cleanup your drive contents - - - Type - - - Bytes - - - Extract - - - Extract files - - - Extract here - - - Ctrl+E - - - Extract to {0} - - - Run script - - - Run with PowerShell - - - Calculating folder sizes is resource intensive and may cause your CPU usage to increase. - - - Rotate left - - - Rotate right - - - Install - - - Close tabs to the left - - - Close tabs to the right - - - Close other tabs - - - Music - - - Pictures - - - Update Files - - - Tags - - - Universal - - - ex: {0}, {1} - - - Show thumbnails - - - Retry - - - Move operation is not supported in this context. Do you want to copy the items instead? - - - Hidden items - - - Custom - - - Set as desktop slideshow - - - Size all columns to fit - - - Adaptive layout - - - Adaptive layout (Ctrl+Shift+7) - - - Ctrl+Shift+7 - - - Show alternate data streams - - - Behaviors - - - Review Files - - - Would you like to review Files? - - - Set as background - - - Set as desktop background - - - Set as default - - - Date column - - - Date created column - - - Size column - - - Tag column - - - Type column - - - Lockscreen - - - Open folders with a single click in the Columns Layout - - - Opening items - - - Compress - - - Select files and folders when hovering over them - - - Are you sure you want to restore all items in the recycle bin? - - - Restore all items - - - Do you want to restore the {0} selected item(s)? - - - Restore selection - - - Restore All Items - - - Restore - - - Shortcut cannot be opened - - - The target destination cannot be found {0}. Do you want to delete this shortcut? - - - You don't have permission to access this folder. - - - Archive password - - - Path - - - Sorting and grouping - - - Create archive - - - Create - - - Create {0} - - - Enter a name - - - Compression level - - - Ultra - - - High - - - Normal - - - Low - - - Fast - - - None - - - Splitting size - - - Do not split - - - CD - - - DVD - - - FAT - - - Blu-ray - - - Encryption - - - Password - - - Format - - - Sync status column - - - Group by - - - Sort by - - - Group in descending order - - - Sort in descending order - - - Create a new shortcut - - - Create shortcuts to local or network programs, files, folders, computers or Internet addresses. - - - Enter the location of the item: - - - Creates a shortcut - - - Recently used files is currently disabled in Windows File Explorer. - - - Edit settings file - - - What's new - - - Creating a shortcut in this location requires administrator privileges - - - Would you like to create the shortcut on the desktop instead? - - - The specified item name is invalid - - - Settings - - - Double click on a blank space to go up one directory - - - View more - - - Show edit tags flyout - - - Show compression options - - - Show send to menu - - - Show option to open folders in a new tab - - - Show option to open folders in a new window - - - Quick access - - - Enter your credentials to connect to: {0} - - - Enter network credentials - - - Remember my credentials - - - Network folder error - - - App version - - - Windows version - - - Always - - - Permanent deletion only - - - Never - - - Tag color - - - New tag - - - Create new tag - - - Loading... - - - Context menu options - - - Show file extensions - - - Show hidden items - - - Format... - - - Help - - - Toggle full screen - - - Are you sure you want to delete this tag? - - - Play all - - - Height - - - Width - - - Apply this action to all conflicting items - - - Open in Windows Terminal - - - Open in Windows Terminal as administrator - - - Save - - - Multiselect - - - Reorder sidebar items - - - Hashes - - - An error occurred during the calculation - - - Failed to calculate the hash, please close the file and try again. - - - Hashes aren't available for online files - - - Algorithm - - - Hash value - - - Select hashes to show - - - Calculating... - - - Pinned items - - - Decrease size - - - Increase size - - - Toggle sort direction - - - Invalid name - - - Name must not be empty or start or end with a period. - - - Videos - - - Launch preview popup - - - Toggle compact overlay - - - Open online help page in browser - - - Toggle full screen - - - Enter compact overlay - - - Exit compact overlay - - - Toggle compact overlay - - - Go to search box - - - Toggle whether to show hidden items - - - Toggle whether to show file extensions - - - Toggle the preview pane to view file previews - - - Toggle whether to show sidebar - - - Copy item(s) to clipboard - - - Copy path of selected items to the clipboard - - - Copy path of selected items with quotes to the clipboard - - - Cut item(s) to clipboard - - - Paste item(s) from clipboard to current folder - - - Paste item(s) from clipboard to selected folder - - - Delete item(s) - - - Create new folder - - - Create new shortcut(s) to selected item(s) - - - Create new shortcut to any item - - - Empty recycle bin - - - Open "Format Drive" menu for selected item - - - Restore selected item(s) from recycle bin - - - Restore all items from recycle bin - - - Open item(s) - - - Open item(s) with selected application - - - Open parent folder of searched item - - - Refresh page contents - - - Rename selected item - - - Select all items - - - Invert item selection - - - Clear item selection - - - Toggle item selection - - - Share selected file(s) with others - - - Pin item(s) to the Start Menu - - - Unpin item(s) from the Start Menu - - - Pin folder(s) to Sidebar - - - Unpin folder(s) from Sidebar - - - Set selected picture as desktop background - - - Set selected pictures as desktop slideshow - - - Set selected picture as lockscreen background - - - Set selected picture as the app background - - - Install selected font(s) - - - Install driver(s) using selected inf file(s) - - - Install selected certificate(s) - - - Run selected application as administrator - - - Run selected application as another user - - - Run selected PowerShell script - - - Launch preview in popup window - - - Create archive with selected item(s) - - - Create 7z archive instantly with selected item(s) - - - Create zip archive instantly with selected item(s) - - - Extract items from selected archive(s) to any folder - - - Extract items from selected archive(s) to current folder - - - Extract items from selected archive(s) to new folder - - - Rotate selected image(s) to the left - - - Rotate selected image(s) to the right - - - Open settings page - - - Open folder in terminal - - - Open folder in terminal as administrator - - - Decrease item size in the current view - - - Increase item size in the current view - - - Switch to details view - - - Switch to tiles view - - - Switch to list view - - - List - - - Grid - - - Switch to grid view - - - Switch to columns view - - - Switch views adaptively - - - Sort items by name - - - Sort items by date modified - - - Sort items by date created - - - Sort items by size - - - Sort items by type - - - Sort items by sync status - - - Sort items by tags - - - Sort items by original folder - - - Sort items by date deleted - - - Sort items in ascending order - - - Sort items in descending order - - - Toggle item sort direction - - - List items without grouping - - - Group items by name - - - Group items by date modified - - - Group items by date created - - - Group items by size - - - Group items by type - - - Group items by sync status - - - Group items by tags - - - Group items by original folder - - - Group items by date deleted - - - Group items by folder path - - - Sort groups in ascending order - - - Sort groups in descending order - - - Toggle group sort direction - - - Open new tab - - - Navigate backward in navigation history - - - Navigate forward in navigation history - - - Navigate up one directory - - - Duplicate current tab - - - Duplicate selected tab - - - Close tabs to the left of current tab - - - Close tabs to the left of selected tab - - - Close tabs to the right of current tab - - - Close tabs to the right of selected tab - - - Close tabs other than current tab - - - Close tabs other than selected tab - - - Reopen last closed tab - - - Move to the previous tab - - - Move to the next tab - - - Close current tab - - - Close active pane - - - Focus other pane - - - Switch focus to the non active pane - - - Toggle the sidebar - - - Alt - Key name for hotkeys in menus. Use abbreviation if possible. - - - Ctrl - Key name for hotkeys in menus. Use abbreviation if possible. - - - Shift - Key name for hotkeys in menus. Use abbreviation if possible. - - - Win - Key name for hotkeys in menus. Use abbreviation if possible. - - - Enter - Key name for hotkeys in menus. Use abbreviation if possible. - - - Space - Key name for hotkeys in menus. Use abbreviation if possible. - - - Esc - Key name for hotkeys in menus. Use abbreviation if possible. - - - Backspace - Key name for hotkeys in menus. Use abbreviation if possible. - - - Tab - Key name for hotkeys in menus. Use abbreviation if possible. - - - Ins - Key name for hotkeys in menus. Use abbreviation if possible. - - - Del - Key name for hotkeys in menus. Use abbreviation if possible. - - - Left - Key name for hotkeys in menus. Use abbreviation if possible. - - - Right - Key name for hotkeys in menus. Use abbreviation if possible. - - - Down - Key name for hotkeys in menus. Use abbreviation if possible. - - - Up - Key name for hotkeys in menus. Use abbreviation if possible. - - - Home - Key name for hotkeys in menus. Use abbreviation if possible. - - - End - Key name for hotkeys in menus. Use abbreviation if possible. - - - PageDown - Key name for hotkeys in menus. Use abbreviation if possible. - - - PageUp - Key name for hotkeys in menus. Use abbreviation if possible. - - - Sep - Key name for hotkeys in menus. Use abbreviation if possible. - - - Pause - Key name for hotkeys in menus. Use abbreviation if possible. - - - Sleep - Key name for hotkeys in menus. Use abbreviation if possible. - - - Clear - Key name for hotkeys in menus. Use abbreviation if possible. - - - Print - Key name for hotkeys in menus. Use abbreviation if possible. - - - Help - Key name for hotkeys in menus. Use abbreviation if possible. - - - Mouse4 - Key name for hotkeys in menus. Use abbreviation if possible. - - - Mouse5 - Key name for hotkeys in menus. Use abbreviation if possible. - - - App - Key name for hotkeys in menus. Use abbreviation if possible. - - - App1 - Key name for hotkeys in menus. Use abbreviation if possible. - - - App2 - Key name for hotkeys in menus. Use abbreviation if possible. - - - Mail - Key name for hotkeys in menus. Use abbreviation if possible. - - - GoHome - Key name for hotkeys in menus. Use abbreviation if possible. - - - GoBack - Key name for hotkeys in menus. Use abbreviation if possible. - - - GoForward - Key name for hotkeys in menus. Use abbreviation if possible. - - - BrowserRefresh - Key name for hotkeys in menus. Use abbreviation if possible. - - - BrowserStop - Key name for hotkeys in menus. Use abbreviation if possible. - - - Search - Key name for hotkeys in menus. Use abbreviation if possible. - - - Favorites - Key name for hotkeys in menus. Use abbreviation if possible. - - - PlayPause - Key name for hotkeys in menus. Use abbreviation if possible. - - - MediaStop - Key name for hotkeys in menus. Use abbreviation if possible. - - - PrevTrack - Key name for hotkeys in menus. Use abbreviation if possible. - - - NextTrack - Key name for hotkeys in menus. Use abbreviation if possible. - - - MediaSelect - Key name for hotkeys in menus. Use abbreviation if possible. - - - Mute - Key name for hotkeys in menus. Use abbreviation if possible. - - - VolDown - Key name for hotkeys in menus. Use abbreviation if possible. - - - VolUp - Key name for hotkeys in menus. Use abbreviation if possible. - - - Change - - - Toggle selection - - - Show warning when changing file extensions - - - This PC - - - Recycle Bin - - - Untagged - - - Moves to the previous tab - - - Moves to the next tab - - - Closes current tab - - - Edit path - - - Redo - - - Undo - - - Invalid location - - - Are you sure you want to copy these files without their properties? - - - These files have properties that can't be copied to the new location - - - These files have properties that can't be moved to the new location - - - Are you sure you want to move these files without their properties? - - - Show checkboxes when selecting items - - - Focus path bar - - - Create new item - - - No groups or users have permission to access this object. However, the owner of this object can assign permissions. - - - Open the item's location - - - Delete permanently - - - Delete item(s) permanently - - - Play the selected media files - - - Undo the last file operation - - - Redo the last file operation - - - Location: - - - Group items by month - - - Group items by year - - - Month - - - Day - - - Toggle unit for grouping by date - - - Toggle grouping unit - - - Year - - - Group by date unit - - - Group items by day of date created - - - Group items by month of date created - - - Group items by year of date created - - - Group items by day of date deleted - - - Group items by month of date deleted - - - Group items by year of date deleted - - - Group items by day of date modified - - - Group items by month of date modified - - - Group items by year of date modified - - - Click 'Advanced permissions' to continue. - - - You must have Read permissions to view the properties of this item. - - - To try taking ownership of the item, which includes permission to view its properties, click Change above. - - - Unable to display permissions. - - - Leave my changes on '{0}' - - - Discard my changes - - - Bring my changes to '{0}' - - - You have uncommitted changes on this branch. What would you like to do with them? - - - Switch branch - - - Branches - - - Switch - - - New branch - - - Invalid branch name - - - Create branch - - - Create branch - - - Based on - - - Switch to new branch - - - Create a folder with the currently selected item(s) - - - Open properties - - - Open properties window - - - Locals - - - Remotes - - - Translate on Crowdin - - - Fetch - - - Pull - - - Run git fetch - - - Run git pull - - - Preview - - - Git status - - - Git error - - - git pull failed due to a timeout error - - - (multiple values) - Text indicating that multiple selected files have different metadata values. - - - Author - - - Date committed - - - Commit message - - - Commit SHA - - - Git - - - Acrylic - - - Mica - - - Mica Alt - - - Backdrop Material - - - Solid - - - Manage branches - - - Push - - - Run git push - - - Sync - - - Run git pull and then git push - - - {0} outgoing / {1} incoming commits - - - Connect to GitHub - - - Enter the following code at the link below to start working with GitHub repositories. - - - Files cannot access GitHub right now. - - - Open folder in VS Code - - - Open the current directory in Visual Studio Code - - - Open repo in VS Code - - - Open the root of the Git repo in Visual Studio Code - - - Copy code - - - Added - - - Deleted - - - Modified - - - Untracked - - - Unable to display current owner. - - - Great! You are now authorized. - - - Initialize repo - - - Initialize a Git repository - - - Remote Repository Name - - - Current Branch - - - Path column - - - Sort items by path - - - Open directory in new pane - - - Open directory in new tab - - - Open directory in new window - - - Open all - - - Open all tagged items - - - Drive ({0}) - {0} is the drive letter. - - - Invalid command - - - '{0}' is not recognized as a command. - - - Command not executable - - - The '{0}' command is not ready to be executed. - - - Command palette - - - Open command palette - - - Start in: - - - Turn on BitLocker - - - Manage BitLocker - - - The following items are too large to be copied to this drive - - - Format drive - - - Don't show again - - - Files is running as administrator - - - Due to a limitation with Windows and WinAppSdk, drag and drop isn't available when running Files as admin. If you wish to use drag and drop, you can work around this limitation by opening UAC (User Account Control) from the Start Menu and selecting the third level, and restarting Windows. Otherwise, you can keep using Files without drag & drop. - - - Leave app running in the background when the window is closed - - - Blue - One of the custom color themes - - - Blue Gray - One of the custom color themes - - - Brick Red - One of the custom color themes - - - Camouflage - One of the custom color themes - - - Cool Blue Bright - One of the custom color themes - - - Gray - One of the custom color themes - - - Gray Dark - One of the custom color themes - - - Green - One of the custom color themes - - - Iris Pastel - One of the custom color themes - - - Mint Light - One of the custom color themes - - - Mod Red - One of the custom color themes - - - Orange Bright - One of the custom color themes - - - Overcast - One of the custom color themes - - - Red - One of the custom color themes - - - Rose Bright - One of the custom color themes - - - Seafoam - One of the custom color themes - - - Storm - One of the custom color themes - - - Violet Red Light - One of the custom color themes - - - Yellow Gold - One of the custom color themes - - - Clear completed - - - Name: - - - {0} of {1} processed - Shown in a StatusCenter card. Used as "64 MB of 1 GB processed" - - - items - - - Files can't initialize this directory as a Git repository. - - - Contributing Artist - - - Add page - - - Processing items... - - - Speed: - - - Canceled compressing {0} items to "{1}" - Shown in a StatusCenter card. - - - Canceled compressing {0} items from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Compressed {0} item(s) to "{1}" - Shown in a StatusCenter card. - - - Compressed {0} item(s) from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Error compressing {0} item(s) to "{1}" - Shown in a StatusCenter card. - - - Failed to compress {0} item(s) from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Compressing {0} item(s) to "{1}" - Shown in a StatusCenter card. - - - Compressing {0} item(s) from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Canceled copying {0} item(s) to "{1}" - Shown in a StatusCenter card. - - - Canceled copying {0} item(s) from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Copied {0} item(s) to "{1}" - Shown in a StatusCenter card. - - - Copied {0} item(s) from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Error copying {0} item(s) to "{1}" - Shown in a StatusCenter card. - - - Failed to copy {0} item(s) from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Copying {0} item(s) to "{1}" - Shown in a StatusCenter card. - - - Copying {0} item(s) from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Canceled extracting "{0}" to "{1}" - Shown in a StatusCenter card. - - - Canceled extracting "{0}" from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Extracted "{0}" to "{1}" - Shown in a StatusCenter card. - - - Extracted "{0}" from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Error extracting "{0}" to "{1}" - Shown in a StatusCenter card. - - - Failed to extract "{0}" from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Extracting "{0}" to "{1}" - Shown in a StatusCenter card. - - - Extracting "{0}" from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Canceled deleting {0} item(s) from "{1}" - Shown in a StatusCenter card. - - - Deleted {0} item(s) from "{1}" - Shown in a StatusCenter card. - - - Error deleting {0} item(s) from "{1}" - Shown in a StatusCenter card. - - - Failed to delete {0} item(s) from "{1}" - Shown in a StatusCenter card. - - - Deleting {0} item(s) from "{1}" - Shown in a StatusCenter card. - - - Canceled moving {0} item(s) to "{1}" - Shown in a StatusCenter card. - - - Canceled moving {0} item(s) from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Moved {0} item(s) to "{1}" - Shown in a StatusCenter card. - - - Moved {0} item(s) from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Moving {0} item(s) to "{1}" - Shown in a StatusCenter card. - - - Moving {0} item(s) from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Error moving {0} item(s) to "{1}" - Shown in a StatusCenter card. - - - Failed to move {0} item(s) from "{1}" to "{2}" - Shown in a StatusCenter card. - - - Canceled emptying Recycle Bin - Shown in a StatusCenter card. - - - Emptied Recycle Bin - Shown in a StatusCenter card. - - - Emptying Recycle Bin - Shown in a StatusCenter card. - - - Error emptying Recycle Bin - Shown in a StatusCenter card. - - - Failed to empty Recycle Bin - Shown in a StatusCenter card. - - - Preparing the operation... - Shown in a StatusCenter card. - - - {0}/{1} item(s) processed - Shown in a StatusCenter card. Used as "8/20 items processed" - - - Unblock downloaded file - - - No ongoing file operations - - - The option to open Files on Windows Startup is not available due to your system settings or group policy. To re-enable, open the startup page in Task Manager. - - - Failed to restore items from Recycle Bin - - - Failed to set the background wallpaper - - - Delete Git branch - - - Are you sure you want to permanently delete "{0}" branch? - - - Connected to GitHub - - - Logout - - - Connect to GitHub - - - Login - - - Tags are currently only compatible on drives formatted as NTFS. - - - There was an error applying this tag - - - Extract items from selected archive(s) to current folder for single-item archive, or to new folder for multi-item archive - - - Extract here (Smart) - - - Sort files and folders together - - - Sort files and folders together - - - Sort files first - - - Sort files first then folders - - - Sort folders first - - - Sort folders first then files - - - Sort priority - - - Failed to rotate the image - - - Restart - - - Quit - - - Failed to share items - - - Scroll to previous folder when navigating up - - - Open new window - - - Change album cover - - - Failed to rename item - - - Editing "{0}" requires additional permissions - - - Edit permissions - - - Files is still running in the background to improve startup performance. - - - Where did Files go? - - - Questions & discussions - - - Additional sizes are not yet available for the Tiles View. - - - Compact - Used to describe layout sizes - - - Small - Used to describe layout sizes - - - Medium - Used to describe layout sizes - - - Medium + - Used to describe layout sizes - - - Medium ++ - Used to describe layout sizes - - - Medium +++ - Used to describe layout sizes - - - Medium ++++ - Used to describe layout sizes - - - Medium +++++ - Used to describe layout sizes - - - Large - Used to describe layout sizes - - - Large + - Used to describe layout sizes - - - Large ++ - Used to describe layout sizes - - - Large +++ - Used to describe layout sizes - - - Extra large - Used to describe layout sizes - - - Details view - - - Layout type - - - Actions - - - Commands - - - Add command - - - Restore defaults - - - Choose an action - - - Are you sure you want to restore the default key bindings? This action cannot be undone. - - - This key binding is already being used, please choose a different key binding to continue. - - - The key binding you choose cannot be used, please try again using a different key binding. - - - Customized - - - Adaptive layout is not available when preferences are synced, the default layout has been changed to Details. - - - Background image - - - Bottom - Image alignment type - - - Center - Image alignment type - - - Fill - Image stretch type - - - Horizontal alignment - - - Image fit - - - Left - Image alignment type - - - Opacity - - - Right - Image alignment type - - - Top - Image alignment type - - - Uniform - Image stretch type - - - Uniform to fill - Image stretch type - - - Vertical alignment - - - Thin Acrylic - This is a type of backdrop for the application background - - - Show for all locations - Setting where users can choose to display "Open IDE" button for Git Repos - - - Developer tools - - - Configure the "Open IDE" button on the status bar - - - Show for Git repos - Setting where users can choose to display "Open IDE" button for all locations. - - - Application extension - This is the friendly name for DLL files. - - - ICO File - This is the friendly name for ICO files. - - - Zip File - This is the friendly name for ZIP files. - - - Bitmap Files - This is the friendly name for bitmap files. - - - Num - - - Network locations - - - There are no network locations. If you don't use network locations, you can disable the widget. - - - Disable - - - Edit in Notepad - - - Edit the selected file in Notepad - - - Dimensions: - - - Status: - - - Show Toolbar - Setting that controls if the Toolbar is shown in the main view - - - Tab actions menu - - - Add vertical pane - - - Add a vertical pane - - - Add horizontal pane - - - Add a horizontal pane - - - Arrange vertically - - - Arrange panes vertically - - - Arrange horizontally - - - Arrange panes horizontally - - - Add pane - - - Show tab actions button in the title bar - - - Arrange panes - - - Default pane arrangement - - - Horizontal - - - Vertical - - \ No newline at end of file diff --git a/src/Files.App/Strings/pl-PL/Resources.resw b/src/Files.App/Strings/pl-PL/Resources.resw index 47509d10a676..30c69c33a23a 100644 --- a/src/Files.App/Strings/pl-PL/Resources.resw +++ b/src/Files.App/Strings/pl-PL/Resources.resw @@ -123,9 +123,15 @@ Kopiuj ścieżkę + + Kopiuj ścieżkę pliku/folderu + Kopiuj ścieżkę w cudzysłowie + + Skopiuj wybraną ścieżkę produktu z cudzysłowami + Przeglądaj @@ -175,10 +181,10 @@ Ostatnio używany: - Wyczyść ostatnie elementy + Wyczyść wszystkie elementy - Wprowadź ścieżkę, do której chcesz przejść lub wpisz ">", aby otworzyć paletę poleceń + Wprowadź ścieżkę nawigacji lub wpisz ">", aby otworzyć paletę poleceń Szukaj @@ -187,7 +193,7 @@ Tutaj pojawią się wcześniej otwierane pliki - Usuń z listy + Usuń ten element Repozytorium GitHub @@ -196,7 +202,7 @@ O aplikacji - Otwarty kod źródłówy + Otwarty kod źródłowy Format daty @@ -249,6 +255,9 @@ Wklej + + Wklej skrót + Nowy @@ -283,7 +292,7 @@ Przyznaj uprawnienia - Przed rozpoczęciem należy przyznać aplikacji uprawnienia do odczytu plików. Otworzy się karta w Ustawieniach, gdzie można przyznać te uprawnienia. Będzie wymagany restart aplikacji po wprowadzeniu zmian. + Aby rozpocząć, musisz przyznać uprawnienia do odczytu Twoich plików. Spowoduje to otwarcie strony w Ustawieniach, na której możesz to wykonać. Po wprowadzeniu zmian należy ponownie otworzyć aplikację. Widok @@ -291,8 +300,8 @@ Wprowadź nazwę elementu - - Ustaw nazwę + + Utwórz nowy {0} Jasny @@ -300,11 +309,14 @@ Ciemny + + Użyj ustawień systemowych + Aplikacja - Systemowy + Systemu Nowy folder @@ -319,7 +331,7 @@ Wstecz - Dalej + Naprzód W górę @@ -346,7 +358,7 @@ Nie mogliśmy usunąć tego elementu - Włóż właściwy dysk aby uzyskać dostęp do tego elementu. + Włóż właściwy dysk, aby uzyskać dostęp do tego elementu. Dysk odłączony @@ -439,11 +451,11 @@ Podana nazwa elementu jest nieprawidłowa - {0, plural, one {# element wybrany} few {# elementy wybrane} many {# elementy wybrane} other {# elementy wybrane}} + {0, plural, one {# element wybrany} few {# elementy wybrane} many {# elementów wybranych} other {# elementy wybrane}} ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural - {0, plural, one {przedmiot} few {przedmioty} many {przedmioty} other {przedmioty}} + {0, plural, one {przedmiot} few {przedmioty} many {przedmiotów} other {przedmioty}} ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural @@ -477,10 +489,10 @@ B - {0, number} {0, plural, one {plik} other {plików}}, {1, number} {1, plural, one {folder} other {folderów}} + {0, number} {0, plural, one {plik} few {pliki} other {plików}}, {1, number} {1, plural, one {folder} few {foldery} other {folderów}} - {0, number} {0, plural, one {plik} other {plików}}, {1, number} {1, plural, one {folder} other {folderów}} z {2, number} {2, plural, one {lokacji} other {lokacji}} + {0, number} {0, plural, one {plik} few {pliki} other {plików}}, {1, number} {1, plural, one {folder} few {foldery} other {folderów}} z {2, number} {2, plural, one {lokacji} other {lokacji}} Uruchom jako inny użytkownik @@ -515,9 +527,6 @@ Status - - Domyślny dla systemu Windows - Brak wyników @@ -530,6 +539,9 @@ Kopiuj do {0} + + Sklonuj do {0} + Utwórz skrót @@ -747,7 +759,7 @@ Odległość ogniskowa - Przysłona + Obiektyw Nazwa osób @@ -864,7 +876,7 @@ Data utworzenia - Całkowity czas zakończenia + Całkowity czas edycji Szablon @@ -873,7 +885,7 @@ Ilość słów - Ilość liter + Ilość znaków Ilość linii @@ -891,13 +903,13 @@ Klatki na sekundę - Współczynnik kodowania Bitrate + Przepływność kodowania - Bitrate kodowania audio + Przepływność kodowania dźwięku - Bitrate kodowania wideo + Przepływność kodowania wideo Kompresja @@ -948,7 +960,7 @@ Wybierz - Element Kosza + Kosz Skrót @@ -998,6 +1010,9 @@ Wyniki wyszukiwania {0} w {1} + + Wyniki wyszukiwania dla "{0}" + Szczegóły @@ -1005,7 +1020,7 @@ Nie - Pokaż chronione pliki systemu operacyjnego + Pokaż chronione pliki systemu Synchronizuj układ i preferencje sortowania pomiędzy katalogami @@ -1052,9 +1067,6 @@ Widok szczegółów (Ctrl+Shift+1) - - Widok kafelków (Ctrl+Shift+2) - Data usunięcia @@ -1068,7 +1080,7 @@ Żądana nazwa - Przełącz panel zapory sieciowej + Przełącz panel podglądu Przełącz panel szczegółów @@ -1077,19 +1089,34 @@ Przełącz panel szczegółów, aby wyświetlić podstawowe właściwości pliku - Przełącz panel informacyjny + Przełącz panel informacji - Przełącz panel informacji, aby wyświetlić szczegóły/podgląd + Przełącz widoczność panelu szczegółów/podglądu Przełącz pasek narzędzi + + Przełącz widoczność paska narzędzi + + + Przełącz nagłówek filtru + + + Przełącz widoczność nagłówka filtru + + + Przełącz tryb dwupanelowy + + + Przełącz tryb dwupanelowy + Podgląd niedostępny - Nazwa + Nazwa elementu Odepnij z panelu bocznego @@ -1110,16 +1137,16 @@ Opinia - Dostępne w trybie Online + Dostępne w trybie online Dokumentacja - Dostępne w trybie Offline + Dostępne w trybie offline - Częściowo dostępne w trybie Offline + Częściowo dostępne w trybie offline Synchronizowanie @@ -1140,7 +1167,7 @@ Stacja dysków CD ROM - Chmura + Dysk w chmurze Dysk twardy @@ -1158,7 +1185,7 @@ Dysk RAM - Wymienny urządzenie magazynujące + Wymienne urządzenie pamięci masowej Nieznany @@ -1206,10 +1233,10 @@ Kolumny (Ctrl+Shift+6) - Przypnij do Menu Start + Przypnij do menu Start - Odepnij od Menu Start + Odepnij od menu Start Biblioteka @@ -1230,52 +1257,58 @@ Nazwa nie może posiadać znaków: \ / : * ? " < > | - Niedozwolona nazwa + Niedozwolona nazwa! Bliblioteka z tą samą nazwą już istnieje! - Otwórz Storage Sense + Otwórz Czujnik pamięci + + + Włącz stronę Czujnika pamięci w ustawieniach Windows + + + Wyczyść Kopiuj - Copy {0, plural, one {item} other {items}} + Skopiuj {0, plural, one {element} other {elementy}} - Delete {0, plural, one {item} other {items}} + Usuń {0, plural, one {element} other{elementy}} - Przenoszenie + Przenieś - {0, plural, one {One item will be moved} other {# items will be moved}} + {0, plural, one {# element zostanie przeniesiony} few {# elementy zostaną przeniesione} many {# elementów zostanie przeniesionych} other {# elementów zostanie przeniesionych}} - Move {0, plural, one {item} other {items}} + Przenieś {0, plural, one {element} other {elementy}} - {0, plural, one {One item will be deleted} other {# items will be deleted}} + {0, plural, one {# element zostanie usunięty} few {# elementy zostaną usunięte} many {# elementów zostanie usuniętych} other {# elementów zostanie usuniętych}} Kontynuuj - {0, plural, one {There is one conflicting file name} other {There are # conflicting file names}} + {0, plural, one {Istnieje # sprzeczna nazwa pliku} few {Istnieją # sprzeczne nazwy plików} many {Istnieje # sprzecznych nazw plików} other {Istnieje # sprzecznych nazw plików}} - {0, plural, one {There is one conflicting file name} other {There are # conflicting file names}}, and {1, plural, one {one outgoing item} other {# outgoing items}} + {0, plural, one {Istnieje # sprzeczna nazwa pliku} few {Istnieją # sprzeczne nazwy plików} many {Istnieje # sprzecznych nazw plików} other {Istnieje # sprzecznych nazw plików}} i {1, plural, one {# element wychodzący} few {# elementy wychodzące} many {# elementów wychodzących} other {# elementów wychodzących}} - Conflicting {0, plural, one {file name} other {file names}} + Konflikt {0, plural, one {nazwy pliku} few {nazw plików} many {nazw plików} other {nazw plików}} - {0, plural, one {One item will be copied} other {# items will be copied}} + {0, plural, one {# element zostanie skopiowany} few {# elementy zostaną skopiowane} many {# elementów zostanie skopiowanych} other {# elementów zostanie skopiowanych}} - Usuń trwale + Usuń na stałe Szybkość ISO @@ -1380,7 +1413,7 @@ {0} elementy - Otwórz ponownie zamkniętą kartę + Ponownie otwórz zakładkę Zmień nazwę @@ -1443,7 +1476,7 @@ Nieznane konto - Nie masz uprawnień do przeglądania zabezpieczeń tego objektu. Kliknij "Uprawnienia zaawansowane" aby kontynuować. + Nie masz uprawnień do przeglądania zabezpieczeń tego obiektu. Kliknij "Uprawnienia zaawansowane" aby kontynuować. Właściciel: @@ -1470,13 +1503,13 @@ Czy na pewno chcesz przywrócić domyślne biblioteki? Wszystkie pliki pozostaną na Twoim dysku. - Przywróć Biblioteki + Przywróć biblioteki Właściciel - Uprawnienia specjalne + Specjalne Dostęp @@ -1503,7 +1536,7 @@ Uprawnienia - Nie masz uprawnień do przeglądania właściwości zabezpieczeń tego obiektu. Możesz spróbować przejąć własność nad tym objektem. + Nie masz uprawnień do przeglądania właściwości zabezpieczeń tego obiektu. Możesz spróbować przejąć własność nad tym obiektem. Ten folder i pliki @@ -1587,7 +1620,7 @@ Zastąp wszystkie wpisy uprawnień obiektu podrzędnego wpisami uprawnień dziedzicznych z tego obiektu - Close active pane + Zamknij panel Otwórz kompaktową nakładkę @@ -1608,7 +1641,7 @@ Znaki towarowe - Wersja pliku: + Wersja pliku Załaduj pełny podgląd @@ -1617,7 +1650,7 @@ Pobiera element z chmury i ładuje podgląd - {0, plural, one {# item} other {# items}} ({1, plural, one {# file} other {# files}}, {2, plural, one {# folder} other {# folders}}) + {0, plural, one {# element} few {# elementy} other {# elementów}} ({1, plural, one {# plik} few {# pliki} other {# plików}}, {2, plural, one {# folder} few {# foldery} other {# folderów}}) Rozmiar nieskompresowany @@ -1626,7 +1659,7 @@ WSL - Ukryj {0} sekcję + Ukryj sekcję {0} Dodaj plik @@ -1646,17 +1679,8 @@ Strona główna - - Ctrl+Shift+1 - - - Ctrl+Shift+2 - - - Ctrl+Shift+6 - - Rozpakowywanie archiwum zakończone pomyślnie. + Wypakowywanie archiwum zakończone pomyślnie. Wypakowywanie archiwum @@ -1712,8 +1736,8 @@ Kolumny - - Kafelki + + Karty Otwieraj foldery w nowej karcie @@ -1749,7 +1773,7 @@ Tag pliku - Otwórz elementy jednym kliknięciem + Otwieraj pliki jednym kliknięciem Lokalizacja folderu @@ -1794,16 +1818,16 @@ Brak - Przy zalogowaniu do systemu Windows + Przy logowaniu Windows Przy uruchomieniu programu - Systemowy + System - System (rozwinięty) + System (ulepszony) 16-bitowy (65536) kolor @@ -1818,7 +1842,7 @@ Uruchom w rozdzielczości ekranu 640 x 480 - Zastąp skalowanie przy wysokiej rozdzielczości DPI + Zastąp zachowanie skalowania przy wysokim DPI Tryb ograniczonych kolorów @@ -1826,6 +1850,18 @@ Zarejestruj ten program do ponownego uruchomienia + + Okno startowe + + + Okno normalne + + + Zminimalizowane + + + Zmaksymalizowane + Uruchom jako administrator @@ -1833,7 +1869,7 @@ Uruchom narzędzie do rozwiązywania problemów ze zgodnością - Użyj ustawień skalowania głównego monitora + Użyj ustawień DPI głównego monitora Nie dostosowuj DPI @@ -1848,11 +1884,14 @@ Brak redukcji kolorów - Third party libraries + Biblioteki zewnętrzne To ustawienie modyfikuje pliki systemu i może wywołać niechciane efekty uboczne na Twoim urządzeniu. Kontynuujesz na własne ryzyko. + + Operacje płaskie są trwałe i nie są zalecane. Kontynuuj na własne ryzyko. + Utwórz bibliotekę @@ -1908,7 +1947,7 @@ Uruchom skrypt - Otwórz za pomocą programu PowerShell + Uruchom przy użyciu PowerShell Obliczanie rozmiaru folderów wymaga dużej ilości zasobów i może spowodować wzrost wykorzystania procesora. @@ -1931,6 +1970,9 @@ Zamknij inne karty + + Zamknij wszystkie karty + Muzyka @@ -1985,14 +2027,26 @@ Zachowania - - Oceń Files + + Cześć! + + + Podoba Ci się Files? Rozważ zostawienie recenzji w sklepie Microsoft. + + + Podoba Ci się Files? Rozważ wsparcie projektu na GitHub. + + + Sponsoruj + + + Oceń nas - - Czy chcesz ocenić Files? + + Odrzuć - Ustaw tło + Ustaw jako tło Ustaw jako tło pulpitu @@ -2018,8 +2072,8 @@ Lockscreen - - Otwórz foldery jednym za pomocą pojedynczego kliknięcia w kolumnę szczegóły + + Otwieraj foldery pojedynczym kliknięciem Otwieranie elementów @@ -2027,6 +2081,21 @@ Skompresuj + + Przenieś całą zawartość z podfolderów do wybranej lokalizacji + + + Spłaszcz folder + + + Spłaszcz + + + Spłaszczenie folderu przeniesie całą zawartość z jego podfolderów do wybranej lokalizacji. Ta operacja jest trwała i nie może zostać cofnięta. Korzystając z tej funkcji eksperymentalnej, potwierdzasz ryzyko i zgadzasz się nie ponosić odpowiedzialności zespołu Files za utratę danych. + + + Pokaż opcje spłaszczania + Zaznacz pliki i foldery w trakcie trzymania kursora nad nimi @@ -2037,7 +2106,7 @@ Przywróć wszystkie elementy - Czy chcesz przywrócić {0} element(ów)? + Czy chcesz przywrócić {0, plural, one {wybrany element} few {{0} wybrane elementy} many {{0} wybranych elementów} other {{0} wybranych elementów}}? Przywróć zaznaczone @@ -2060,6 +2129,12 @@ Hasło do archiwum + + Kodowanie + + + {0} (wykrytych) + Ścieżka @@ -2102,9 +2177,24 @@ Rozmiar po podziale - + Nie dziel + + Auto + + + Dictionary size + + + Word size + + + Estimated memory usage: {0} + + + Available memory: {0} + CD @@ -2159,8 +2249,14 @@ Edytuj plik ustawień - - Co nowego + + Otwórz plik ustawień w domyślnym edytorze + + + Lista zmian + + + Otwórz listę zmian Tworzenie skrótu w tej lokalizacji wymaga uprawnień administratora @@ -2190,10 +2286,10 @@ Pokaż wysyłanie do menu - Pokaż opcję otwierania folderów w nowym panelu + Pokaż opcję otwierania folderów w nowej zakładce - Pokaż opcję otwierania folderów w nowym panelu + Pokaż opcję otwierania folderów w nowym oknie Szybki dostęp @@ -2220,7 +2316,7 @@ Zawsze - Tylko trwałe usuwanie + Tylko usuwanie na stałe Nigdy @@ -2241,25 +2337,22 @@ Opcje menu kontekstowego - Pokaż rozszerzenia plików - - - Pokaż ukryte elementy + Rozszerzenia plików - Formatuj... + Format Pomoc - Przełącz pełny ekran + Pełny ekran Na pewno chcesz usunąć ten tag? - Odtwórz wszystko + Odtwórz Wysokość @@ -2271,7 +2364,7 @@ Zastosuj tę akcję do wszystkich sprzecznych elementów - Otwórz w Terminalu Windows + Otwórz w Windows Terminal Otwórz jako administrator w Windows Terminal @@ -2304,7 +2397,7 @@ Wartość haszowania - Wybierz hasze do pokazania + Wybierz hashe do pokazania Obliczanie... @@ -2319,7 +2412,7 @@ Zwiększ rozmiar - Zmień kolejność sortowania + Kierunek sortowania Nieprawidłowa nazwa @@ -2331,7 +2424,7 @@ Wideo - Otwórz podgląd + Pokaż okno podglądu Przełącz kompaktową nakładkę @@ -2340,25 +2433,28 @@ Otwórz stronę pomocy w przeglądarce - Przełącz pełny ekran + Przełącz tryb pełnoekranowy - Otwórz kompaktową nakładkę + Wejdź w tryb kompaktowy - Wyjdź z kompaktowej nakładki + Wyjdź z trybu kompaktowego - Przełącz kompaktową nakładkę + Przełącz tryb kompaktowy - Przejdź do pola wyszukiwania + Rozpocznij wyszukiwanie w OmniBar - Przełącz, czy pokazać ukryte elementy + Przełącz widoczność plików ukrytych + + + Przełącz widoczność pilików zaczynających się kropką - Przełącz, czy pokazywać rozszerzenia plików + Przełącz widoczność rozszerzeń plików Przełącz panel podglądu, aby wyświetlić podgląd pliku @@ -2367,52 +2463,64 @@ Przełącz, czy wyświetlić pasek boczny - Kopiuj element(y) do schowka + Skopiuj {0, plural, one {wybrany plik/folder} other {wybrane pliki/foldery}} - Skopiuj ścieżkę wybranych elementów do schowka + Kopiuj ścieżkę do bieżącego folderu + + + Kopiuj ścieżkę do zaznaczonych plików/folderów + + + Kopiuj ścieżkę zaznaczonych elementów z cudzysłowami - Skopiuj ścieżkę wybranych elementów do schowka z cudzysłowami + Kopiuj ścieżkę do bieżącego folderu z cudzysłowami - Wytnij element(y) do schowka + Wytnij {0, plural, one {wybrany plik/folder} other {pliki/foldery}} - Wklej element(y) ze schowka do bieżącego folderu + Wklej pliki/foldery do bieżącego folderu + + + Wklej pliki/foldery do bieżącego folderu jako skróty - Wklej element(y) ze schowka do wybranego folderu + Wklej pliki/foldery do wybranego folderu + + + Wklej do wybranego folderu - Usuń element(y) + Usuń {0, plural, one {wybrany plik/folder} other {wybrane pliki/foldery}} Utwórz nowy folder - Utwórz nowy skrót do zaznaczonych elementów + Utwórz {0, plural, one {nowy skrót} few {nowe skróty} many {nowe skróty} other {nowe skróty}} do {0, plural, one {wybranego elementu} few {wybranych elementów} many {wybranych elementów} other {wybranych elementów}} Utwórz nowy skrót do dowolnego elementu - Opróżnij kosz + Opróżnij zawartość kosza Otwórz menu "Formatuj dysk" dla wybranego elementu - Przywróć zaznaczone elementy z kosza + Przywróć {0, plural, one {wybrany przedmiot} few {wybrane przedmioty} many {wybrane przedmioty} other {wybrane przedmioty}} z kosza Przywróć wszystkie elementy z kosza - Otwórz element(y) + Otwórz {0, plural, one {element} other {elementy}} - Otwórz element(y) za pomocą wybranej aplikacji + Otwórz {0, plural, one {element} few {elementy} many {elementy} other {elementy}} w wybranej aplikacji Otwórz folder nadrzędny dla wyszukanego elementu @@ -2427,28 +2535,28 @@ Zaznacz wszystkie elementy - Odwróć znaczenie + Odwróć zaznaczenie plików/folderów - Wyczyść zaznaczenie + Odznacz pliki/foldery Przełącz wybór zaznaczenia - Udostępnij wybrane pliki innym + Udostępnij {0, plural, one {wybrany plik} few {wybrane pliki} many {wybrane pliki} other {pliki}} z innymi - Przypnij element(y) do menu Start + Przypnij {0, plural, one {element} other {elementy}} do menu Start - Odepnij element(y) z Menu Start + Odepnij {0, plural, one {element} other {elementy}} z menu Start - Przypnij folder(y) do panelu bocznego + Przypnij {0, plural, one {folder} other {foldery}} do paska bocznego - Odepnij folder(y) z panelu bocznego + Odepnij {0, plural, one {folder} other {foldery}} z paska bocznego Ustaw zaznaczony obraz jako tło pulpitu @@ -2462,14 +2570,23 @@ Ustaw wybrane zdjęcie jako tło aplikacji + + Instaluj czcionkę + + + Instaluj sterownik + + + Instaluj certyfikat + - Zainstaluj wybrane czcionki + Zainstaluj {0, plural, one {wybraną czcionkę} other {wybrane czcionki}} - Zainstaluj sterownik(i) używając wybranych plików inf + Zainstaluj {0, plural, one {sterownik} other {sterowniki}} używając {0, plural, one {wybranego pliku} other {wybrane pliki}} inf - Zainstaluj wybrane certyfikaty + Zainstaluj {0, plural, one {wybrany certyfikat} other {wybrane certyfikaty}} Uruchom wybraną aplikację jako administrator @@ -2484,28 +2601,28 @@ Uruchom podgląd w wyskakującym oknie - Utwórz archiwum z wybranymi elementami + Stwórz archiwum z {0, plural, one {wybranym przedmiotem} other {wybranymi przedmiotami}} - Utwórz archiwum 7z z wybranymi elementami + Stwórz archiwum 7z z {0, plural, one {wybranym przedmiotem} other {wybranymi przedmiotami}} - Utwórz archiwum zip z wybranymi elementami + Stwórz archiwum zip z {0, plural, one {wybranym przedmiotem} other {wybranymi przedmiotami}} - Wypakuj elementy z wybranego archiwum(ów) do dowolnego folderu + Wyodrębnij wybrane {0, plural, one {achiwum} other {archiwa}} do dowolnego folderu - Wypakuj elementy z wybranego archiwum(ów) do obecnego folderu + Wyodrębnij wybrane {0, plural, one {achiwum} other {archiwa}} do bieżączego folderu - Wypakuj elementy z wybranego archiwum(ów) do nowego folderu + Wyodrębnij wybrane {0, plural, one {achiwum} other {archiwa}} do nowego folderu - Obróć wybrane obrazy w lewo + Obróć {0, plural, one {wybrany obraz} other {wybrane obrazy}} w lewo - Obróć wybrane obrazy w prawo + Obróć {0, plural, one {wybrany obraz} other {wybrane obrazy}} w prawo Otwórz stronę ustawień @@ -2525,8 +2642,8 @@ Przełącz na widok szczegółowy - - Przełącz na widok kafelek + + Przełącz na widok kart Przełącz na widok listy @@ -2544,7 +2661,7 @@ Przełącz na widok kolumn - Przełącz widoki adaptacyjnie + Zmień widoki adaptacyjnie Sortuj według nazwy @@ -2628,13 +2745,13 @@ Otwórz nową kartę - Idź wstecz w historii nawigacji + Przejdź wstecz - Idź do przodu w historii nawigacji + Przejdź do przodu - Idź o jeden katalog wyżej + Przejdź jeden katalog wyżej Duplikuj bieżącą kartę @@ -2660,6 +2777,9 @@ Zamknij wszystkie karty oprócz zaznaczonej + + Zamknij wszystkie karty wraz z bieżącą kartą + Przywróć ostatnio zamkniętą kartę @@ -2673,13 +2793,13 @@ Zamknij bieżącą kartę - Close active pane + Zamknij aktywny panel - Focus other pane + Skup się na innej stronie - Switch focus to the non active pane + Przełącz fokus na inną stronę Przełącz boczny pasek @@ -2887,13 +3007,13 @@ Nietagowane - Przenosi do poprzedniej karty + Poprzednia karta - Przenosi do następnej karty + Następna zakładka - Zamyka bieżącą kartę + Zamknij kartę Edytuj ścieżkę @@ -2923,7 +3043,7 @@ Pokaż pola zaznaczenia podczas wybierania elementów - Przełącz pasek ścieżki + Edytuj ścieżkę w OmniBar Utwórz nowy element @@ -2935,10 +3055,10 @@ Otwórz lokalizację elementu - Usuń trwale + Usuń na stałe - Usuń element(y) na stałe + Usuń wybrane {0, plural, one {element} other {elementy}} permanentnie Odtwórz wybrane pliki multimedialne @@ -3027,6 +3147,15 @@ Masz niezatwierdzone zmiany w tej gałęzi. Co chciałbyś z nimi zrobić? + + Masz trwające połączenie z nierozwiązanymi konfliktami. Rozwiąż konflikty lub przerwij scalanie. + + + Przerwij scalanie i przełącz na '{0}' + + + Pozostań w '{0}' i rozwiąż konflikty + Zmień gałąź @@ -3055,14 +3184,20 @@ Przełącz na nową gałąź - Utwórz folder z aktualnie wybranymi elementami + Utwórz folder z obecnie {0, plural, one {wybranym przedmiotem} other {wybranymi przedmiotami}} - Otwórz właściwości + Właściwości + + + Otwórz właściwości Eksploratora Plików Otwórz okno właściwości + + Otwórz właściwości okna Eksploratora Plików + Lokalne @@ -3081,6 +3216,9 @@ Uruchom git fetch + + Sklonuj repozytorium git + Uruchom git pull @@ -3157,17 +3295,17 @@ Pliki nie mogą teraz uzyskać dostępu do GitHub. - - Otwórz folder w VS Code + + Otwórz folder w {0} - - Otwórz bieżącą ścieżkę w Visual Studio Code + + Otwórz bieżący katalog w {0} - - Otwórz repozytorium w VS Code + + Otwórz repozytorium w {0} - - Otwórz źródło repozytorium Git w Visual Studio Code + + Otwórz źródło repozytorium git w {0} Skopiuj kod @@ -3188,16 +3326,16 @@ Nie można wyświetlić obecnego właściciela. - Wspaniale! Jesteś teraz autoryzowany. + Świetnie! Jesteś teraz autoryzowany. - Zainicjuj repozytorium + Inicjalizuj repozytorium - Inicjalizuj repozytorium Git + Zainicjuj bieżący folder jako repozytorium GitHub - Nazwa Zdalnego Repozytorium + Nazwa zdalnego repozytorium Bieżąca gałąź @@ -3209,13 +3347,13 @@ Sortuj pozycje według ścieżki - Otwórz folder w nowym panelu + Otwórz zaznaczony folder w nowym panelu - Otwórz folder w nowej karcie + Otwórz wybrany katalog w nowej karcie - Otwórz folder w nowym oknie + Otwórz zaznaczony katalog w nowym oknie Otwórz wszystko @@ -3243,7 +3381,7 @@ Paleta poleceń - Otwórz paletę poleceń + Otwórz paletę poleceń w OmniBar Rozpocznij w: @@ -3317,7 +3455,7 @@ One of the custom color themes - Jasny Pomarańczowy + Jasny pomarańczowy One of the custom color themes @@ -3329,7 +3467,7 @@ One of the custom color themes - Jasny Różowy + Jasny różowy One of the custom color themes @@ -3373,6 +3511,9 @@ Przetwarzanie elementów... + + Odkryj przedmioty... + Prędkość: @@ -3385,61 +3526,129 @@ Shown in a StatusCenter card. - Skompresowano {0} plik(ów) do "{1}" + Skompresowano {0, plural, one {# przedmiot} few {# przedmiotów} many {# przedmiotów} other {# przedmiotów}} do "{1}" Shown in a StatusCenter card. - Skompresowano {0} plik(ów) z "{1}" do "{2}" + Skompresowano {0, plural, one {# przedmiot} few {# przedmiotów} many {# przedmiotów} other {# przedmiotów}} z "{1}" do "{2}" Shown in a StatusCenter card. - Błąd kompresowania {0} plik(ów) do "{1}" + Błąd kompresji {0, plural, one {# przedmiot} few {# przedmiotów} many {# przedmiotów} other {# przedmiotów}} do "{1}" Shown in a StatusCenter card. - Błąd kompresowania {0} plik(ów) z "{1}" do "{2}" + Nie udało się skompresować {0, plural, one {# przedmiotu} few {# przedmiotów} many {# przedmiotów} other {# przedmiotów}} z "{1}" do "{2}" Shown in a StatusCenter card. - Kompresowanie {0} plików do "{1}" + Kompresowanie {0, plural, one {# przedmiotu} few {# przedmiotów} many {# przedmiotów} other {# przedmiotów}} do "{1}" Shown in a StatusCenter card. - Kompresowanie {0} plików z "{1}" do "{2}" + Kompresowanie {0, plural, one {# przedmiotu} few {# przedmiotów} many {# przedmiotów} other {# przedmiotów}} z "{1}" do "{2}" + Shown in a StatusCenter card. + + + Anulowano klonowanie {0} do "{1}" + Shown in a StatusCenter card. + + + Anulowano klonowanie {0} z "{1}" do "{2}" + Shown in a StatusCenter card. + + + Sklonowano "{0}" do "{1}" + Shown in a StatusCenter card. + + + Sklonowano {0} z "{1}" do "{2}" + Shown in a StatusCenter card. + + + Błąd klonowania "{0}" do "{1}" + Shown in a StatusCenter card. + + + Nie udało się sklonować {0} z "{1}" do "{2}" + Shown in a StatusCenter card. + + + Klonowanie "{0}" do "{1}" + Shown in a StatusCenter card. + + + Klonowanie {0} z "{1}" do "{2}" + Shown in a StatusCenter card. + + + Anulowano instalację {0} czcionki/-ek + Shown in a StatusCenter card. + + + Anulowano instalację {0, plural, one {# czcionki} few {# czcionek} many {# czcionek} other {# czcionek}} z "{1}" + Shown in a StatusCenter card. + + + Zainstalowano {0, plural, one {# czcionkę} few {# czcionki} many {# czcionki} other {# czcionki}} + Shown in a StatusCenter card. + + + Zainstalowano {0, plural, one {# czcionkę} few {# czcionki} many {# czcionki} other {# czcionki}} z "{1}" + Shown in a StatusCenter card. + + + Błąd instalacji {0, plural, one {# czcionki} few {# czcionek} many {# czcionek} other {# czcionki}} + Shown in a StatusCenter card. + + + Nie udało się zainstalować {0, plural, one {# czcionki} few {# czcionek} many {# czcionek} other {# czcionki}} z "{1}" + Shown in a StatusCenter card. + + + Instalowanie {0, plural, one {# czcionki} few {# czcionek} many {# czcionek} other {# czcionek}} + Shown in a StatusCenter card. + + + Instalowanie {0, plural, one {# czcionki} few {# czcionek} many {# czcionek} other {# czcionek}} z "{1}" Shown in a StatusCenter card. - Anulowano kopiowanie {0} plik(ów) do "{1}" + Anulowano kopiowanie {0, plural, one {# przedmiotu} few {# przedmiotów} many {# przedmiotów} other {# przedmiotów}} do "{1}" Shown in a StatusCenter card. - Anulowano kopiowanie {0} plik(ów) z "{1}" do "{2}" + Anulowano kopiowanie {0, plural, one {# przedmiotu} few {# przedmiotów} many {# przedmiotów} other {# przedmiotów}} z "{1}" do "{2}" Shown in a StatusCenter card. - Skopiowano {0} plik(ów) do "{1}" + Kopiowanie {0, plural, one {# przedmiotu} few {# przedmiotów} many {# przedmiotów} other {# przedmiotów}} do "{1}" Shown in a StatusCenter card. - Skopiowano {0} plik(ów) z "{1}" do "{2}" + Skopiowano {0, plural, one {# element} few {# elementy} many {# elementy} other {# elementy}} z "{1}" do "{2}" Shown in a StatusCenter card. - Błąd kopiowania {0} plików do "{1}" + Błąd kopiowania {0, plural, one {# elementu} few {# elementów} many {# elementów} other {# elementów}} do "{1}" Shown in a StatusCenter card. - Błąd kopiowania {0} plików z "{1}" do "{2}" + Nie udało się skopiować {0, plural, one {# elementu} other {# elementów}} z "{1}" do "{2}" Shown in a StatusCenter card. - Kopiowanie {0} elementu/ów do "{1}" + Kopiowanie {0, plural, one {# elementu} few {# elementów} many {# elementów} other {# elementy}} do "{1}" Shown in a StatusCenter card. - Skopiowano {0} plik(ów) z "{1}" do "{2}" + Kopiowanie {0, plural, one {# elementu} few {# elementów} many {# elementów} other {# elementy}} z "{1}" do "{2}" Shown in a StatusCenter card. + + Znaleziono {0, plural, one {# element} few {# elementów} many {# elementów} other {# elementy}} + Shown in a StatusCenter card during file discovery phase. + Anulowano wypakowywanie "{0}" do "{1}" Shown in a StatusCenter card. @@ -3449,11 +3658,11 @@ Shown in a StatusCenter card. - Rozpakowano "{0}" do "{1}" + Wypakowano "{0}" do "{1}" Shown in a StatusCenter card. - Rozpakowano "{0}" z "{1}" do "{2}" + Wypakowano "{0}" z "{1}" do "{2}" Shown in a StatusCenter card. @@ -3469,59 +3678,59 @@ Shown in a StatusCenter card. - Wyodrębnianie "{0}" z "{1}" do "{2}" + Wypakowywanie "{0}" z "{1}" do "{2}" Shown in a StatusCenter card. - Anulowano usuwanie {0} elementów z "{1}" + Anulowano kopiowanie {0, plural, one {# elementu} few {# elementów} many {# elementów} other {# elementów}} z "{1}" Shown in a StatusCenter card. - Usunięto {0} elementów z "{1}" + Usunięto {0, plural, one {# element} few {# elementów} many {# elementów} other {# elementów}} from "{1}" Shown in a StatusCenter card. - Błąd przy usuwaniu {0} elementów z "{1}" + Błąd usuwania {0, plural, one {# elementu} few {# elementów} many {# elementów} other {# elementów}} z "{1}" Shown in a StatusCenter card. - Nie udało się usunąć {0} elementów z "{1}" + Nie udało się usunąć {0, plural, one {# elementu} other {# elementów}} z "{1}" Shown in a StatusCenter card. - Usuwanie {0} elementów z "{1}" + Usuwanie {0, plural, one {# elementu} other {# elementów}} z "{1}" Shown in a StatusCenter card. - Anulowano przenoszenie {0} plików do "{1}" + Anulowano przenoszenie {0, plural, one {# elementu} other {# elementów}} do "{1}" Shown in a StatusCenter card. - Anulowano przenoszenie {0} plików z "{1}" do "{2}" + Anulowano przenoszenie {0, plural, one {# elementu} other {# elementów}} z "{1}" do "{2}" Shown in a StatusCenter card. - Przeniesiono {0} element(y/ów) do "{1}" + Moved {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Przeniesiono {0} element(y/ów) z "{1}" do "{2}" + Moved {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Przenoszenie {0} elementu/ów do "{1}" + Moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Przenoszenie {0} elementu/ów z "{1}" do "{2}" + Moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Błąd przesuwania {0} plików do "{1}" + Error moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Nie udało się przenieść {0} plików z "{1}" do "{2}" + Failed to move {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. @@ -3549,7 +3758,7 @@ Shown in a StatusCenter card. - {0}/{1} plików przetworzone + {0}/{1, plural, one {# item} other {# items}} processed Shown in a StatusCenter card. Used as "8/20 items processed" @@ -3567,6 +3776,9 @@ Nie udało się ustawić tapety tła + + Nie udało się otworzyć pliku ustawień + Usuń gałąź Git @@ -3592,16 +3804,16 @@ Wystąpił błąd podczas stosowania tego tagu - Wypakuj elementy z wybranych archiwów do bieżącego folderu dla jednoelementowych archiwów lub do nowego folderu dla wieloelementowych archiwów + Wypakuj wybrane {0, plural, one {archiwum} few {archiwa} many {archiwa} other {archiwa}} tutaj dla pojedynczego elementu lub do folderu dla wielu elementów - Wypakuj tutaj (Smart) + Wypakuj tutaj (Inteligentnie) Posortuj razem pliki i foldery - Posortuj razem pliki i foldery + Sortuj pliki i foldery na tej samej liście Posortuj najpierw pliki @@ -3652,14 +3864,11 @@ Files dalej działa w tle, aby poprawić wydajność uruchamiania. - Co się stało z Files? + Gdzie się podziało Files? Pytania i dyskusja - - Dodatkowe rozmiary nie są jeszcze dostępne dla widoku kafelków. - Kompaktowy Used to describe layout sizes @@ -3746,7 +3955,7 @@ Dostosuj - Adaptacyjny układ nie jest dostępny, gdy preferencje są synchronizowane, domyślny układ został zmieniony na Szczegóły. + Adaptacyjny układ nie jest dostępny, gdy preferencje są synchronizowane; domyślny układ został zmieniony na Szczegóły. Obraz tła @@ -3796,7 +4005,7 @@ Wyrównanie pionowe - Cienka akryla + Cienki Akryl This is a type of backdrop for the application background @@ -3821,6 +4030,10 @@ Plik ICO This is the friendly name for ICO files. + + Plik ICL + This is the friendly name for ICL files. + Plik zip This is the friendly name for ZIP files. @@ -3829,6 +4042,10 @@ Pliki bitmapowe This is the friendly name for bitmap files. + + Pliki obrazów + This is the friendly name for image files. + Nr. @@ -3848,58 +4065,316 @@ Edytuj wybrany plik w notatniku - Dimensions: + Wymiary: - Status: + Stan: - Show Toolbar - Setting that controls if the Toolbar is shown in the main view + Pokaż pasek narzędzi + Setting that controls if the toolbar is shown in the main view - Tab actions menu + Menu akcji kart - - Add vertical pane + + Podziel w pionie - Add a vertical pane + Split pane vertically - - Add horizontal pane + + Podziel w poziomie - - Add a horizontal pane + + Split pane horizontally - Arrange vertically + Ułóż pionowo - Arrange panes vertically + Ułóż strony pionowo - Arrange horizontally + Ułóż poziomo - Arrange panes horizontally + Ułóż strony poziomo - - Add pane + + podział panelu - Show tab actions button in the title bar + Pokaż przycisk akcji karty na pasku tytułu - Arrange panes + Ułóż strony + + + Default dual pane split direction - - Default pane arrangement + + Dual pane mode - Horizontal + Poziomo - Vertical + Pionowo + + + Pokaż ikonę Files w zasobniku systemowym + + + Wątki CPU + + + Przejdź do strony głównej + + + Paski narzędzi + + + ID użytkownika + + + Masowa zmiana nazw + + + Skompresuj zawartość + + + Pokaż opcję tworzenia alternatywnego strumienia danych + + + Pokaż alternatywne strumienie danych + + + Utwórz alternatywny strumień danych dla {0, plural, one {wybranego przedmiotu} other {wybranych przedmiotów}} + + + Wprowadź nazwę strumienia danych + + + Wystąpił błąd podczas tworzenia alternatywnego strumienia danych + + + Pamiętaj, że alternatywne strumienie danych działają tylko na dyskach sformatowanych jako NTFS. + + + Alternatywne strumienie danych są obecnie ukryte + + + Czy chcesz wyświetlić alternatywne strumienie danych? Możesz zmienić to ustawienie w dowolnym momencie na stronie ustawień plików i folderów. + + + Zarządzaj tagami + + + Dostępne + + + Razem + + + Zawsze przełącz fokus na nowo utworzoną kartę + + + Toggle shelf pane + + + Toggle the visibility of the shelf pane + + + Pokaż przełącznik panelu półki na pasku adresu + + + Półka + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + Wyczyść zawartość + + + Usuń z półki + + + Dodaj do półki + Tooltip that displays when dragging items to the Shelf Pane + + + Wprowadź kod hash do porównania + Placeholder that appears in the compare hash text box + + + Pasuje do {0} + Appears when two compared hashes match, e.g. "Matches SHA256" + + + Nie znaleziono dopasowań + Appears when two compared hashes don't match + + + Ścieżka lub alias + + + Nieprawidłowa ścieżka + + + Integracja testowa + + + Nie można zlokalizować {0}. Sprawdź ustawienia i spróbuj ponownie. + + + Nie można odnaleźć skonfigurowanego IDE + + + Otwórz ustawienia + + + Visual Studio Code + + + Wprowadź ścieżkę lub alias uruchamiania + + + Wprowadź nazwę IDE + + + Sklonuj repozytorium + Clone repo dialog title + + + Sklonuj + Primary action button in the clone repo dialog + + + Adres URL repozytorium + URL textbox header in the clone repo dialog + + + Nie można sklonować repozytorium + Cannot clone repo dialog title + + + Porównaj plik + Button that appears in file hash properties that allows the user to compare two files + + + Format rozmiaru + + + Binarny + + + Dziesiętny + + + Możesz dodać sekcje do paska bocznego, klikając prawym przyciskiem myszy i wybierając sekcje, które chcesz dodać + + + Przeciągnij tutaj pliki lub foldery, aby korzystać z nich w różnych kartach + + + Wpisz ścieżkę do... + + + Znajdź funkcje i komendy... + + + Szukaj plików i folderów... + + + During file operations + + + Show status center button + + + Status center progress ring + Screen reader name for the status center progress ring + + + Pliki ikon + This is the friendly name for a variety of different icon files. + + + Show collapsed path breadcrumbs + + + Pokaż foldery w {0} + + + Pokaż foldery na stronie głównej + + + Nie ma poleceń zawierających {0} + + + Zobacz więcej + + + Filtrowanie dla + + + Nazwa pliku + + + Sygnatury + + + Lista podpisów + + + Wystawca: + + + Wydany dla: + + + Data wydania: + + + Ważne do: + + + Nie znaleziono podpisu. + + + Pokaż opcję otwierania folderów w Terminalu Windows + + + Otwórz plik dziennika + + + Otwórz plik dziennika w domyślnym edytorze + + + Otwórz lokalizację pliku dziennika w domyślnym eksploratorze plików + + + Nie można otworzyć pliku dziennika + + + Pokaż pasek statusu + + + Only in Columns View + + + Nowy {0} + + + Enable smooth scrolling + + + Scrolling + + + Show option to Pin to Sidebar + + + Show option to Pin to the Start Menu \ No newline at end of file diff --git a/src/Files.App/Strings/pt-BR/Resources.resw b/src/Files.App/Strings/pt-BR/Resources.resw index 09d27e1b9fdb..8774c8832ceb 100644 --- a/src/Files.App/Strings/pt-BR/Resources.resw +++ b/src/Files.App/Strings/pt-BR/Resources.resw @@ -123,9 +123,15 @@ Copiar caminho + + Copiar caminho do item + Copiar caminho entre aspas + + Copiar caminho do item selecionado com aspas + Navegar @@ -160,7 +166,7 @@ Pular - Selecionar Tudo + Selecionar tudo Inverter seleção @@ -181,7 +187,7 @@ Digite um caminho para navegar ou digite ">" para abrir a paleta de comandos - Procurar + Pesquisar Os arquivos que você acessou anteriormente serão exibidos aqui @@ -211,7 +217,7 @@ Mostrar arquivos e pastas ocultas - Mostrar arquivos "." + Mostrar arquivos com ponto inicial Aparência @@ -249,6 +255,9 @@ Colar + + Colar atalho + Novo @@ -268,13 +277,13 @@ Compartilhar - Cortar + Recortar Excluir - Fixar na Barra Lateral + Fixar na barra lateral Bem-vindo ao Files! @@ -291,8 +300,8 @@ Insira o nome do item - - Defina um nome + + Criar novo {0} Claro @@ -300,6 +309,9 @@ Escuro + + Usar configuração do sistema + Aplicativo @@ -316,13 +328,13 @@ Esta pasta está vazia. - Voltar (Alt + Seta para a esquerda) + Voltar - Avançar (Alt + Seta para a direita) + Avançar - Em cima (Alt + Seta para cima) + Acima Atualizar @@ -376,7 +388,7 @@ O arquivo que você está tentando acessar está sendo usado por outro aplicativo - O arquivo está em uso + Arquivo em uso Layout @@ -515,9 +527,6 @@ Status - - Padrão do Windows - Sem resultados @@ -530,6 +539,9 @@ Copiar para {0} + + Clonar para {0} + Criar atalho @@ -998,6 +1010,9 @@ Resultados da pesquisa em {1} para {0} + + Resultados da pesquisa para '{0}' + Detalhes @@ -1005,10 +1020,10 @@ Não - Mostrar arquivos de sistema protegido + Mostrar arquivos protegidos do sistema - Sincronizar o layout e a ordenação entre diretórios + Sincronizar preferências de disposição e classificação entre diretórios Personalize o menu de contexto (Botão Direito) @@ -1052,9 +1067,6 @@ Detalhado (Ctrl+Shift+1) - - Blocos (Ctrl+Shift+2) - Data de exclusão @@ -1080,10 +1092,25 @@ Exibir o painel de informações - Ative o painel de informações para visualizar os painéis\na visualização de detalhes + Alternar a visibilidade dos painéis de detalhe/visualização - Mostrar/Ocultar Barra de Ferramentas + Mostrar/esconder barra de ferramentas + + + Alternar a visibilidade da barra de ferramentas + + + Alternar cabeçalho do filtro + + + Alternar a visibilidade do cabeçalho do filtro + + + Ativar/desativar painel duplo + + + Alternar modo de painel duplo Nenhuma visualização disponível @@ -1236,7 +1263,13 @@ Já existe uma biblioteca com o mesmo nome! - Abrir Sensor de Armanezamento + Abrir Sensor de armazenamento + + + Abrir página do Sensor de Armazenamento nas Configurações do Windows + + + Limpeza Copiar @@ -1314,7 +1347,7 @@ Coluna de data de exclusão - Ordenar + Classificar Data de modificação @@ -1380,7 +1413,7 @@ {0} itens - Reabrir a guia fechada + Reabrir aba Renomear @@ -1587,7 +1620,7 @@ Substituir todas as entradas de permissão de objetos menores por entradas de permissão herdáveis deste objeto - Fechar painel ativo + Fechar painel Insira a sobreposição compacta @@ -1646,15 +1679,6 @@ Início - - Ctrl+Shift+1 - - - Ctrl+Shift+2 - - - Ctrl+Shift+6 - A extração do arquivo foi concluída com sucesso. @@ -1712,8 +1736,8 @@ Colunas - - Blocos + + Cartões Abrir pastas em uma nova aba @@ -1728,7 +1752,7 @@ Coluna de tamanho do item - Flags de recursos experimentais + Sinalizadores de recursos experimentais Ajuda e suporte @@ -1749,7 +1773,7 @@ Etiqueta - Abrir itens com um único clique + Abrir arquivos com um único clique Caminho da pasta @@ -1782,7 +1806,7 @@ Restaurar padrão - Abrir aba na janela existente ao abrir Files a partir de outro programa + Abrir aba na janela existente ao abrir o Files a partir de outro programa Configurações de inicialização @@ -1826,6 +1850,18 @@ Registre este programa para reiniciar + + Janela inicial + + + Janela normal + + + Minimizada + + + Maximizada + Executar como administrador @@ -1853,6 +1889,9 @@ Esta opção modifica o registro do sistema e pode causar efeitos colaterais inesperados no seu dispositivo. Continue por sua conta e risco. + + As operações de nivelamento são permanentes e não recomendadas. Continue por sua conta e risco. + Criar Biblioteca @@ -1866,7 +1905,7 @@ Arquivos recentes - Abrir arquivos na inicialização do Windows + Executar o Files na inicialização do Windows Atributos @@ -1931,6 +1970,9 @@ Fechar demais guias + + Fechar todas as abas + Música @@ -1985,11 +2027,23 @@ Comportamentos - - Revisar Arquivos + + Olá! + + + Gostando do Files? Considere fazer uma avaliação na Microsoft Store. + + + Gostando do Files? Por favor, considere apoiar o projeto no GitHub. + + + Patrocinador - - Deseja revisar Arquivos? + + Avalie-nos + + + Dispensar Definir como plano de fundo @@ -2018,8 +2072,8 @@ Tela de bloqueio - - Abrir pastas com um único clique no Layout de Colunas + + Abrir pastas com um único clique Abrindo itens @@ -2027,6 +2081,21 @@ Comprimir + + Mover todo o conteúdo das subpastas para o local selecionado + + + Nivelar pasta + + + Nivelar + + + Nivelar uma pasta moverá todo o conteúdo de suas subpastas para o local selecionado. Esta operação é permanente e não pode ser desfeita. Ao usar este recurso experimental, você reconhece o risco e concorda em não responsabilizar a equipe do Files por qualquer perda de dados. + + + Mostrar opções de nivelamento + Selecionar arquivos e pastas ao passar o mouse sobre elas @@ -2037,7 +2106,7 @@ Restaurar todos os itens - Deseja restaurar os {0} itens selecionados? + Você quer restaurar {0, plural, one {o item selecionado} other {os {0} itens selecionados}}? Restaurar seleção @@ -2060,11 +2129,17 @@ Senha do arquivo + + Codificação + + + {0} (detectado) + Caminho - Ordenação e agrupamento + Classificar e Agrupar Criar arquivo @@ -2102,9 +2177,24 @@ Tamanho da divisão - + Não dividir + + Automático + + + Tamanho do dicionário + + + Tamanho da palavra + + + Uso estimado da memória: {0} + + + Memória disponível: {0} + CD @@ -2139,13 +2229,13 @@ Agrupar em ordem decrescente - Ordenar em ordem decrescente + Classificar em ordem decrescente Criar um novo atalho - Criar atalhos para programas, arquivos locais ou em rede, pastas, computadores ou endereços de Internet. + Criar atalhos para programas locais ou em rede, arquivos, pastas, computadores ou endereços de Internet. Digite a localização do item: @@ -2154,13 +2244,19 @@ Cria um atalho - Arquivos usados recentemente estão desativados no Windows File Explorer. + Arquivos recentemente usados estão desativados no Explorador de Arquivos do Windows. - Editar arquivo de configurações + Editar o arquivo de configurações + + + Abrir arquivo de configurações no seu editor padrão + + + Notas da versão - - O que há de novo + + Abrir notas da versão Criar um atalho neste local requer privilégios de administrador @@ -2226,7 +2322,7 @@ Nunca - Cor da Etiqueta + Cor da etiqueta Nova etiqueta @@ -2241,25 +2337,22 @@ Opções do menu de contexto - Mostrar extensões de arquivo - - - Mostrar itens ocultos + Extensões de arquivo - Formatar... + Formato Ajuda - Alternar para o modo tela cheia + Tela cheia Tem certeza de que deseja excluir esta tag? - Reproduzir tudo + Reproduzir Altura @@ -2319,100 +2412,115 @@ Aumentar tamanho - Alternar ordem de classificação + Ordenar direção Nome inválido - O nome não pode ser vazio, começar, ou terminar com um período. + O nome não pode estar em branco nem começar ou terminar com um ponto final. Vídeos - Iniciar pré-visualização em janela + Pré-visualizar popup Alternar janela para sobreposição compacta - Abrir a página de ajuda online no navegador + Abrir página de ajuda on-line no navegador - Alternar para o modo tela cheia + Alternar modo de tela cheia - Insira a sobreposição compacta + Entrar no modo de sobreposição compacta - Remova a sobreposição compacta + Sair do modo de sobreposição compacta - Alternar janela para sobreposição compacta + Alternar modo de sobreposição compacta - Ir para o campo de pesquisa + Iniciar busca no OmniBar - Alternar se deseja mostrar itens ocultos + Alternar a visibilidade dos itens ocultos + + + Alternar a visibilidade de arquivos com ponto inicial - Alternar entre mostrar extensões de arquivos + Alternar a visibilidade das extensões de arquivos - Alternar o painel de pré-visualização para visualizar os arquivos + Mostrar ou ocultar o painel de visualização Alternar se deseja mostrar a barra lateral - Copiar item(s) para área de transferência + Copiar {0, plural, one {item selecionado} other {itens selecionados}} - Copiar o caminho dos itens selecionados para a área de transferência + Copiar caminho da pasta atual + + + Copiar caminho dos itens selecionados + + + Copiar caminho dos itens selecionados com aspas - Copiar o caminho dos itens selecionados com cotas para a área de transferência + Copiar caminho da pasta atual com aspas - Recortar item(s) para área de transferência + Recortar {0, plural, one {item selecionado} other {itens selecionados}} - Colar item(s) da área de transferência para a pasta atual + Colar itens na pasta atual + + + Colar itens na pasta atual como atalhos - Colar item(s) da área de transferência para a pasta selecionada + Colar itens na pasta selecionada + + + Cola na pasta selecionada - Excluir item(ns) + Excluir {0, plural, one {item} other {itens}} Criar nova pasta - Criar atalho(s) para o(s) item(s) selecionado(s) + Criar {0, plural, one {atalho} other {atalhos}} para {0, plural, one {item} other {itens}} da seleção Criar novo atalho para qualquer item - Esvaziar lixeira + Esvaziar o conteúdo da Lixeira Abra o menu "Formatar Drive" para o item selecionado - Restaurar itens selecionados da lixeira + Restaurar selecionado {0, plural, one {item} other {itens}} da lixeira Restaurar todos os itens da lixeira - Abrir item(ns) + Abrir {0, plural, one {item} other {itens}} - Abrir item(ns) com o aplicativo selecionado + Abrir {0, plural, one {item} other {itens}} com o aplicativo selecionado Abrir pasta superior do item pesquisado @@ -2427,28 +2535,28 @@ Selecionar todos os itens - Inverter seleção de itens + Inverter itens selecionados - Limpar seleção de itens + Limpar itens selecionados Alternar seleção de itens - Compartilhar arquivo(s) selecionado(s) com outros + Compartilhar {0, plural, one {arquivo} other {arquivos}} selecionado(s) com outros - Fixar item(ns) no Menu Inicial + Fixar {0, plural, one {item} other {itens}} no Menu Iniciar - Desafixar item(ns) do Menu Inicial + Desafixar {0, plural, one {item} other {itens}} do Menu Iniciar - Fixar a(s) pasta(s) na Barra lateral + Fixar {0, plural, one {pasta} other {pastas}} na barra lateral - Desafixar a(s) pasta(s) da Barra lateral + Desafixar a pasta {0, plural, one {} other {pastas}} da barra lateral Definir imagem selecionada como fundo da área de trabalho @@ -2462,14 +2570,23 @@ Definir imagem selecionada como plano de fundo do aplicativo + + Instalar fonte + + + Instalar driver + + + Instalar certificado + - Instalar tipos de letra selecionados + Instalar {0, plural, one {fonte} other {fontes}} selecionada(s) - Instalar driver(s) utilizando arquivo(s) inf selecionado(s) + Instalar {0, plural, one {driver} other {drivers}} usando {0, plural, one {o arquivo} other {os arquivos}} INF selecionado(s) - Instalar certificado(s) selecionado(s) + Instalar {0, plural, one {certificado} other {certificados}} selecionado(s) Executar aplicativo selecionado como administrador @@ -2484,28 +2601,28 @@ Iniciar pré-visualização em janela popup - Criar arquivo com os itens selecionados + Criar arquivo com {0, plural, one {item selecionado} other {itens selecionados}} - Criar arquivo 7z com os itens selecionados + Criar arquivo 7z com {0, plural, one {item selecionado} other {itens selecionados}} - Criar arquivo zip com os itens selecionados + Criar arquivo zip com {0, plural, one {item selecionado} other {itens selecionados}} - Extrair itens do arquivo selecionado para uma pasta + Extrair {0, plural, one {arquivo selecionado} other {arquivos selecionados}} em qualquer pasta - Extrair itens do arquivo selecionado para a pasta atual + Extrair {0, plural, one {arquivo selecionado} other {arquivos selecionados}} na pasta atual - Extrair itens do(s) arquivo(s) selecionado(s) para nova pasta + Extrair {0, plural, one {arquivo selecionado} other {arquivos selecionados}} em uma nova pasta - Girar a(s) imagem(s) selecionada(s) para a esquerda + Girar {0, plural, one {imagem selecionada} other {imagens selecionadas}} para a esquerda - Girar a(s) imagem(s) selecionada(s) para a direita + Girar {0, plural, one {imagem selecionada} other {imagens selecionadas}} para a direita Abrir página de configurações @@ -2525,8 +2642,8 @@ Alternar para exibição de detalhes - - Alternar para visualização de blocos + + Alterar para exibição em cartões Alternar para visualização em lista @@ -2547,19 +2664,19 @@ Alternar visualizações adaptadamente - Ordenar itens por nome + Classificar por nome - Ordenar itens por data de alteração + Classificar por data de modificação - Ordenar itens por data de criação + Classificar por data da criação - Ordenar itens por tamanho + Classificar por tamanho - Ordenar itens por tipo + Classificar por tipo Ordena os itens por status de sincronização @@ -2571,13 +2688,13 @@ Ordenar itens por pasta original - Ordenar itens por data de exclusão + Classificar por data de exclusão Ordenar itens em ordem crescente - Ordenar itens em ordem decrescente + Classificar em ordem decrescente Alternar direção da ordem dos itens @@ -2625,13 +2742,13 @@ Alternar direção da ordem dos grupos - Abrir nova aba + Abrir uma nova guia - Navegar para trás no histórico de navegação + Navegar para trás - Navegar para frente no histórico de navegação + Navegar para a frente Navegar para cima uma pasta @@ -2660,8 +2777,11 @@ Fechar abas diferentes da aba selecionada + + Fechar todas as abas, incluindo a aba atual + - Reabrir última aba fechada + Reabrir aba fechada recentemente Mover para a aba anterior @@ -2673,13 +2793,13 @@ Fechar aba atual - Fechar painel ativo + Fechar o painel ativo Focar outro painel - Mudar foco para o painel não-ativo + Mudar foco para o outro painel Mostrar/ocultar barra lateral @@ -2887,13 +3007,13 @@ Sem marcação - Mover para a aba anterior + Aba anterior - Mover para a próxima aba + Próxima aba - Fechar a aba atual + Fechar aba Editar caminho @@ -2923,7 +3043,7 @@ Mostrar caixas de seleção ao selecionar itens - Foco na barra de endereço + Editar caminho no OmniBar Criar novo item @@ -2938,7 +3058,7 @@ Excluir permanentemente - Excluir itens permanentemente + Excluir {0, plural, one {item} other {itens}} permanentemente Reproduzir os arquivos de mídia selecionados @@ -3027,6 +3147,15 @@ Você tem alterações não confirmadas nesta ramificação. O que você gostaria de fazer com eles? + + Você tem uma mesclagem em andamento com conflitos não resolvidos. Resolva os conflitos ou aborte a mesclagem para alternar os branches. + + + Abortar e mudar para '{0}' + + + Permanecer em '{0}' e resolver conflitos + Alternar branch @@ -3055,14 +3184,20 @@ Mudar para nova Subdivisão - Crie uma pasta com o(s) item(ns) atualmente selecionado(s) + Criar pasta com {0, plural, one {o item atual selecionado} other {os itens atuais selecionados}} - Abrir propriedades + Propriedades + + + Abrir propriedades do Explorador de Arquivos Abrir janela de propriedades + + Abrir janela de propriedades do Explorador de Arquivos + Locais @@ -3076,11 +3211,14 @@ Buscar - Puxar + Obter Executar busca no git + + Clonar repositório git + Puxar git @@ -3125,7 +3263,7 @@ Mica Alt - Material de plano de fundo + Fundo Sólido @@ -3134,7 +3272,7 @@ Gerenciar filiais - Empurrar + Enviar Executar push do git @@ -3157,17 +3295,17 @@ Files não pode acessar o GitHub neste momento. - - Abrir pasta no VS Code + + Abrir pasta no {0} - - Abra a pasta atual no Visual Studio Code + + Abrir a pasta atual no {0} - - Abrir repositório no VS Code + + Abrir repositório no {0} - - Abra a raiz do repositório Git no Visual Studio Code + + Abrir a raiz do repositório Git em {0} Copiar código @@ -3194,7 +3332,7 @@ Inicializar repositório - Inicializar um repositório Git + Inicializar pasta atual como um repositório Git Nome do Repositório Remoto @@ -3206,16 +3344,16 @@ Coluna de caminho - Ordenar itens por caminho + Classificar por caminho - Abrir pasta em um novo painel + Abrir a pasta selecionada num novo painel - Abrir pasta em uma nova aba + Abrir a pasta selecionada em uma nova aba - Abrir pasta em uma nova janela + Abrir pasta selecionada em uma nova janela Abrir todos @@ -3243,7 +3381,7 @@ Paleta de comandos - Abrir paleta de comandos + Abrir paleta de comandos na OmniBar Iniciar em: @@ -3270,7 +3408,7 @@ Devido a uma limitação com Windows e WinAppSdk, arrastar e soltar não está disponível ao executar Arquivos como administrador. Se você deseja usar arrastar e soltar, você pode contornar essa limitação abrindo UAC (Controle de Conta do Usuário) no menu inicial e selecionando o terceiro nível e reiniciando o Windows. Caso contrário, você pode continuar usando Arquivos sem arrastar e soltar. - Manter o aplicativo sendo executado em segundo plano quando a janela do files estiver fechada + Manter o aplicativo sendo executado em segundo plano ao fechar a janela Azul @@ -3373,8 +3511,11 @@ Processando itens... + + Descobrindo itens... + - Veocidade: + Velocidade: Cancelada a compressão dos ítens {0} para "{1}" @@ -3385,61 +3526,129 @@ Shown in a StatusCenter card. - Itens {0} comprimidos para "{1}" + {0, plural, one {# item compactado} other {# items compactados}} para "{1}" Shown in a StatusCenter card. - Item(s) {0} comprimido(s) de "{1}" para "{2}" + {0, plural, one {# item compactado} other {# items compactados}} de "{1}" para "{2}" Shown in a StatusCenter card. - Erro ao comprimir item(s) {0} para "{1}" + Erro ao compactar {0, plural, one {# item} other {# itens}} para "{1}" Shown in a StatusCenter card. - Falha ao comprimir item(s) {0} de "{1}" para "{2}" + Falha ao compactar {0, plural, one {# item} other {# itens}} de "{1}" para "{2}" Shown in a StatusCenter card. - Comprimindo {0} item(s) para "{1}" + Compactando {0, plural, one {# item} other {# itens}} para "{1}" Shown in a StatusCenter card. - Comprimindo item(s) {0} de "{1}" para "{2}" + Compactando {0, plural, one {# item} other {# itens}} de "{1}" para "{2}" + Shown in a StatusCenter card. + + + Clonar {0} para "{1}" cancelado + Shown in a StatusCenter card. + + + Clonar {0} de "{1}" para "{2}" cancelado + Shown in a StatusCenter card. + + + "{0}" para "{1}" clonado + Shown in a StatusCenter card. + + + {0} de "{1}" para "{2}" clonado + Shown in a StatusCenter card. + + + Erro ao clonar "{0}" para "{1}" + Shown in a StatusCenter card. + + + Falha ao clonar {0} de "{1}" para "{2}" + Shown in a StatusCenter card. + + + Clonando "{0}" para "{1}" + Shown in a StatusCenter card. + + + Clonando {0} de "{1}" para "{2}" + Shown in a StatusCenter card. + + + Cancelada a instalação de {0} fontes + Shown in a StatusCenter card. + + + Cancelado instalação de {0, plural, one {# fonte} other {# fontes}} de "{1}" + Shown in a StatusCenter card. + + + {0, plural, one {# fonte instalada} other {# fontes instaladas}} + Shown in a StatusCenter card. + + + {0, plural, one {# fonte instalada} other {# fontes instaladas}} de "{1}" + Shown in a StatusCenter card. + + + Erro ao instalar {0, plural, one {# fonte} other {# fontes}} + Shown in a StatusCenter card. + + + Falha ao instalar {0, plural, one {# fonte} other {# fontes}} de "{1}" + Shown in a StatusCenter card. + + + Instalando {0, plural, one {# fonte} other {# fontes}} + Shown in a StatusCenter card. + + + Instalando {0, plural, one {# fonte} other {# fontes}} de "{1}" Shown in a StatusCenter card. - Cancelando a cópia do item {0} para "{1}" + Cancelada a cópia de {0, plural, one {# item} other {# itens}} para "{1}" Shown in a StatusCenter card. - Cancelando a cópia de {0} item(s) de "{1}" para "{2}" + Cancelada a cópia de {0, plural, one {# item} other {# itens}} de "{1}" para "{2} " " Shown in a StatusCenter card. - Copiado(s) {0} item(s) para "{1}" + {0, plural, one {# item copiado} other {# itens copiados}} para "{1}" Shown in a StatusCenter card. - Copiado(s) {0} item(s) de "{1}" para "{2}" + Copiado {0, plural, one {# item} other {# itens}} de "{1}" para "{2}" Shown in a StatusCenter card. - Erro ao copiar item(s) {0} para "{1}" + Erro ao copiar {0, plural, one {# item} other {# itens}} para "{1}" Shown in a StatusCenter card. - Falha ao copiar item(s) {0} de "{1}" para "{2}" + Falha ao copiar {0, plural, one {# item} other {# itens}} de "{1}" para "{2} " " Shown in a StatusCenter card. - Copiando item(s) {0} para "{1}" + Copiando {0, plural, one {# item} other {# itens}} para "{1}" Shown in a StatusCenter card. - Copiando item(s) {0} de "{1}" para "{2}" + Copiando {0, plural, one {# item} other {# itens}} de "{1}" para "{2}" Shown in a StatusCenter card. + + {0, plural, one {# item descoberto} other {# itens descobertos}} + Shown in a StatusCenter card during file discovery phase. + Extração cancelada "{0}" para "{1}" Shown in a StatusCenter card. @@ -3473,55 +3682,55 @@ Shown in a StatusCenter card. - Exclusão cancelada do item {0} de "{1}" + Cancelada a exclusão de {0, plural, one {# item} other {# itens}} de "{1}" Shown in a StatusCenter card. - {0} item(s) apagados de "{1}" + {0, plural, one {# item excluído} other {# itens excluídos}} de "{1}" Shown in a StatusCenter card. - Erro ao excluir item(s) {0} de "{1}" + Erro ao excluir {0, plural, one {# item} other {# itens}} de "{1}" Shown in a StatusCenter card. - Falha em excluir {0} itens de {1}" + Falha ao excluir {0, plural, one {# item} other {# itens}} de "{1}" Shown in a StatusCenter card. - Excluindo Item(s) {0} de "{1}" + Excluindo {0, plural, one {# item} other {# itens}} de "{1}" " Shown in a StatusCenter card. - Movimentação cancelada do(s) {0} item(s) para "{1}" + Cancelado mover {0, plural, one {# item} other {# itens}} para "{1}" Shown in a StatusCenter card. - Transferência cancelada do item {0} de "{1}" para "{2}" + Cancelado mover {0, plural, one {# item} other {# itens}} de "{1}" para "{2} " " Shown in a StatusCenter card. - {0} item(ns) Movido(s) para "{1}" + {0, plural, one {# item movido} other {# itens movidos}} para "{1}" Shown in a StatusCenter card. - {0} Movido(s) item(ns) de "{1}" para "{2}" + {0, plural, one {# item movido} other {# itens movidos}} de "{1}" para "{2}" Shown in a StatusCenter card. - Movendo {0} item(ns) para "{1}" + Movendo {0, plural, one {# item} other {# itens}} para "{1}" Shown in a StatusCenter card. - Movendo um item(s) {0} de "{1}" para "{2}" + Movendo {0, plural, one {# item} other {# itens}} de "{1}" para "{2}" Shown in a StatusCenter card. - Erro ao mover item(s) {0} para "{1}" + Erro ao mover {0, plural, one {# item} other {# itens}} para "{1}" Shown in a StatusCenter card. - Falha ao mover item(s) {0} de "{1}" para "{2}" + Falha ao mover {0, plural, one {# item} other {# itens}} de "{1}" para "{2}" Shown in a StatusCenter card. @@ -3549,7 +3758,7 @@ Shown in a StatusCenter card. - {0}/{1} item(ns) processado(s) + {0}/ {1, plural, one {# item processado} other {# itens processados}} Shown in a StatusCenter card. Used as "8/20 items processed" @@ -3567,6 +3776,9 @@ Falha ao definir o plano de fundo + + Falha ao abrir o arquivo de configurações + Apagar a Ramificação do Git @@ -3592,16 +3804,16 @@ Ocorreu um erro ao aplicar esta etiqueta - Extrair itens do(s) arquivo(s) selecionado(s) para a pasta atual para um único arquivo, ou para uma nova pasta para arquivo de vários itens + Extrair {0, plural, one {arquivo selecionado} other {arquivos selecionados}} aqui para um único item ou em uma nova pasta para vários itens Extraia aqui (Inteligente) - Ordenar arquivos e pastas juntos + Classificar arquivos e pastas juntos - Ordenar arquivos e pastas juntos + Ordenar arquivos e pastas na mesma lista Ordene arquivos primeiro @@ -3657,9 +3869,6 @@ Perguntas & discussões - - Tamanhos adicionais ainda não estão disponíveis para a Visualização em Blocos. - Compacto Used to describe layout sizes @@ -3746,7 +3955,7 @@ Personalizado - O layout adaptativo não está disponível quando as preferências estão sincronizadas, o layout padrão foi alterado para Detalhes. + A disposição adaptativa não está disponível quando as preferências estão sincronizadas, a disposição padrão foi alterada para Detalhes. Imagem de fundo @@ -3796,7 +4005,7 @@ Alinhamento vertical - acrílico fino + Acrílico Fino This is a type of backdrop for the application background @@ -3821,6 +4030,10 @@ Arquivo ICO This is the friendly name for ICO files. + + Arquivo ICL + This is the friendly name for ICL files. + Arquivo compactado This is the friendly name for ZIP files. @@ -3829,6 +4042,10 @@ Arquivos Bitmap This is the friendly name for bitmap files. + + Arquivos de imagem + This is the friendly name for image files. + Num @@ -3854,23 +4071,23 @@ Status: - Mostrar Barra de Ferramentas - Setting that controls if the Toolbar is shown in the main view + Mostrar barra de ferramentas + Setting that controls if the toolbar is shown in the main view Menu de ações de aba - - Adicionar painel vertical + + Dividir verticalmente - Adicionar painel vertical + Dividir painel verticalmente - - Adicionar painel horizontal + + Dividir horizontalmente - - Adicionar painel horizontal + + Dividir painel horizontalmente Organizar verticalmente @@ -3884,8 +4101,8 @@ Organizar painéis horizontalmente - - Adicionar painel + + Dividir painel Mostrar botão de ações da aba na barra de título @@ -3893,8 +4110,11 @@ Organizar painéis - - Disposição padrão do painel + + Direção padrão do duplo painel dividido + + + Modo de painel duplo Horizontal @@ -3902,4 +4122,259 @@ Vertical + + Mostrar ícone do Files na bandeja do sistema + + + Núcleos da CPU + + + Navegar para a página inicial + + + Barras de ferramentas + + + ID do usuário + + + Renomear em massa + + + Compactar conteúdo + + + Mostrar opção para criar fluxo de dados alternativo + + + Criar fluxo de dados alternativo + + + Crie fluxo de dados alternativos para o item {0, plural, one {item} other {items}} + + + Digite o nome do fluxo de dados + + + Ocorreu um erro ao criar o fluxo de dados alternativo + + + Observe que fluxos de dados alternativos só funcionam em unidades formatadas como NTFS. + + + Fluxos de dados alternativos estão ocultos no momento + + + Você gostaria de exibir fluxos de dados alternativos? Você pode modificar essa configuração a qualquer momento a partir das configurações de pastas e arquivos. + + + Gerenciar etiquetas + + + Disponível + + + Total + + + Sempre mudar o foco para a aba recém-criada + + + Ativar/desativar o painel de prateleira + + + Alternar a visibilidade do painel da prateleira + + + Exibir painel da prateleira na barra de endereços + + + Prateleira + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + Limpar itens + + + Remover da prateleira + + + Adicionar à prateleira + Tooltip that displays when dragging items to the Shelf Pane + + + Digite um hash para comparar + Placeholder that appears in the compare hash text box + + + Corresponde com {0} + Appears when two compared hashes match, e.g. "Matches SHA256" + + + Nenhuma correspondência + Appears when two compared hashes don't match + + + Caminho ou alias + + + Caminho inválido + + + Testar integração + + + {0} não pôde ser localizado. Por favor, verifique suas configurações e tente novamente. + + + O IDE configurado não pôde ser localizado + + + Abrir configurações + + + Código do Visual Studio + + + Insira um caminho ou inicie o alias + + + Por favor, insira um nome para o IDE + + + Clonar repositório + Clone repo dialog title + + + Clonar + Primary action button in the clone repo dialog + + + URL do repositório + URL textbox header in the clone repo dialog + + + Não foi possível clonar o repositório + Cannot clone repo dialog title + + + Comparar um arquivo + Button that appears in file hash properties that allows the user to compare two files + + + Formato do tamanho + + + Binário + + + Decimal + + + Você pode adicionar seções à barra lateral clicando com o botão direito e selecionando as seções que você deseja adicionar + + + Arraste arquivos ou pastas aqui para interagir com eles nas diferentes abas + + + Insira um caminho para navegar para... + + + Encontrar recursos e comandos... + + + Procurar por arquivos e pastas... + + + Durante operações de arquivo + + + Mostrar botão de status central + + + Toque de progresso do centro de status + Screen reader name for the status center progress ring + + + Arquivos de ícone + This is the friendly name for a variety of different icon files. + + + Mostrar trilhas de navegação recolhidas + + + Mostrar pastas em {0} + + + Mostrar pastas no Início + + + Não há comandos contendo {0} + + + Ver mais + + + Filtrar por + + + Nome do arquivo + + + Assinaturas + + + Lista de assinaturas + + + Emitido por: + + + Emitido para: + + + Válido de: + + + Válido até: + + + Nenhuma assinatura foi encontrada. + + + Mostrar a opção de abrir pastas no Terminal do Windows + + + Abrir arquivo de registro + + + Abrir arquivo de registro no seu editor padrão + + + Abrir o local do arquivo de registro no seu gerenciador de arquivos padrão + + + Não foi possível abrir o arquivo de registro + + + Mostrar barra de status + + + Somente na visualização em colunas + + + Novo {0} + + + Ativar rolagem suave + + + Rolagem + + + Mostrar a opção de Fixar na Barra Lateral + + + Mostrar a opção de Fixar no Menu Iniciar + \ No newline at end of file diff --git a/src/Files.App/Strings/pt-PT/Resources.resw b/src/Files.App/Strings/pt-PT/Resources.resw index d2a9091228e9..0c986cffb287 100644 --- a/src/Files.App/Strings/pt-PT/Resources.resw +++ b/src/Files.App/Strings/pt-PT/Resources.resw @@ -123,11 +123,17 @@ Copiar caminho + + Copiar caminho do item + Copiar caminho com aspas + + Copiar caminho do item selecionado com aspas + - Explorar + Procurar... Tamanho @@ -166,7 +172,7 @@ Inverter seleção - Limpar seleção + Desmarcar tudo Modificado: @@ -214,7 +220,7 @@ Mostrar ficheiros ocultos - Aparência + Aspeto Cor de fundo @@ -229,7 +235,7 @@ Abrir um novo separador - Abrir página(s) específica(s) + Abrir uma ou várias páginas específicas Ambiente de trabalho @@ -249,6 +255,9 @@ Colar + + Colar atalho + Novo @@ -274,10 +283,10 @@ Eliminar - Afixar na barra lateral + Afixar em Acesso rápido - Obrigado por utilizar Files! + Bem-vindo ao Files! Conceder permissão @@ -291,8 +300,8 @@ Introduza o nome do item - - Definir nome + + Criar {0} Claro @@ -300,6 +309,9 @@ Escuro + + Utilizar definições do sistema + Aplicação @@ -316,7 +328,7 @@ Esta pasta está vazia. - Recuar + Voltar Avançar @@ -346,7 +358,7 @@ Não foi possível eliminar este item - Por favor, insira a unidade necessária para aceder a este item. + Insira a unidade necessária para aceder a este item. Unidade desconectada @@ -433,7 +445,7 @@ Enviar para - O nome do item não pode ter os seguintes caracteres: \ / : * ? " < > | + O nome do item não pode ter os seguintes carateres: \ / : * ? " < > | O nome do item especificado é inválido @@ -450,7 +462,7 @@ Sim - Tem a certeza de que pretende eliminar permanentemente todos estes itens? + Tem a certeza de que pretende eliminar de forma permanente todos estes itens? Esvaziar reciclagem @@ -515,9 +527,6 @@ Estado - - Padrão do Windows - Sem resultados @@ -530,6 +539,9 @@ Copiar para {0} + + Clonar para "{0}" + Criar atalho @@ -873,7 +885,7 @@ N.º de palavras - N.º de caracteres + N.º de carateres N.º de linhas @@ -998,6 +1010,9 @@ Resultados da pesquisa {0} em {1} + + Resultados da pesquisa para `{0}` + Detalhes @@ -1052,9 +1067,6 @@ Detalhes (Ctrl+Shift+1) - - Mosaicos (Ctrl+Shift+2) - Data de eliminação @@ -1074,25 +1086,40 @@ Mostrar/ocultar painel de detalhes - Alternar painel de detalhes para ver as propriedades básicas do ficheiro + Mostra/oculta a exibição do painel de detalhes Mostrar/ocultar painel de informações - Alternar painel de informações para ver os painéis detalhes ou de pré-visualização + Mostra/oculta os painéis de detalhe/pré-visualização Mostrar/ocultar barra de ferramentas + + Mostra/oculta a exibição da barra de ferramentas + + + Mostrar/ocultar cabeçalho do filtro + + + Mostra/oculta a visibilidade do cabeçalho do filtro + + + Mostrar/ocultar o painel duplo + + + Mostra/oculta o modo de painel duplo + - Nenhuma pré-visualização disponível + Pré-visualização indisponível Nome do item - Desafixar da barra lateral + Remover de Acesso rápido Rede @@ -1176,7 +1203,7 @@ Mapear unidade de rede - Fixada + Afixada Bibliotecas @@ -1191,7 +1218,7 @@ Argumentos - Padrão + Predefinição do dispositivo N.º de itens @@ -1206,10 +1233,10 @@ Colunas (Ctrl+Shift+6) - Afixar no menu Iniciar + Afixar em Iniciar - Desafixar do menu Iniciar + Remover de Iniciar Biblioteca @@ -1218,7 +1245,7 @@ Localizações: - Definar como localização padrão para guardar + Definir como localização predefinida para guardar Sem localizações @@ -1238,6 +1265,12 @@ Abrir sensor de armanezamento + + Abrir Sensor de armazenamento nas definições Windows + + + Limpeza + Copiar @@ -1380,7 +1413,7 @@ {0} itens - Reabrir separador fechado + Reabrir separador Mudar nome @@ -1452,10 +1485,10 @@ Proprietário desconhecido - Extrair arquivo + Selecionar um destino e Extrair Ficheiros - Abrir pasta de destino ao terminar + Mostrar ficheiros extraídos quando concluir Extrair para {0}\ @@ -1464,10 +1497,10 @@ Criar nova biblioteca - Restaurar bibliotecas originais + Restaurar bibliotecas predefinidas - Tem a certeza de que pretende restaurar as bibliotecas originais? Todos os seus ficheiros vão permanecer no armazenamento. + Tem a certeza de que pretende restaurar as bibliotecas predefinidas? Todos os seus ficheiros vão permanecer no armazenamento. Restaurar bibliotecas @@ -1587,13 +1620,13 @@ Substituir todas as entradas de permissão do objeto filho por entradas de permissão herdáveis deste objeto - Fechar painel ativo + Fechar painel - Ativar modo de sobreposição compacta + Ativar sobreposição compacta - Desativar modo de sobreposição compacta + Desativar sobreposição compacta Descrição do ficheiro @@ -1614,7 +1647,7 @@ Carregar pré-visualização completa - Descarrega o item da nuvem e carrega a pré-visualização + Transfere o item da nuvem e carrega a pré-visualização {0, plural, one {# item} other {# itens}} ({1, plural, one {# ficheiro} other {# ficheiros}}, {2, plural, one {# pasta} other {# pastas}}) @@ -1646,20 +1679,11 @@ Início - - Ctrl+Shift+1 - - - Ctrl+Shift+2 - - - Ctrl+Shift+6 - - A extração do arquivo foi terminada com sucesso. + A extração dos ficheiros foi concluída com sucesso. - A extrair arquivo + A extrair ficheiros Extração terminada! @@ -1680,10 +1704,10 @@ Abrir unidade - Por favor insira um disco na unidade {0} + Insira um disco na unidade {0} - Insira um disco + Inserir disco OK @@ -1712,8 +1736,8 @@ Colunas - - Mosaicos + + Cartões Abrir pastas em novo separador @@ -1749,7 +1773,7 @@ Etiqueta - Abrir itens com um clique + Abrir ficheiros com um clique Caminho da pasta @@ -1812,7 +1836,7 @@ Cores 8-bit (256) - Desativar optimizações de ecrã inteiro + Desativar otimizações de ecrã completo Executar com a resolução 640 x 480 @@ -1826,6 +1850,18 @@ Registar este programa para reiniciar + + Janela inicial + + + Normal + + + Minimizada + + + Maximizada + Executar como administrador @@ -1853,11 +1889,14 @@ Esta opção modifica o registo do sistema e pode ter efeitos inesperados no seu dispositivo. Continue por sua conta e risco. + + Estas operações são permanentes e não recomendamos a sua utilização. Continue por sua conta e risco. + Criar biblioteca - Insira o nome da biblioteca + Introduza o nome da biblioteca Calcular tamanho das pastas @@ -1914,10 +1953,10 @@ Esta operação exige muitos recursos e pode aumentar a utilização do CPU. - Rodar à esquerda + Rodar para a esquerda - Rodar à direita + Rodar para a direita Instalar @@ -1931,6 +1970,9 @@ Fechar outros separadores + + Fechar todos os separadores + Música @@ -1985,11 +2027,23 @@ Comportamentos - - Avaliar Files + + Olá! + + + Está a gostar do Files? Considere deixar uma classificação na Microsoft Store. - - Deseja avaliar Files? + + Está a gostar do Files? Considere apoiar o projeto no GitHub. + + + Patrocinador + + + Clasifique-nos + + + Dispensar Definir como imagem de fundo @@ -1998,7 +2052,7 @@ Definir como fundo do ambiente de trabalho - Utilizar por omissão + Definir como predefinição Coluna Data @@ -2018,8 +2072,8 @@ Ecrã de bloqueio - - Abrir pastas com um clique no esquema Colunas + + Abrir pastas com um clique Abrir itens @@ -2027,6 +2081,21 @@ Comprimir + + Mover todo o conteúdo das sub-pastas para a localização especificada + + + Nivelar pasta + + + Nivelar + + + Nivelar uma pasta moverá todo o conteúdo das subpastas para o local selecionado. Esta operação é permanente e não pode ser revertida. Ao utilizar esta funcionalidade, reconhece o risco e concorda em não responsabilizar a equipa de Files por qualquer perda de dados. + + + Mostrar opções de nivelamento + Selecionar ficheiros e pastas ao passar com o rato por cima @@ -2037,7 +2106,7 @@ Restaurar todos os itens - Deseja restaurar os {0} itens selecionados? + Deseja restaurar {0, plural, one {item selecionado} other {{0} itens selecionados}}? Restaurar seleção @@ -2060,6 +2129,12 @@ Palavra-passe do arquivo + + Codificação + + + {0} (detetado) + Caminho @@ -2102,9 +2177,24 @@ Tamanho de divisão - + Não separar + + Automático + + + Tamanho do dicionário + + + Tamanho da palavra + + + Utilização de memória estimada: {0} + + + Memória disponível: {0} + CD @@ -2159,8 +2249,14 @@ Editar ficheiro de definições - - Novidades + + Abrir ficheiro de definições no editor predefinido + + + Notas de lançamento + + + Abrir notas de lançamento Criar um atalho neste local requer privilégios de administrador @@ -2241,25 +2337,22 @@ Opções do menu de contexto - Mostrar extensão dos ficheiros - - - Mostrar itens ocultos + Extensões de ficheiro - Formatar... + Formatar Ajuda - Ativar/desativar ecrã inteiro + Ecrã completo Tem a certeza de que deseja eliminar esta etiqueta? - Reproduzir tudo + Reproduzir Altura @@ -2310,7 +2403,7 @@ A calcular... - Itens fixados + Itens afixados Diminuir tamanho @@ -2319,7 +2412,7 @@ Aumentar tamanho - Alternar sentido de ordenação + Ordem de ordenação Nome inválido @@ -2331,7 +2424,7 @@ Vídeos - Abrir janela de pré-visualização + Janela de pré-visualização Ativar/desativar modo de sobreposição compacta @@ -2340,7 +2433,7 @@ Abrir página de ajuda no navegador - Ativa/desativa o ecrã inteiro + Ativa/desativa o ecrã completo Ativar modo de sobreposição compacta @@ -2349,49 +2442,64 @@ Desativar modo de sobreposição compacta - Ativar/desativar modo de sobreposição compacta + Ativa/desativa o modo de sobreposição compacta Ir para a caixa de pesquisa - Mostrar/ocultar itens ocultos + Mostra/oculta itens ocultos + + + Mostra/oculta os ficheiros ocultos - Ativar/desativar exibição das extensões dos ficheiros + Mostra/oculta as extensões de ficheiros - Alternar painel de pré-visualização de ficheiros + Mostra/oculta as extensões de ficheiros do painel de pré-visualização - Alternar exibição da barra lateral + Mostra/oculta a barra lateral - Copiar itens para a área de transferência + Copia {0, plural, one {o item selecionado} other {os itens selecionados}} - Copiar caminho dos itens selecionados para a área de transferência + Copia o caminho da pasta atual + + + Copia o caminho dos itens selecionados + + + Copia o caminho dos itens selecionados com aspas - Copiar caminho dos itens selecionados, com aspas, para a área de transferência + Copia o caminho da pasta atual com aspas - Cortar itens para a área de transferência + Corta {0, plural, one {o item selecionado} other {os itens selecionados}} - Colar itens na pasta atual (da área de transferência) + Cola itens na pasta atual + + + Cola itens na pasta atual como atalhos - Colar itens na pasta selecionada (da área de transferência) + Cola itens na pasta selecionada + + + Colar na pasta selecionada - Eliminar itens + Eliminar {0, plural, one {item} other {itens}} Criar nova pasta - Criar atalho(s) para os itens selecionados + Criar {0, plural, one {novo atalho} other {novos atalhos}} para {0, plural, one {o item selecionado} other {os itens selecionados}} Criar atalho para qualquer item @@ -2403,16 +2511,16 @@ Abrir menu "Formatar unidade" para o item selecionado - Restaurar, da reciclagem, os itens selecionados + Restaurar {0, plural, one {item selecionado} other {items selecionados}} da reciclagem Restaurar todos os itens da reciclagem - Abrir itens + Abrir {0, plural, one {item} other {items}} - Abrir itens com a aplicação selecionada + Abrir {0, plural, one {item} other {itens}} com a aplicação selecionada Abrir pasta superior do item pesquisado @@ -2424,31 +2532,31 @@ Mudar nome do item selecionado - Selecionar todos os itens + Selecionar tudo Inverter seleção de itens - Limpar seleção de itens + Limpar itens selecionados - Alternar seleção de itens + Seleciona/desseleciona o item - Partilhar ficheiros selecionados + Partilhar {0, plural, one {ficheiro selecionado} other {ficheiros selecionados}} com outros - Afixar itens no menu Iniciar + Afixar {0, plural, one {item} other {itens}} em Iniciar - Desafixar itens do menu Iniciar + Remover {0, plural, one {item} other {itens}} de Iniciar - Afixar pasta na barra lateral + Afixar {0, plural, one {pasta} other {pastas}} em Acesso rápido - Desafixar pasta da barra lateral + Remover {0, plural, one {pasta} other {pastas}} de Acesso rápido Definir imagem selecionada como fundo do ambiente de trabalho @@ -2462,20 +2570,29 @@ Utilizar imagem selecionada como fundo da aplicação + + Instalar tipo de letra + + + Instalar controlador + + + Instalar certificado + - Instalar tipos de letra selecionados + Instala {0, plural, one {o tipo de letra selecionado} other {os tipos de letra selecionados}} - Instalar controladores através do ficheiro .inf selecionado + Instalar {0, plural, one {controlador} other {controladores}} utilizando {0, plural, one {o ficheiro inf selecionado} other {os ficheiros inf selecionados}} - Instalar certificado(s) selecionado(s) + Instalar {0, plural, one {certificado selecionado} other {certificados selecionados}} - Exceutar aplicação selecionada como administrador + Executar aplicação selecionada como administrador - Exceutar aplicação selecionada como outro utilizador + Executar aplicação selecionada como outro utilizador Executar script PowerShell selecionado @@ -2484,28 +2601,28 @@ Abrir pré-visualização em janela distinta - Criar arquivo com os itens selecionados + Criar ficheiro com {0, plural, one {item selecionado} other {itens selecionados}} - Criar arquivo 7z com os itens selecionados + Criar ficheiro 7z com {0, plural, one {item selecionado} other {itens selecionados}} - Criar arquivo zip com os itens selecionados + Criar ficheiro zip com {0, plural, one {item selecionado} other {itens selecionados}} - Extrair itens do arquivo selecionado para uma pasta + Extrai {0, plural, one {o ficheiro selecionado} other {os ficheiros selecionados}} para qualquer pasta - Extrair itens do arquivo selecionado para a pasta atual + Extrai {0, plural, one {o ficheiro selecionado} other {os ficheiros selecionados}} para a pasta atual - Extrair itens do arquivo selecionado para uma nova pasta + Extrai {0, plural, one {o ficheiro selecionado} other {os ficheiros selecionados}} para uma nova pasta - Rodar imagens selecionadas para a esquerda + Rodar {0, plural, one {imagem selecionada} other {imagens selecionadas}} para a esquerda - Rodar imagens selecionadas para a direita + Rodar {0, plural, one {imagem selecionada} other {imagens selecionadas}} para a direita Abrir página de definições @@ -2525,8 +2642,8 @@ Trocar para a vista de detalhes - - Trocar para a vista de mosaicos + + Trocar para vista em cartões Trocar para vista de lista @@ -2580,7 +2697,7 @@ Ordenar itens por ordem descendente - Alternar direção da ordem dos itens + Alterna a ordem de ordenação dos itens Não agrupar itens @@ -2589,7 +2706,7 @@ Agrupar itens por nome - Agrupar itens por data de alteração + Agrupar itens por data de modificação Agrupar itens por data de criação @@ -2622,16 +2739,16 @@ Ordenar grupos por ordem descendente - Alternar direção da ordem dos grupos + Alterna a ordem de ordenação dos grupos Abrir novo separador - Recuar no histórico de navegação + Navegar para trás - Avançar no histórico de navegação + Navegar para a frente Subir uma pasta @@ -2660,6 +2777,9 @@ Fechar todos os separadores à exceção do selecionado + + Fechar todos os separadores, incluindo o atual + Reabrir último separador fechado @@ -2679,7 +2799,7 @@ Foco no outro painel - Trocar foco para o painel inativo + Trocar foco para o outro painel Mostrar/ocultar barra lateral @@ -2861,18 +2981,18 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Baixar volume + Diminuir volume Key name for hotkeys in menus. Use abbreviation if possible. - Subir volume + Aumentar volume Key name for hotkeys in menus. Use abbreviation if possible. Alterar - Inverter seleção + Selecionar/desselecionar Mostrar aviso ao alterar as extensões de ficheiros @@ -2887,13 +3007,13 @@ Sem etiqueta - Ir para o separador anterior + Separador anterior - Ir para o separador seguinte + Separador seguinte - Fechar separador atual + Fechar separador Editar caminho @@ -2923,7 +3043,7 @@ Mostrar caixas de seleção ao selecionar itens - Focar barra de caminho + Editar caminho no OmniBar Criar novo item @@ -2938,7 +3058,7 @@ Eliminar permanentemente - Eliminar itens permanentemente + Eliminar {0, plural, one {item} other {itens}} permanentemente Reproduzir ficheiros selecionados @@ -2965,7 +3085,7 @@ Dia - Alternar unidade de agrupamento por data + Alterna a unidade de agrupamento por data Alternar unidade de agrupamento @@ -3027,6 +3147,15 @@ Existem alterações por submeter neste 'branch'. O que pretende fazer? + + Existe um combinação em curso com conflitos por resolver. Resolva os conflitos ou cancele a operação para trocar de 'branch'. + + + Cancelar combinação e trocar para '{0}' + + + Ficar em '{0}' e resolver conflitos + Trocar 'branch' @@ -3055,14 +3184,20 @@ Ir para o novo 'branch' - Criar uma pasta com os itens selecionados + Criar pasta com {0, plural, one {o item atual selecionado} other {os itens atuais selecionados}} - Mostrar propriedades + Propriedades + + + Abrir propriedades do Explorador de ficheiros Abrir janela de propriedades + + Abrir janela de propriedades do Explorador de ficheiros + Locais @@ -3081,6 +3216,9 @@ Executar 'git fetch' + + Clonar um repositório git + Executar 'git pull' @@ -3125,7 +3263,7 @@ Mica alternativo - Backdrop Material + Tema de fundo Sólido @@ -3157,17 +3295,17 @@ De momento, Files não se consegue ligar a GitHub. - - Abrir pasta em VS Code + + Abrir pasta em {0} - - Abrir pasta atual no Visual Studio Code + + Abrir pasta atual em {0} - - Abrir repositório em VS Code + + Abrir repositório em {0} - - Abrir repositório Git na aplicação Visual Studio Code + + Abrir 'root' do repositório Git em {0} Copiar código @@ -3194,7 +3332,7 @@ Inicializar repositório - Inicializar um repositório Git + Inicializar pasta atual como repositório Git Nome do repositório remoto @@ -3209,13 +3347,13 @@ Ordenar itens por caminho - Abrir pasta em novo painel + Abrir pasta selecionada em novo painel - Abrir pasta em novo separador + Abrir pasta selecionada em novo separador - Abrir pasta em nova janela + Abrir pasta selecionada em nova janela Abrir tudo @@ -3243,7 +3381,7 @@ Paleta de comandos - Abrir paleta de comandos + Abrir paleta de comandos na OmniBar Iniciar em: @@ -3267,7 +3405,7 @@ A aplicação está a ser executada no modo de administrador - Devido a uma limitação com Windows e WinAppSdk, arrastar e largar não está disponível ao executar a aplicação como administrador. Se quiser usar a funcionalidade "Arrastar e largar", pode contornar a limitação abrindo UAC (Controle de Conta do Utilizador) no menu Iniciar, selecionar o terceiro nível e reiniciando o Windows. Caso contrário, terá que continuar a utilizar a aplicação sem a funcionalidade. + Devido a uma limitação com o Windows e o WinAppSdk, a funcionalidade arrastar e largar não está disponível quando o Files é executado como administrador. Se pretender utilizar o arrastar e largar, pode contornar esta limitação abrindo o UAC (Controlo de Conta do Utilizador) a partir do Menu Iniciar e selecionando o terceiro nível, e reiniciando o Windows. Caso contrário, pode continuar a utilizar o Files sem a funcionalidade arrastar e soltar. Manter aplicação em segundo plano ao fechar a janela @@ -3373,6 +3511,9 @@ A processar itens... + + A procurar itens... + Velocidade: @@ -3385,61 +3526,129 @@ Shown in a StatusCenter card. - {0} itens comprimidos para "{1}" + {0, plural, one {# item comprimido} other {# itens comprimidos}} para "{1}" Shown in a StatusCenter card. - {0} itens comprimidos de "{1}" para "{2}" + {0, plural, one {# item comprimido} other {# itens comprimidos}} de "{1}" para "{2}" Shown in a StatusCenter card. - Erro ao comprimir {0} itens para "{1}" + Erro ao comprimir {0, plural, one {# item} other {# itens}} para "{1}" Shown in a StatusCenter card. - Falha ao comprimir {0} itens de "{1}" para "{2}" + Falha ao comprimir {0, plural, one {# item} other {# itens}} de "{1}" para "{2}" Shown in a StatusCenter card. - A comprimir {0} itens para "{1}" + A comprimir {0, plural, one {# item} other {# itens}} para "{1}" Shown in a StatusCenter card. - A comprimir {0} itens de "{1}" para "{2}" + A comprimir {0, plural, one {# item} other {# itens}} de "{1}" para "{2}" + Shown in a StatusCenter card. + + + Cancelada a clonagem {0} de “{1}” + Shown in a StatusCenter card. + + + Cancelada a clonagem {0} de “{1}” para “{2}” + Shown in a StatusCenter card. + + + Clonado {0} de "{1}" + Shown in a StatusCenter card. + + + Clonado {0} de "{1}" para "{2}" + Shown in a StatusCenter card. + + + Erro ao clonar "{0}" para "{1}" + Shown in a StatusCenter card. + + + Falha ao clonar {0} de "{1}" para "{2}" + Shown in a StatusCenter card. + + + A clonar "{0}" para "{1}" + Shown in a StatusCenter card. + + + A clonar {0} de "{1}" para "{2}" + Shown in a StatusCenter card. + + + Instalação cancelada de {0} tipo(s) de letra + Shown in a StatusCenter card. + + + Instalação cancelada de {0, plural, one {# tipo de letra} other {# tipos de letra}} de "{1}" + Shown in a StatusCenter card. + + + {0, plural, one {# tipo de letra instalado} other {# tipos de letra instalados}} + Shown in a StatusCenter card. + + + {0, plural, one {# tipo de letra instalado} other {# tipos de letra instalados}} de "{1}" + Shown in a StatusCenter card. + + + Erro ao instalar {0, plural, one {# tipo de letra} other {# tipos de letra}} + Shown in a StatusCenter card. + + + Falha ao instalar {0, plural, one {# tipo de letra} other {# tipos de letra}} de "{1}" + Shown in a StatusCenter card. + + + A instalar {0, plural, one {# tipo de letra} other {# tipos de letra}} + Shown in a StatusCenter card. + + + A instalar {0, plural, one {# tipo de letra} other {# tipos de letra}} de "{1}" Shown in a StatusCenter card. - Cancelou a cópia de {0} itens para "{1}" + Cópia cancelada de {0, plural, one {# item} other {# itens}} para "{1}" Shown in a StatusCenter card. - Cancelou a cópia de {0} itens de "{1}" para "{2}" + Cópia cancelada de {0, plural, one {# item} other {# itens}} de "{1}" para "{2}" Shown in a StatusCenter card. - {0} itens copiados para "{1}" + {0, plural, one {# item copiado} other {# itens copiados}} para "{1}" Shown in a StatusCenter card. - {0} itens copiados de "{1}" para "{2}" + {0, plural, one {# item copiado} other {# itens copiados}} de "{1}" para "{2}" Shown in a StatusCenter card. - Erro ao copiar {0} itens para "{1}" + Erro ao copiar {0, plural, one {# item} other {# itens}} para "{1}" Shown in a StatusCenter card. - Falha ao copiar {0} itens de "{1}" para "{2}" + Falha ao copiar {0, plural, one {# item} other {# itens}} de "{1}" para "{2}" Shown in a StatusCenter card. - A copiar {0} itens para "{1}" + A copiar {0, plural, one {# item} other {# itens}} para "{1}" Shown in a StatusCenter card. - A copiar {0} itens de "{1}" para "{2}" + A copiar {0, plural, one {# item} other {# itens}} de "{1}" para "{2}" Shown in a StatusCenter card. + + {0, plural, one {# item encontrado} other {# itens encontrados}} + Shown in a StatusCenter card during file discovery phase. + Cancelou a extração de "{0}" para "{1}" Shown in a StatusCenter card. @@ -3473,59 +3682,59 @@ Shown in a StatusCenter card. - Cancelou a eliminação de {0} itens de "{1}" + Eliminação cancelada de {0, plural, one {# item} other {# itens}} de "{1}" Shown in a StatusCenter card. - Eliminou {0} itens de "{1}" + {0, plural, one {# item eliminado} other {# itens eliminados}} de "{1}" Shown in a StatusCenter card. - Erro ao eliminar {0} itens de "{1}" + Erro ao eliminar {0, plural, one {# item} other {# itens}} de "{1}" Shown in a StatusCenter card. - Falha ao eliminar {0} itens de "{1}" + Falha ao eliminar {0, plural, one {# item} other {# itens}} de "{1}" Shown in a StatusCenter card. - A eliminar {0} itens de "{1}" + A eliminar {0, plural, one {# item} other {# itens}} de "{1}" Shown in a StatusCenter card. - Cancelou a movimentação de {0} itens para "{1}" + Movimentação cancelada de {0, plural, one {# item} other {# itens}} para "{1}" Shown in a StatusCenter card. - Cancelou a movimentação de {0} itens de "{1}" para "{2}" + Movimentação cancelada de {0, plural, one {# item} other {# itens}} de "{1}" para "{2}" Shown in a StatusCenter card. - {0} itens movidos para "{1}" + {0, plural, one {# item movido} other {# itens movidos}} para "{1}" Shown in a StatusCenter card. - {0} itens movidos de "{1}" para "{2}" + {0, plural, one {# item movido} other {# itens movidos}} de "{1}" para "{2}" Shown in a StatusCenter card. - A mover {0} itens para "{1}" + A mover {0, plural, one {# item} other {# itens}} para "{1}" Shown in a StatusCenter card. - A mover {0} itens de "{1}" para "{2}" + A mover {0, plural, one {# item} other {# itens}} de "{1}" para "{2}" Shown in a StatusCenter card. - Erro ao mover {0} itens para "{1}" + Erro ao mover {0, plural, one {# item} other {# itens}} para "{1}" Shown in a StatusCenter card. - Falha ao mover {0} itens de "{1}" para "{2}" + Falha ao mover {0, plural, one {# item} other {# itens}} de "{1}" para "{2}" Shown in a StatusCenter card. - Cancelou a limpeza da Reciclagem + Esvaziamento da reciclagem cancelado Shown in a StatusCenter card. @@ -3533,15 +3742,15 @@ Shown in a StatusCenter card. - A esvaziar Reciclagem + A esvaziar reciclagem Shown in a StatusCenter card. - Erro ao esvaziar a Reciclagem + Erro ao esvaziar a reciclagem Shown in a StatusCenter card. - Falha ao esvaziar a Reciclagem + Falha ao esvaziar a reciclagem Shown in a StatusCenter card. @@ -3549,11 +3758,11 @@ Shown in a StatusCenter card. - {0}/{1} item(s) processados + {0}/{1, plural, one {# item} other {# itens}} processados Shown in a StatusCenter card. Used as "8/20 items processed" - Desbloquear ficheiro descarregado + Desbloquear o ficheiro transferido Não existem operações em curso @@ -3567,6 +3776,9 @@ Falha ao definir como imagem de fundo + + Falha ao abrir o ficheiro de definições + Eliminar Git 'branch' @@ -3592,7 +3804,7 @@ Ocorreu um erro ao aplicar a etiqueta - Extrai os itens dos arquivos selecionados para a pasta atual (se apenas possuir 1 item) ou para uma nova pasta (se possuir vários itens) + Extrai {0, plural, one {o ficheiro selecionado} other {os ficheiros selecionados}} aqui para um item único ou para uma nova pasta para vários itens Extrair aqui (inteligente) @@ -3601,7 +3813,7 @@ Ordenar ficheiros e pastas em conjunto - Ordenar ficheiros e pastas em conjunto + Ordenar ficheiros e pastas na mesma lista Ordenar ficheiros primeiro @@ -3657,9 +3869,6 @@ Questões e discussões - - Tamanhos adicionais ainda não estão disponíveis para o modo Mosaico. - Compacto Used to describe layout sizes @@ -3734,7 +3943,7 @@ Escolha uma ação - Tem a certeza de que pretende repor as combinações de teclas padrão? Esta ação não pode ser anulada. + Tem a certeza de que pretende repor as combinações de teclas predefinidas? Esta ação não pode ser anulada. Esta combinação de teclas já está a ser utilizada. Escolha uma combinação de teclas diferente para continuar. @@ -3746,7 +3955,7 @@ Personalizado - O esquema adaptativo não está disponível quando as preferências são sincronizadas e o esquema padrão foi alterado para Detalhes. + O esquema adaptativo não está disponível quando as preferências são sincronizadas, o esquema predefinido foi alterado para Detalhes. Imagem de fundo @@ -3814,13 +4023,17 @@ Setting where users can choose to display "Open IDE" button for all locations. - Ficheiros DLL + Extensão da aplicação This is the friendly name for DLL files. Ficheiro ICO This is the friendly name for ICO files. + + Ficheiro ICL + This is the friendly name for ICL files. + Ficheiro Zip This is the friendly name for ZIP files. @@ -3829,6 +4042,10 @@ Ficheiros Bitmap This is the friendly name for bitmap files. + + Ficheiros de imagem + This is the friendly name for image files. + Num @@ -3855,22 +4072,22 @@ Mostrar barra de ferramentas - Setting that controls if the Toolbar is shown in the main view + Setting that controls if the toolbar is shown in the main view Menu de ações em separadores - - Adicionar painel vertical + + Dividir verticalmente - Adicionar painel vertical + Divide o painel na vertical - - Adicionar painel horizontal + + Dividir horizontalmente - - Adicionar painel horizontal + + Dividir o painel na horizontal Organizar verticalmente @@ -3884,8 +4101,8 @@ Organizar painéis horizontalmente - - Adicionar painel + + Dividir painel Mostrar botão para ações de separador na barra de título @@ -3893,8 +4110,11 @@ Organizar painéis - - Organização padrão dos painéis + + Direção predefinida da divisão do painel duplo + + + Modo de painel duplo Horizontal @@ -3902,4 +4122,259 @@ Vertical + + Mostrar ícone na bandeja do sistema + + + Processos do CPU + + + Ir para a página inicial + + + Barras de ferramentas + + + ID do utilizador + + + Mudar nome em lote + + + Comprimir conteúdo + + + Mostrar opção para criar fluxo de dados alternativo + + + Criar fluxo de dados alternativo + + + Criar um fluxo de dados alternativo para {0, plural, one {o item selecionado} other {os itens selecionados}} + + + Introduza o nome do fluxo + + + Ocorreu um erro ao criar o fluxo de dados alternativo + + + Tenha em atenção de que os fluxos de dados alternativos apenas funcionam em unidades NTFS. + + + Atualmente, os fluxos de dados alternativos estão ocultos + + + Gostaria de mostrar os fluxos de dados alternativos? Pode alterar esta opção nas definições de ficheiros e pastas. + + + Gerir etiquetas + + + Disponível + + + Total + + + Trocar foco para o separador mais recente + + + Mostrar/ocultar painel da prateleira + + + Mostra/oculta o painel da prateleira + + + Mostrar botão para mostrar/ocultar a prateleira na barra de endereços + + + Prateleira + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + Limpar itens + + + Remover da prateleira + + + Adicionar à prateleira + Tooltip that displays when dragging items to the Shelf Pane + + + Introduza uma hash para comparar + Placeholder that appears in the compare hash text box + + + Corresponde a {0} + Appears when two compared hashes match, e.g. "Matches SHA256" + + + Nenhuma correspondência encontrada + Appears when two compared hashes don't match + + + Caminho ou 'alias' + + + Caminho inválido + + + Testar integração + + + Não foi possível encontrar {0}. Verifique as definições e tente novamente. + + + Não foi possível encontrar o IDE configurado + + + Abrir definições + + + Visual Studio Code + + + Introduza um caminho ou um 'alias' + + + Indique o nome do IDE + + + Clonar repositório + Clone repo dialog title + + + Clonar + Primary action button in the clone repo dialog + + + URL do repositório + URL textbox header in the clone repo dialog + + + Não foi possível clonar o repositório + Cannot clone repo dialog title + + + Comparar um ficheiro + Button that appears in file hash properties that allows the user to compare two files + + + Formato do tamanho + + + Binário + + + Decimal + + + Pode adicionar secções à barra lateral clicando com o botão direito do rato e selecionando as que pretende adicionar + + + Arraste ficheiros e pastas para aqui de modo a conseguir interagir com eles entre os diversos separadores + + + Introduza um caminho para navegar... + + + Localizar funcionalidades e comandos... + + + Pesquisar ficheiros e pastas... + + + Durante as operações de ficheiros + + + Mostrar botão "Centro de estado" + + + Anel de progresso do centro de estado + Screen reader name for the status center progress ring + + + Ficheiros de ícones + This is the friendly name for a variety of different icon files. + + + Mostra os caminhos de navegação recolhidos + + + Mostrar pastas em {0} + + + Mostrar pastas em Início + + + Não existem comandos que contenham {0} + + + Ver mais + + + Filtrar por + + + Nome de ficheiro + + + Assinaturas + + + Lista de assinaturas + + + Emitido por: + + + Emitido para: + + + Válido desde: + + + Válido até: + + + Nenhuma assinatura encontrada. + + + Mostrar opção para abrir pastas no Terminal do Windows + + + Abrir ficheiro de registos + + + Abre o ficheiro de registos no editor predefinido + + + Abre a pasta do ficheiro de registos no gestor de ficheiros predefinido + + + Não foi possível abrir o ficheiro de registos + + + Mostrar barra de estado + + + Apenas na visualização em Colunas + + + Novo {0} + + + Ativar deslocamento suave + + + Deslocamento + + + Mostrar opção para Afixar em Acesso rápido + + + Mostrar opção para Afixar em Iniciar + \ No newline at end of file diff --git a/src/Files.App/Strings/ro-RO/Resources.resw b/src/Files.App/Strings/ro-RO/Resources.resw index eaf065914c62..8df9beedbf39 100644 --- a/src/Files.App/Strings/ro-RO/Resources.resw +++ b/src/Files.App/Strings/ro-RO/Resources.resw @@ -118,19 +118,25 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Fereastră nouă + Fereastră Nouă - Copiază calea + Copiere cale + + + Copiere cale element - Copiază calea cu ghilimele + Copiați calea cu ghilimele + + + Copiază calea elementului selectat cu ghilimele - Răsfoiește + Răsfoire - Mărime + Dimensiune Creat: @@ -139,34 +145,34 @@ Cale - Mărime: + Dimensiune: - Mărime pe disc: + Dimensiune pe disc: - Mărime necomprimat: + Dimensiune necomprimat: Această acțiune nu poate fi efectuată - Dosarul destinație + Folderul destinație - este un sub-dosar al dosarului sursă + este un sub-folder al folderului sursă - Omite + Omitere - Selectează tot + Selectați tot - Inversează selecția + Inversare selecție - Anulează selecția + Anulare selecție Modificat: @@ -175,28 +181,28 @@ Accesat: - Elimină toate elementele + Eliminați toate elementele - Introdu o cale pentru a naviga sau tastează „>” pentru a deschide paleta de comenzi + Introduceți o cale pentru a naviga sau tastați „>” pentru a deschide paleta de comenzi - Caută + Căutare - Fișierele pe care le vei accesa vor apărea aici + Fișierele pe care le veți accesa vor apărea aici - Elimină acest element + Eliminați acest element - Repository GitHub + Repozitoriu GitHub Despre - Open source + Sursă deschisă Format dată @@ -205,16 +211,16 @@ Temă - Afișează extensiile pentru tipurile de fișiere cunoscute + Afișare extensii pentru tipurile de fișiere cunoscute - Afișează fișierele și dosarele ascunse + Afișare fișiere și foldere ascunse - Afișează fișierele care încep cu punct + Afișare fișiere punct - Aspect + Aspect vizual Culoare de fundal @@ -223,13 +229,13 @@ Avansat - Continuă de unde ai rămas + Continuați de unde ați rămas - Deschide o filă nouă + Deschideți o filă nouă - Deschide o anumită pagină sau pagini + Deschideți o anumită pagină sau pagini Desktop @@ -247,7 +253,10 @@ Ascendent - Lipește + Lipire + + + Lipire comandă rapidă Nou @@ -256,43 +265,43 @@ Proprietăți - Deschide + Deschidere - Deschide într-o filă nouă + Deschideți în filă nouă - Deschide într-o fereastră nouă + Deschideți într-o fereastră nouă - Partajează + Partajare - Decupează + Decupare - Șterge + Ștergere - Fixează la bara laterală + Fixați la Bara Laterală Bun venit la Files! - Acordă permisiunea + Acordați permisiunea - Pentru a începe, va trebui să ne acorzi permisiunea de a-ți afișa fișierele. Acest lucru va deschide o pagină de setări de unde ne poți acorda permisiunea. Va trebui să repornești aplicația după ce ai acordat permisiunea. + Pentru a începe, va trebui să ne acordați permisiunea de a vă afișa fișierele. Acest lucru va deschide o pagină de setări de unde ne puteți acorda permisiunea. Va trebui să reporniți aplicația după ce ați acordat permisiunea. Afișare - Introdu un nume de element + Introduceți un nume - - Setează numele + + Creare {0} nou Luminoasă @@ -300,6 +309,9 @@ Întunecată + + Utilizați setările de sistem + Aplicație @@ -307,13 +319,13 @@ Sistem - Dosar nou + Folder nou Fișier nou - Acest dosar este gol. + Acest folder este gol. Înapoi @@ -328,52 +340,52 @@ Reîmprospătare - Un element cu acest nume există deja în acest dosar. + Un element cu acest nume exista deja în acest folder. - Înlocuiește elementul existent + Înlocuiți elementul existent Elementul există deja - Setează ca ecran de blocare + Stabilire ca ecran de blocare - Setează ca fundal al aplicației + Stabilire ca fundal al aplicației Nu am putut șterge acest element - Te rog introdu unitatea necesară pentru a accesa acest element. + Vă rugăm introduceți unitatea necesară pentru a accesa acest element. Unitate deconectată - Fișierul pe care încerci să-l accesezi a fost mutat sau șters. + Fișierul pe care încercați să-l accesați a fost mutat sau șters. Nu se pot deschide proprietățile pentru acest fișier - Fișierul pe care încerci să-l accesezi a fost mutat sau șters. + Fișierul pe care încercați să-l accesați a fost mutat sau șters. Fișierul nu a fost găsit - Dosarul pe care încerci să-l accesezi a fost mutat sau șters. + Fișierul pe care încercați să-l accesați a fost mutat sau șters. - Ai șters acest dosar? + Ați șters acest folder? - Fișierul pe care încerci să-l accesezi este utilizat de {0} + Fișierul pe care încercați să îl accesați este utilizat în prezent de {0} - Fișierul pe care încerci să-l accesezi este utilizat de o altă aplicație + Fișierul pe care încercați să îl accesați este utilizat în prezent de o altă aplicație Fișierul este în uz @@ -382,43 +394,43 @@ Aspect - Doar citire + Doar în citire - {0, plural, one {acum # zi} few {acum # zile} other {acum # de zile}} + {0, plural, one {o zi în urmă} few {# zile în urmă} other {# de zile în urmă}} - acum o zi + O zi în urmă - acum {0} zile + {0} zile în urmă - {0, plural, one {acum # oră} few {acum # ore} other {acum # de ore}} + {0, plural, one {O oră în urmă} few {# ore în urmă} other {# de ore în urmă}} - acum o oră + O oră în urmă - acum {0} ore + {0} ore în urmă - {0, plural, one {acum # minut} few {acum # minute} other {acum # de minute}} + {0, plural, one {Un minut în urmă} few {# minute în urmă} other {# de minute în urmă}} - acum un minut + 1 minut în urmă - acum {0} minute + {0} minute în urmă - {0, plural, one {acum # secundă} few {acum # secunde} other {acum # de secunde}} + {0, plural, one {O secundă în urmă} few {# secunde în urmă} other {# de secunde în urmă}} - acum o secundă + 1 secundă în urmă - acum {0} secunde + {0} secunde în urmă Acum @@ -427,69 +439,69 @@ Operația solicitată nu este suportată - {0} liberi din {1} + {0} liber din {1} - Trimite la + Trimitere la - Numele elementului nu trebuie să conțină următoarele caractere: \ / : * ? " < > | + Numele elementului nu trebuie să conțină următoarele caractere: \ / : * ? „ < > | Numele elementului specificat nu este valid - {0, plural, one {# articol selectat} few {# articole selectate} other {# de articole selectate}} + {0, plural, one {# element selectat} few {# elemente selectate} other {# de elemente selectate}} ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural - {0, plural, one {articol} few {articole} other {de articole}} + {0, plural, one {element} few {elemente} other {de elemente}} ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural Da - Ești sigur că dorești să ștergi definitiv toate aceste elemente? + Sunteți sigur că vreți să ștergeți definitiv toate aceste elemente? - Golește coșul de reciclare + Golire Coș de Reciclare octeți - KO + KB - MO + MB - GO + GB - TO + TB - PO + PB - O + B - {0, number} {0, plural, one {fișier} few {fișiere} other {de fișiere}}, {1, number} {1, plural, one {dosar} few {dosare} other {de dosare}} + {0, number} {0, plural, one {fișier} few {fișiere} other {de fișiere}}, {1, number} {1, plural, one {folder} few {foldere} other {de foldere}} - {0, number} {0, plural, one {fișier} few {fișiere} other {de fișiere}}, {1, number} {1, plural, one {dosar} few {dosare} other {de dosare}} din {2, number} {2, plural, one {locație} few {locații} other {de locații}} + {0, number} {0, plural, one {fișier} few {fișiere} other {de fișiere}}, {1, number} {1, plural, one {folder} few {foldere} other {de foldere}} din {2, number} {2, plural, one {locație} few {locații} other {de locații}} - Rulează ca alt utilizator + Executare ca alt utilizator Toate tipurile de {0} - Tipuri diferite + Diferite tipuri Toate în {0} @@ -510,14 +522,11 @@ Recente - Mută fila aici + Mutați fila aici Stare - - Implicit sistem - Niciun rezultat @@ -525,16 +534,19 @@ Nu se poate accesa niciun element de afișat - Mută în {0} + Mutare în {0} - Copiază în {0} + Copiere în {0} + + + Clonare la {0} - Creează o scurtătură + Creați o comandă rapidă - Deschide locație fișier + Deschidere locație fișier Argumente: @@ -543,22 +555,22 @@ Destinație: - Tip scurtătură: + Tip comandă rapidă: - Legătură web + Link web General - Scurtătură + Comandă rapidă - Scurtătură internet + Comandă rapidă internet - {0} - scurtătură + {0} - comandă rapidă Files a întâmpinat o problemă pentru care dezvoltatorii nu s-au pregătit încă. @@ -582,7 +594,7 @@ Nu este nimic de partajat acum... - Fișierul pe care l-ați selectat va fi partajat + Elementele pe care le-ați selectat vor fi partajate Elementul selectat va fi partajat @@ -594,7 +606,7 @@ Se partajează {0} {1} - Fișierul pe care încercați să îl redenumiți nu mai există. Verificați locația corectă a fișierului pe care trebuie să-l redenumiți. + Elementul pe care încercați să îl redenumiți nu mai există. Verificați locația corectă a elementului pe care trebuie să-l redenumiți. Elementul nu mai există @@ -606,49 +618,49 @@ Nume element invalid - Lungimea numelui fișierului specificat depășește limita maximă. Vă rugăm să verificați numele dorit și încercați din nou. + Lungimea numelui elementului specificat depășește limita maximă. Vă rugăm să verificați numele dorit și încercați din nou. - Numele fișierului era prea lung + Numele elementului era prea lung - Afișează mai multe opțiuni + Afișați mai multe opțiuni Aplicația trebuie să repornească pentru a aplica aceste setări, doriți să reporniți aplicația? - Sponsorizaţi-ne pe GitHub + Sponsorizați-ne pe GitHub - Nu am putut crea acest element + Nu s-a putut crea acest element Acces Respins - Setaţi ca + Stabilire ca Anulare - Creați un element nou + Creare element nou - Alege un tip pentru acest fișier nou mai jos + Alegeți un tip pentru acest element nou de mai jos Fișier - Creați un fișier gol + Creare fișier gol Folder - Creați un folder gol + Creează un folder gol Reîmprospătați directorul @@ -660,7 +672,7 @@ Mutați extensiile shell într-un sub-meniu - Arată dialogul de confirmare înainte de a șterge + Afișare dialog de confirmare înainte de a șterge A apărut o eroare la salvarea unor proprietăți. @@ -669,7 +681,7 @@ Eroare - Închide oricum + Închidere oricum Evaluare @@ -678,7 +690,7 @@ Cale element - Tip element + Tip Element Titlu @@ -690,22 +702,22 @@ Comentariu - Drepturi de autor + Drepturi de Autor - Data modificării + Data Modificării - Adâncimea biților + Adâncime de Biți Dimensiuni - Mărime orizontală + Dimensiune Orizontală - Dimensiune verticală + Dimensiune Verticală Rezoluție orizontală @@ -714,7 +726,7 @@ Rezoluție verticală - Spaţiu de culoare + Spațiu de culoare sRGB @@ -723,55 +735,55 @@ Nespecificat - Decimal Longitudine + Longitudine Zecimală - Decimal latitudine + Latitudine Zecimală Altitudine - Data preluării + Data Preluării - Fabricantul camerei foto + Producător Cameră - Model cameră + Model Cameră - Timp de expunere + Timp de Expunere - Lungime focală + Distanță Focală Apertură - Nume persoane + Nume Persoane - Numărul de canale + Număr de Canale Format - Rată de eșantionare + Rată de Eșantionare - Albumul Artistului + Artist Album - Titlu album + Titlu Album Artist - Bătăi pe minut + Bătăi Pe Minut Compozitor @@ -780,67 +792,67 @@ Dirijor - Numărul discului + Numărul Discului Gen - Număr pistă + Număr Piesă Durată - Numărul de cadre + Număr Cadre - Tip de protecție + Tip Protecție - URL-ul autorului + URL Autor - Distribuitor de conținut + Distribuitor Conținut - Data lansării + Data Lansării - Numele seriilor + Numele Seriei - Numărul de sezon + Numărul Sezonului - Numărul episodului + Numărul Episodului Producător - URL promoțional + URL Promoție - Stilul furnizorului + Stilul Furnizorului Editor - Calea Miniaturilor Mari + Cale Miniatură Mare - Calea Miniaturilor Uri + URI Miniatură Mare - Calea Miniaturilor Mici + Cale Miniatură Mică - Calea Miniaturilor Uri + URI Miniatură Mică - URL web al utilizator + URL Web Utilizator Autor @@ -852,61 +864,61 @@ Contribuitor - Ultimul autor + Ultimul Autor - Numărul reviziei + Număr Revizuire Versiune - Data creării + Data Creării - Timpul total de editare + Timp Total de Editare Șablon - Număr de cuvinte + Contor de Cuvinte - Numărul de caractere + Contor Caractere - Numărul de linii + Contor Linii - Număr paragrafe + Contor Paragrafe - Numărul de pagini + Număr Pagini - Număr diapozitive + Contor Diapozitive - Cadre pe secundă + Frecvență Cadre - Codare Bitrate + Codificare Rată de Biți - Rată de Biți de Codificare Audio + Codificare Rată de Biți Audio - Rată de Biți de Codificare Video + Codificare Rată de Biți Video Compresie - Lățimea Cadrului + Lățime Cadru - Înălțimea Cadrului + Înălțime Cadru Orientare @@ -948,10 +960,10 @@ Selectare - Articol coș de reciclare + Element coș de reciclare - Articol comandă rapidă + Element comandă rapidă Unități @@ -966,7 +978,7 @@ Dispozitivul poate fi acum scos în siguranță din calculator. - Problemă la scoaterea dispozitivului + Problemă la Scoaterea Dispozitivului Acest dispozitiv este în prezent în uz. Închideți orice programe, ferestre sau file care ar putea utiliza dispozitivul și apoi încercați din nou. @@ -978,7 +990,7 @@ Duplicare filă - Mută fila într-o fereastră nouă + Mutare fila într-o fereastră nouă Filă nouă @@ -998,6 +1010,9 @@ Rezultatele căutării în {1} pentru {0} + + Rezultatele căutării pentru `{0}` + Detalii @@ -1005,7 +1020,7 @@ Nu - Arată fișierele protejate de sistem + Afișați fișierele protejate de sistem Sincronizați aspectul și preferințele de sortare între directoare @@ -1026,16 +1041,16 @@ Eliminare - Deschide filele în modul panou dublu + Deschidere file în modul panou dublu - Arată opțiunea pentru a deschide foldere într-un panou nou + Afișați opțiunea pentru a deschide folderele într-un panou nou Afișați opțiunea pentru a copia calea - Afișați opțiunea pentru a crea un folder cu selecție + Afișați opțiunea pentru a crea un folder cu selecția Afișați opțiunea pentru a crea o comandă rapidă @@ -1044,52 +1059,64 @@ Panou nou - Deschideți într-un panou nou + Deschidere într-un panou nou - Fișiere & foldere + Fișiere și foldere Detalii (Ctrl+Shift+1) - - Dale (Ctrl+Shift+2) - Data ștergerii - Unități în cloud + Unități în Cloud - Confirmați + Confirmare Nume dorit - Afișați sau ascundeți panoul de previzualizare + Comutare panou de previzualizare - Afișați sau ascundeți panoul de detalii + Comutare panou de detalii - Comută panoul de detalii pentru a vizualiza proprietățile de bază ale fișierelor + Comutați panoul de detalii pentru a vizualiza proprietățile de bază ale fișierelor - Comută panoul de informații + Comutare panou de informații - Comutați panoul de informații pentru a vizualiza panourile de detalii/previzualizare + Comutare vizibilitate panourilor de detalii și de previzualizare Comutare Bară de Instrumente + + Comutați vizibilitatea barei de instrumente + + + Comutare antet filtru + + + Comutați vizibilitatea antentului de filtru + + + Comutare panou dublu + + + Comutare mod panou dublu + Nicio previzualizare disponibilă - Nume Obiect + Nume Element Anulați fixarea la Bara Laterală @@ -1113,19 +1140,19 @@ Disponibil când sunteți online - Documentaţie + Documentație - Disponibil Offline + Disponibil când sunteți offline - Parțial disponibile offline + Disponibil parțial offline Se sincronizează - Exclus din sincronizare + Exclus de la sincronizare Necalculat @@ -1137,34 +1164,34 @@ Dacă modificați extensia unui fișier, acesta poate deveni inutilizabil. Sigur modificați extensia? - Drive CD ROM + Unitate CD-ROM - Unitate în cloud + Unitate în Cloud - Unitate de disc fixă + Unitate de Disc Fixă - Unitate de disc floppy + Unitate de Disc Floppy - Unitate de rețea + Unitate de Rețea - Unitate nemontată + Unitate Nemontată - Unitate RAM disc + Unitate Disc RAM - Dispozitiv de stocare amovil + Dispozitiv de Stocare Amovibil Necunoscut - Unitate virtuală + Unitate Virtuală Data creării @@ -1194,22 +1221,22 @@ Implicit - Număr de obiecte + Număr de elemente - Această acţiune necesită drepturi de administrator + Această acțiune necesită drepturi de administrator - Doriţi să continuaţi ca administrator? + Doriți să continuați ca administrator? Coloane (Ctrl + Shift+6) - Fixare la Start + Fixare la Meniul Start - Anulați fixarea la Start + Anulați fixarea la Meniul Start Bibliotecă @@ -1218,7 +1245,7 @@ Locații: - Setează ca cale implicită de salvare + Setare ca cale implicită de salvare Nicio locație @@ -1236,7 +1263,13 @@ O bibliotecă cu același nume exista deja! - Deschideți Stocare inteligentă + Deschidere Stocare Inteligentă + + + Deschideți pagina Stocare Inteligentă în Setările Windows + + + Curățare Copiere @@ -1251,43 +1284,43 @@ Mutare la - {0, plural, one {Un element va fii mutat} other {# elemente vor fii mutate}} + {0, plural, one {Un element va fi mutat} few {# elemente vor fi mutate} other {# de elemente vor fi mutate}} - Se mută {0, plural, one {element} other {elemente}} + Se mută {0, plural, one {element} few {elemente} other {elemente}} - {0, plural, one {Un elemente va fii șters} other {# elemente vor fii șterse}} + {0, plural, one {Un element va fi șters} few {# elemente vor fi șterse} other {# de elemente vor fi șterse}} - Continuați + Continuare - {0, plural, one {Există un nume de fișier conflictual} other {Există # nume de fișier conflictuale}} + {0, plural, one {Există un nume de fișier conflictual} few {Există # nume de fișiere conflictuale} other {Există # de nume de fișiere conflictuale}} - {0, plural, one {Există un nume de fișier conflictual} other {Există # nume de fișier conflictuale}}, și {1, plural, one {un element în curs} other {# elemente în curs}} + {0, plural, one {Există un nume de fișier conflictual} few {Există # nume de fișier conflictuale} other {Există # de nume de fișier conflictuale}}, and {1, plural, one {un element în curs} few {# elemente în curs} other {# de elemente în curs}} - {0, plural, one {nume de fișier} other {nume de fișiere}} în conflict + {0, plural, one {Nume de fișier} few {Nume de fișiere} other {Nume de fișiere}} în conflict - {0, plural, one {Un element va fii copiat} other {# elemente vor fii copiate}} + {0, plural, one {Un element va fi copiat} few {# elemente vor fi copiate} other {# de elemente vor fi copiate}} - Șterge definitiv + Ștergere definitivă Viteză ISO - Înlocuiește existent + Înlocuire existente - Generează un nume nou + Generare nume nou - Creare folder cu selecţie + Creare folder cu selecție Tip: @@ -1299,7 +1332,7 @@ Deschidere cu - Pictogramă fișier + Fișier pictogramă Coloana de cale originală @@ -1308,10 +1341,10 @@ Coloană tip element - Coloană data modificări + Coloană data modificării - Coloana dată ștergerii + Coloană data ștergerii Sortare @@ -1329,7 +1362,7 @@ Imens - Foarte mare + Foarte Mare Mare @@ -1371,7 +1404,7 @@ Anul trecut - An {0} + Anul {0} {0} element @@ -1380,7 +1413,7 @@ {0} elemente - Redeschide filele închise + Redeschidere filă închisă Redenumire @@ -1389,7 +1422,7 @@ Confidențialitate - Coșul de reciclare + Coșul de Reciclare Se anulează @@ -1413,13 +1446,13 @@ Permitere - Refuzaţi + Refuzare Control total - Listează conținutul directorului + Listați conținutul directorului Modificare @@ -1428,7 +1461,7 @@ Permisiuni pentru {0} - Citire și execuție + Citire și executare Citire @@ -1452,10 +1485,10 @@ Proprietar necunoscut - Extrage Arhiva + Extragere arhivă - Deschideți folderul de destinație când se finalizează + Deschideți folderul de destinație la finalizare Extragere în {0}\ @@ -1470,7 +1503,7 @@ Sigur doriți să restaurați bibliotecile implicite? Toate fișierele dvs. vor rămâne în spațiul de stocare. - Restabilire biblioteci + Restabilire Biblioteci Proprietar @@ -1518,7 +1551,7 @@ Doar subfoldere - Doar subfoldere şi fişiere + Doar subfoldere și fișiere Doar acest folder @@ -1542,10 +1575,10 @@ Ștergeți subdirectoarele și fișierele - Execută fişierele + Executare fișiere - Citește atributele + Atribute de citire Citire date @@ -1554,22 +1587,22 @@ Citire atribute extinse - Citire permisiuni + Permisiuni citire - Preluați permisiune + Preluați proprietatea Vizitați folderul - Scriere atribute + Atribute de scriere Scriere date - Scriere atribute extinse + Atribute de scriere extinse Convertiți permisiunile moștenite în permisiuni explicite @@ -1587,7 +1620,7 @@ Înlocuiți toate intrările de permisiuni ale obiectului copil cu intrări de permisiuni moștenite de la acest obiect - Închideți panoul activ + Închidere panou Intrare în suprapunere compactă @@ -1596,7 +1629,7 @@ Ieșire din suprapunere compactă - Descriere fişier + Descriere fișier Companie @@ -1605,10 +1638,10 @@ Limbă - Mărci înregistrate + Mărci Înregistrate - Versiune fişier + Versiune fișier Încărcați previzualizarea completă @@ -1617,19 +1650,19 @@ Descarcă elementul din cloud si încarcă previzualizarea - {0, plural, one {# element} other {# elemente}} ({1, plural, one {# fișier} other {# fișiere}}, {2, plural, one {# folder} other {# foldere}}) + {0, plural, one {# element} few {# elemente} other {# de elemente}} ({1, plural, one {# fișier} few {# fișiere} other {# de fișiere}}, {2, plural, one {# folder} few {# foldere} other {# de foldere}}) - Mărime necomprimată + Dimensiune necomprimată - SLW + WSL Ascundeți secțiunea {0} - Adaugă fișier + Adăugare fișier Sunt disponibile actualizări @@ -1646,15 +1679,6 @@ Pornire - - Ctrl+Shift+1 - - - Ctrl+Shift+2 - - - Ctrl+Shift+6 - Extragerea arhivei s-a finalizat cu succes. @@ -1704,25 +1728,25 @@ Niciun element găsit - Deschide locația jurnalului + Deschidere locație jurnal - Creează legătură în {0} + Creare link în {0} Coloane - - Dale + + Carduri - Deschide foldere în filă nouă + Deschidere foldere în filă nouă Centru de stare - Coloană dată de creare + Coloană data creării Coloană cu dimensiunea elementului @@ -1731,10 +1755,10 @@ Caracteristici experimentale - Asistenţă şi suport + Ajutor și asistență - Trimiteți o cerere de funcție + Trimiteți o cerere pentru o funcție nouă Trimiteți un raport despre o problemă @@ -1749,7 +1773,7 @@ Etichetă - Deschide elementele cu un singur clic + Deschidere fișiere cu un singur clic Cale folder @@ -1770,7 +1794,7 @@ Golire Coș de Reciclare - Bară laterală + Bară Laterală Alegeți o pictogramă personalizată pentru folder @@ -1782,7 +1806,7 @@ Restabiliți valorile implicite - Deschide fila în instanța existentă la deschiderea Files dintr-o altă aplicație + Deschideți fila în instanța existentă la deschiderea Files dintr-o altă aplicație Setări pornire @@ -1794,7 +1818,7 @@ Niciuna - Când mă conectez la Windows + La pornirea Windows La pornirea programului @@ -1812,28 +1836,40 @@ Culoare 8-bit (256) - Dezactivați optimizările pe ecran complet + Dezactivare optimizări pe ecran complet - Rulați la rezoluția ecranului de 640 x 480 + Executare la rezoluția ecranului de 640 x 480 - Suprascrieți comportamentul de scalare DPI mare + Suprascriere comportament de scalare DPI mare Mod de culoare redus - Înregistrați acest program pentru repornire + Înregistrare program pentru repornire + + + Fereastră de pornire + + + Fereastră normală + + + Minimizat + + + Maximizat - Rulare ca administrator + Executare ca administrator - Rulați depanatorul pentru compatibilitate + Executare depanator pentru compatibilitate - Utilizați setările DPI ale monitorului principal + Utilizare setări DPI ale monitorului principal Nu ajustați setările DPI @@ -1853,17 +1889,20 @@ Această opțiune modifică registrul sistemului și poate avea efecte secundare neașteptate pe dispozitiv. Continuați pe propriul risc. + + Operațiunile de aplatizare sunt permanente și nu sunt recomandate. Continuați pe propriul risc. + - Creați o bibliotecă + Creare Bibliotecă Introduceți numele Bibliotecii - Calculează mărimea folderelor + Calculați dimensiunea folderelor - Fişiere recente + Fișiere recente Deschideți Files la pornirea Windows @@ -1893,10 +1932,10 @@ Extragere - Extragere fişiere + Extragere fișiere - Extrageți aici + Extragere aici Ctrl+E @@ -1905,13 +1944,13 @@ Extragere în {0} - Execută scriptul + Executare script - Execută cu PowerShell + Executare cu PowerShell - Calcularea dimensiunilor folderelor consumă multe resurse şi poate creşte utilizarea procesorului. + Calcularea dimensiunilor folderelor consumă multe resurse și poate crește utilizarea procesorului. Rotire la stânga @@ -1923,13 +1962,16 @@ Instalare - Închide filele din stânga + Închideți filele din stânga - Închide filele din dreapta + Închideți filele din dreapta - Închideţi celelalte file + Închideți celelalte file + + + Închideți toate filele Muzică @@ -1947,7 +1989,7 @@ Universal - ex: {0}, {1} + de ex: {0}, {1} Afișați miniaturi @@ -1965,10 +2007,10 @@ Personalizat - Setează ca diapozitiv pe desktop + Setare ca expunere de diapozitive pe desktop - Poziționează toate coloanele să încapă + Poziționați toate coloanele să încapă Aspect adaptabil @@ -1980,25 +2022,37 @@ Ctrl+Shift+7 - Arată fluxuri alternative de date + Afișare fluxuri alternative de date Comportamente - - Lăsați o recenzie pentru Files + + Salut! + + + Vă place Files? Vă rugăm să luați în considerare lăsarea unei recenzii în Microsoft Store. + + + Vă place Files? Vă rugăm să luați în considerare susținerea proiectului pe GitHub. - - Doriți să revizuiți Files? + + Sponsorizare + + + Acordați-ne o recenzie + + + Ignorare - Setați ca fundal + Setare ca fundal Stabilire ca imagine de fundal pentru desktop - Setați ca implicit + Setare ca implicit Coloană dată @@ -2018,8 +2072,8 @@ Ecran de blocare - - Deschide folderele cu un singur clic în aspectul coloană + + Deschidere foldere cu un singur clic Deschidere elemente @@ -2027,6 +2081,21 @@ Comprimare + + Mută tot conținutul din subfoldere în locația selectată + + + Aplatizare folder + + + Aplatizare + + + Aplatizarea unui folder mută tot conținutul din subfolderele sale în locația selectată. Această operațiune este permanentă și nu poate fii anulată. Prin utilizarea acestei caracteristici experimentale, vă asumați riscul și sunteți de acord să nu trageți la răspundere echipa Files pentru orice pierdere de date. + + + Afișare opțiuni de aplatizare + Selectați fișiere și foldere când treceți cu mouse-ul peste ele @@ -2037,7 +2106,7 @@ Restaurați toate elementele - Doriți să restaurați cele {0} elemente selectate? + Doriți să restaurați {0, plural, one {un element selectat} few {{0} elemente selectate} other {{0} de elemente selectate}}? Restaurare selecție @@ -2060,6 +2129,12 @@ Parolă arhivă + + Codificare + + + {0} (detectat) + Cale @@ -2091,19 +2166,34 @@ Normal - Scăzută + Scăzut - Rapidă + Rapid - Niciuna + Fără - Dimensiune împărțită + Dimensiune împărțire + + + Nu despărțiți + + + Automat - - Nu despărți + + Dimensiune dicționar + + + Dimensiune cuvânt + + + Utilizare estimată a memoriei: {0} + + + Memorie disponibilă: {0} CD @@ -2127,10 +2217,10 @@ Format - Sincronizați coloana de status + Sincronizați coloana de stare - Grupaţi după + Grupare după Sortare după @@ -2145,22 +2235,28 @@ Creați o comandă rapidă nouă - Creați comenzi rapide la programele locale sau de rețea, fișiere, dosare, computere sau adrese de internet. + Creați comenzi rapide la programele locale sau de rețea, fișiere, foldere, computere sau adrese de internet. Introduceți locația elementului: - Creați o comandă rapidă + Creează o comandă rapidă - Fișierele utilizate recent sunt dezactivate în Windows File Explorer. + Fișierele utilizate recent sunt dezactivate în Explorer. Editați fișierul de setări - - Ce e nou + + Deschideți fișierul de setări în editorul dvs. implicit + + + Note de Lansare + + + Deschideți Notele de Lansare Crearea unei comenzi rapide în această locație necesită privilegii de administrator @@ -2181,19 +2277,19 @@ Vedeți mai mult - Afișează editarea etichetelor + Afișați secțiunea de editare a etichetelor - Afișați opțiunile de compresie + Afișați opțiunile de comprimare - Afișați trimitere la meniu + Afișați meniul trimitere la - Arată opțiunea pentru a deschide foldere într-o filă nouă + Afișați opțiunea pentru a deschide foldere într-o filă nouă - Arată opțiunea pentru a deschide foldere într-o fereastră nouă + Afișați opțiunea pentru a deschide foldere într-o fereastră nouă Acces rapid @@ -2205,10 +2301,10 @@ Introduceți acreditările rețelei - Memorează-mi acreditările + Memorați-mi acreditările - Eroare la dosarul rețelei + Eroare la folderul rețelei Versiune aplicație @@ -2220,7 +2316,7 @@ Întotdeauna - Doar ștergere permanentă + Doar la ștergere permanentă Niciodată @@ -2232,7 +2328,7 @@ Etichetă nouă - Creați o etichetă nouă + Creare etichetă nouă Se încarcă... @@ -2241,34 +2337,31 @@ Opțiuni meniu contextual - Afișare extensii nume de fișier - - - Afișați elementele ascunse + Extensii nume de fișier - Formatare... + Formatare Ajutor - Comutare la ecran complet + Ecran complet - Ești sigur că vrei să ștergi această etichetă? + Sunteți sigur că vreți să ștergeți această etichetă? - Redați tot + Redare - Înălţime + Înălțime Lățime - Aplică această acțiune tuturor elementelor contradictorii + Aplicați această acțiune tuturor elementelor contradictorii Deschideți în Terminal Windows @@ -2289,10 +2382,10 @@ Hash-uri - A apărut o eroare in timpul calculului + A apărut o eroare in timpul calculări - Nu s-a putut calcula hash-ul, te rugăm să închizi fișierul și să încerci din nou. + Nu s-a putut calcula suma de control, vă rugăm să închideți fișierul și să încercați din nou. Hash-urile nu sunt disponibile pentru fișierele online @@ -2313,13 +2406,13 @@ Elemente fixate - Reduceți dimensiunea + Reducere dimensiune - Măriți dimensiunea + Mărire dimensiune - Comutare direcție sortare + Direcție sortare Nume invalid @@ -2331,7 +2424,7 @@ Videoclipuri - Lansați fereastra de previzualizare + Previzualizare pop-up Comutare suprapunere compactă @@ -2352,46 +2445,61 @@ Comutare suprapunere compactă - Mergeți la caseta de căutare + Începeți căutarea în OmniBar - Comută dacă se afișează elementele ascunse + Comutați vizibilitatea elementelor ascunse + + + Comutați vizibilitatea fișierelor punct - Comută dacă se afișează extensiile fișierelor + Comutați vizibilitatea fișierelor ascunse - Comută panoul de previzualizare pentru a vizualiza previzualizări ale fișierelor + Comută panoul de previzualizare pentru a vizualiza previzualizarea fișierelor - Comută dacă se afișează bara laterală + Comutați dacă se afișează bara laterală - Copiază elementul/le în clipboard + Copiați {0, plural, one {elementul selectat} few {elementele selectate} other {elementele selectate}} - Copiați calea elementelor selectate în clipboard + Copiați calea directorului curent + + + Copiați calea elementelor selectate + + + Copiați calea elementelor selectate cu ghilimele - Copiați calea elementelor selectate cu ghilimele în clipboard + Copiați calea directorului curent cu ghilimele - Decupează elementul/le în clipboard + Decupați {0, plural, one {elementul selectat} few {elementele selectate} other {elementele selectate}} - Lipiți element(ele) din clipboard în folderul curent + Lipire elemente la folderul curent + + + Lipire elemente la folderul curent ca comenzi rapide - Lipiți element(ele) din clipboard în folderul selectat + Lipire elemente la folderul selectat + + + Lipire în folderul selectat - Ștergere element(e) + Ștergere {0, plural, one {element selectat} few {elemente selectate} other {elemente selectate}} Crează un folder nou - Creaţi comenzi rapide noi la articolul/ele selectat/e + Creați {0, plural, one {o comandă rapidă nouă} other {comenzi rapide noi}} pentru {0, plural, one {elementul selectat} other {elementele selectate}} Creați o comandă rapidă către orice element @@ -2400,19 +2508,19 @@ Golire Coș de Reciclare - Deschideți meniul "Formatare unitate" pentru elementul selectat + Deschideți meniul „Formatare unitate” pentru elementul selectat - Restaurați element(ele) selectate din coșul de reciclare + Restaurați {0, plural, one {elementul} few {elementele} other {elementele}} din coșul de reciclare Restaurați toate elementele din coșul de reciclare - Deschideți elementul/ele + Deschidere {0, plural, one {element} few {elemente} other {elemente}} - Deschideți element(ele) cu aplicația selectată + Deschidere {0, plural, one {element} other {elemente} other {elemente}} cu aplicația selectată Deschide folderul părinte al elementului căutat @@ -2427,49 +2535,58 @@ Selectați toate elementele - Inversare selecție + Inversare elemente selectate - Ștergeți selecția elementelor + Anulare selecție - Comută selecția elementelor + Comutați selecția elementelor - Partajați fișierul/fișierele selectate cu alții + Partajare {0, plural, one {fișier} few {fișiere} other {fișiere}} cu alții - Fixați element(ele) la Meniul Start + Fixare {0, plural, one {element} few {elemente} other {elemente}} la Meniul Start - Anulați fixarea la Meniul Start a elementului/lor + Anulați fixarea {0, plural, one {elementului} few {elementelor} other {elementelor}} la Meniul Start - Fixați un folder/foldere la Bara Laterală + Fixare {0, plural, one {folder} few {foldere} other {foldere}} la Meniul Start - Anulați fixarea folderului/folderelor la Bara Laterală + Anulați fixarea {0, plural, one {folderului} few {folderelor} other {folderelor}} la Bara Laterală Setați imaginea selectată ca imagine de fundal - Setați imaginea selectată ca expunere de diapozitiv + Setați imaginea selectată ca expunere de diapozitive pe desktop Setați imaginea selectată ca imagine de fundal pentru ecranul de blocare - Setează imaginea selectată ca fundal al aplicației + Setați imaginea selectată ca fundal al aplicației + + + Instalare font + + + Instalare driver + + + Instalare certificat - Instalați fonturile selectate + Instalați {0, plural, one {fontul selectat} few {fonturile selectate} other {fonturile selectate}} - Instalați drivere utilizând fișierele inf selectate + Instalați {0, plural, one {driverul} other {driverele}} folosind {0, plural, one {fișierul .inf selectat} other {fișierele .inf selectate}} - Instalați certificatele selectate + Instalați {0, plural, one {certificatul selectat} other {certificatele selectate}} Executați aplicația selectată ca administrator @@ -2484,28 +2601,28 @@ Lansați previzualizarea în fereastra pop-up - Creare arhivă cu elementele selectate + Creare arhivă cu {0, plural, one {elementul selectat} other{elementele selectate}} - Creare arhivă 7z instant cu elementele selectate + Creare archivă 7z cu {0, plural, one {elementul selectat} other {elementele selectate}} - Creare arhivă zip instant cu elementele selectate + Creare archivă zip cu {0, plural, one {elementul selectat} other {elementele selectate}} - Extragere elemente din arhivele selectate în orice folder + Extrageți {0, plural, one {arhiva selectată} few {arhivele selectate} other {arhivele selectate}} în orice folder - Extragere elemente din arhivele selectate în folderul curent + Extrageți {0, plural, one {arhiva selectată} few {arhivele selectate} other {arhivele selectate}} în folderul curent - Extragere elemente din arhivele selectate într-un folder nou + Extrageți {0, plural, one {arhiva selectată} few {arhivele selectate} other {arhivele selectate}} într-un un folder nou - Rotiți imaginile selectate la stânga + Rotiți {0, plural, one {imaginea selectată} other {imaginile selectate}} la stânga - Rotiți imaginile selectate la dreapta + Rotiți {0, plural, one {imaginea selectată} other {imaginile selectate}} la dreapta Deschideți pagina de setări @@ -2525,8 +2642,8 @@ Comutare la vizualizare detalii - - Comutare la vizualizare dale + + Comutare la vizualizare carduri Comutare la vizualizare listă @@ -2547,7 +2664,7 @@ Comutare vizualizări în mod adaptiv - Sortați elementele după nume + Sortare elemente după nume Sortare elemente după data modificării @@ -2625,13 +2742,13 @@ Comutare direcție sortare grup - Deschidere filă nouă + Deschideți o filă nouă - Navigați înapoi în istoricul navigației + Navigați înapoi - Navigați înainte în istoricul navigației + Navigați înainte Navigați în sus într-un director @@ -2660,8 +2777,11 @@ Închideți alte file decât fila selectată + + Închideți toate filele inclusiv fila curentă + - Redeschideți ultima filă închisă + Redeschideți fila închisă recent Treceți la fila anterioară @@ -2676,10 +2796,10 @@ Închideți panoul activ - Focus other pane + Focalizare pe alt panou - Switch focus to the non active pane + Comutați focalizarea la celălalt panoul Comutare bară laterală @@ -2705,7 +2825,7 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Spaţiu + Spațiu Key name for hotkeys in menus. Use abbreviation if possible. @@ -2745,7 +2865,7 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Pornire + Home Key name for hotkeys in menus. Use abbreviation if possible. @@ -2777,7 +2897,7 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Print + Imprimare Key name for hotkeys in menus. Use abbreviation if possible. @@ -2805,23 +2925,23 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Mail + E-mail Key name for hotkeys in menus. Use abbreviation if possible. - Pornire + NavigareLaPornire Key name for hotkeys in menus. Use abbreviation if possible. - Înapoi + NavigareÎnapoi Key name for hotkeys in menus. Use abbreviation if possible. - Înainte + NavigareÎnainte Key name for hotkeys in menus. Use abbreviation if possible. - BrowserReîmprospătare + ReîmprospătareBrowser Key name for hotkeys in menus. Use abbreviation if possible. @@ -2857,7 +2977,7 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Silențios + Amuțire Key name for hotkeys in menus. Use abbreviation if possible. @@ -2875,7 +2995,7 @@ Comutare selecție - Afișați un avertisment la modificarea extensiilor de fișiere + Afișare avertisment la modificarea extensiilor de fișiere Acest PC @@ -2887,13 +3007,13 @@ Fără etichetă - Mută la fila anterioară + Fila precedentă - Mută la fila următoare + Fila următoare - Închideți fila curentă + Închidere filă Editare cale @@ -2908,7 +3028,7 @@ Locație nevalidă - Sunteţi sigur că doriţi să copiaţi aceste fişiere fără proprietăţile lor? + Sunteți sigur că doriți să copiați aceste fișiere fără proprietățile lor? Aceste fișiere au proprietăți care nu pot fi copiate la noua locație @@ -2923,7 +3043,7 @@ Afișați casete de selectare când selectați elemente - Focusare pe bara de cale + Editare cale în OmniBar Creare element nou @@ -2932,13 +3052,13 @@ Niciun grup sau utilizatori nu au permisiunea de a accesa acest obiect. Cu toate acestea, proprietarul acestui obiect poate atribui permisiuni. - Deschideți locația elementului + Deschidere locație element - Ștergeți definitiv + Ștergere definitivă - Ștergeți elementele definitiv + Ștergeți {0, plural, one {elementul selectat} few {elementele selectate} other {elemente selectate}} permanent Redați fișierele media selectate @@ -2965,7 +3085,7 @@ Zi - Comută unitatea pentru grupare după dată + Comutare unitate pentru grupare după dată Comutare unitate de grupare @@ -2974,7 +3094,7 @@ An - Grupați după unitatea de dată + Grupare după unitatea de dată Grupați elementele după ziua datei create @@ -3016,7 +3136,7 @@ Nu se pot afișa permisiunile. - Lăsați modificările mele pe '{0}' + Lăsați modificările mele pe „{0}” Renunțați la modificările mele @@ -3027,6 +3147,15 @@ Aveți modificări neconfirmate în această ramură. Ce doriți să faceți cu ele? + + Aveți o îmbinare în curs cu conflicte nerezolvate. Vă rugăm să rezolvați conflictele sau să abandonați îmbinarea pentru a comuta ramurile. + + + Renunțați la îmbinare și comutați la „{0}” + + + Rămâneți pe „{0}” și rezolvați conflictele + Comutare ramură @@ -3052,22 +3181,28 @@ Bazat pe - Comutați la o ramură nouă + Comutare la o ramură nouă - Creați un folder cu elementele selectate curent + Creați un folder cu {0, plural, one {elementul selectat} other {elementele selectate}} în prezent - Deschidere proprietăți + Proprietăți + + + Deschideți proprietăți Explorer Deschideți fereastra proprietăți + + Deschideți fereastra proprietăți Explorer + Locale - La distanţă + La distanță Traduceți pe Crowdin @@ -3079,16 +3214,19 @@ Pull - Rulare preluarea git + Executare git fetch + + + Clonați un repozitoriu git - Rulare git pull + Executare git pull Previzualizare - Status Git + Stare Git Eroare Git @@ -3104,19 +3242,19 @@ Autor - Data comiterii + Dată commit - Mesajul comiterii + Mesajul commit-ului - Comitere SHA + Sumă de control SHA Git - Acrylic + Acrilic Mica @@ -3125,7 +3263,7 @@ Mica Alt - Material Fundal + Fundal Solid @@ -3137,16 +3275,16 @@ Push - Rulare git push + Executare git push Sincronizare - Rulați git pull și apoi git push + Executați git pull și apoi git push - {0} ieșiri / {1} comiteri de intrare + {0} ieșiri / {1} comiteri primite Conectați-vă la GitHub @@ -3157,23 +3295,23 @@ Files nu poate accesa GitHub momentan. - - Deschideți folderul în VS Code + + Deschideți folderul în {0} - - Deschideți directorul curent în Visual Studio Code + + Deschideți directorul curent în {0} - - Deschideți repozitoriul în VS Code + + Deschideți repozitoriul în {0} - - Deschideți rădăcina repozitoriului Git în Visual Studio Code + + Deschideți rădăcina repozitoriului Git în {0} Copiere cod - Adaugat + Adăugat Șters @@ -3188,13 +3326,13 @@ Imposibil de afișat proprietarul curent. - Minunat! Acum ești autorizat. + Minunat! Acum sunteți autorizat. Inițializați repozitoriul - Inițializați un repozitoriu Git + Inițializați folderul curent ca un repozitoriu git Nume Repozitoriu la Distanță @@ -3209,16 +3347,16 @@ Sortare elemente după cale - Deschideți directorul într-un panou nou + Deschideți folderul selectat într-un panou nou - Deschideți directorul într-o filă nouă + Deschideți folderul selectat într-o filă nouă - Deschideți directorul într-o fereastră nouă + Deschideți folderul selectat într-o fereastră nouă - Deschideți toate + Deschideți-le pe toate Deschideți toate elementele etichetate @@ -3231,28 +3369,28 @@ Comandă invalidă - '{0}' nu este recunoscut ca o comandă. + „{0}“ nu este recunoscut ca o comandă. Comanda nu este executabilă - Comanda '{0}' nu este gata pentru a fi executată. + Comanda „{0}“ nu este gata pentru a fi executată. - Paletă de comenzi + Paletă de Comenzi - Deschidere paletă de comenzi + Deschideți Paleta de Comenzi în OmniBar Pornire în: - Activați BitLocker + Activare BitLocker - Gestionați BitLocker + Gestionare BitLocker Următoarele elemente sunt prea mari pentru a fi copiate la această unitate @@ -3261,7 +3399,7 @@ Formatare unitate - Nu mai afișa + Nu mai afișați Files rulează ca administrator @@ -3289,7 +3427,7 @@ One of the custom color themes - Albastru Deschis + Albastru Rece Luminos One of the custom color themes @@ -3305,7 +3443,7 @@ One of the custom color themes - Iris Pastelat + Iris Pastel One of the custom color themes @@ -3313,7 +3451,7 @@ One of the custom color themes - Roșu Aprins + Roșu Strident One of the custom color themes @@ -3329,7 +3467,7 @@ One of the custom color themes - Roz Deschis + Rozaliu luminos One of the custom color themes @@ -3341,7 +3479,7 @@ One of the custom color themes - Roșu-violet Deschis + Roșu Violet Deschis One of the custom color themes @@ -3373,155 +3511,226 @@ Se procesează elementele... + + Se descoperă elemente... + Viteză: - S-a anulat comprimarea a {0} elemente în "{1}" + S-a anulat comprimarea a {0} elemente în „{1}” Shown in a StatusCenter card. - S-a anulat comprimarea a {0} elemente din "{1}" în "{2}" + S-a anulat comprimarea a {0} elemente din „{1}” în „{2}” Shown in a StatusCenter card. - S-au comprimat {0} element(e) în "{1}" + {0, plural, one {S-a comprimat # element} few {S-au comprimat # elemente} other {S-au comprimat # de elemente}} la „{1}” Shown in a StatusCenter card. - S-au comprimat {0} element(e) din "{1}" în {2}" + {0, plural, one {S-a comprimat # element} few {S-au comprimat # elemente} other {S-au comprimat # de elemente}} de la „{1}” la „{2}” Shown in a StatusCenter card. - Eroare la comprimarea {0} element(e) la "{1}" + Eroare la comprimarea {0, plural, one {a unui element} few {a # elemente} other {a # de elemente}} la „{1}” Shown in a StatusCenter card. - Copierea a {0} element(e) de la "{1}" la "{2}" a eșuat + {0, plural, one {Nu s-a putut comprima un element} few {Nu s-au putut comprima # elemente} other {Nu s-au putut comprima # de elemente}} de la „{1}” la „{2}” Shown in a StatusCenter card. - Se comprimă {0} element(e) la "{1}" + Se comprimă {0, plural, one {un element} few {# elemente} other {# de elemente}} la „{1}” Shown in a StatusCenter card. - Se comprimă {0} element(e) de la "{1}" la "{2}" + Se comprimă {0, plural, one {un element} few {# elemente} other {# de elemente}} de la „{1}” la „{2}” + Shown in a StatusCenter card. + + + Clonarea {0} la „{1}” a fost anulată + Shown in a StatusCenter card. + + + Clonarea {0} de la „{1}” la „{2}” a fost anulată + Shown in a StatusCenter card. + + + Clonat „{0}” la „{1}” + Shown in a StatusCenter card. + + + Clonat {0} din „{1}” la „{2}” + Shown in a StatusCenter card. + + + Eroare la clonarea „{0}” la „{1}” + Shown in a StatusCenter card. + + + Nu s-a putut clona {0} din „{1}” la „{2}” + Shown in a StatusCenter card. + + + Se clonează „{0}” la „{1}” + Shown in a StatusCenter card. + + + Se clonează {0} din „{1}” la „{2}” + Shown in a StatusCenter card. + + + Instalarea a {0} fonturi a fost anulată + Shown in a StatusCenter card. + + + Instalarea a {0, plural, one {# font} few {# fonturi} other {# de fonturi}} din „{1}” a fost anulată + Shown in a StatusCenter card. + + + {0, plural, one {S-a instalat un font} few{S-au instalat # fonturi} other{S-au instalat # de fonturi}} + Shown in a StatusCenter card. + + + {0, plural, one {S-a instalat un font} few{S-au instalat # fonturi} other{S-au instalat # de fonturi}} din „{1}” + Shown in a StatusCenter card. + + + Eroare la instalarea a {0, plural, one {un font} few {# fonturi} other {# de fonturi}} + Shown in a StatusCenter card. + + + {0, plural, one {Nu s-a putut instala un font} few {Nu s-au putut instala # fonturi} other{Nu s-au putut instala # de fonturi}} de la „{1}” + Shown in a StatusCenter card. + + + Se instalează {0, plural, one {un font} few{# fonturi} other {# de fonturi}} + Shown in a StatusCenter card. + + + Se instalează {0, plural, one {# font} few{# fonturi} other {# de fonturi}} din „{1}” Shown in a StatusCenter card. - S-a anulat copierea a {0} element(e) la "{1}" + Copierea a {0, plural, one {un element} few {# elemente} other {# de elemente}} la „{1}” a fost anulată Shown in a StatusCenter card. - S-a anulat copierea a {0} element(e) de la "{1}" la "{2}" + Copierea a {0, plural, one {un element} few {# elemente} other {# de elemente}} de la „{1}” la „{2}” a fost anulată Shown in a StatusCenter card. - S-au copiat {0} element(e) la "{1}" + {0, plural, one {S-a copiat un element} few {S-au copiat # elemente} other {S-au copiat # de elemente}} la „{1}” Shown in a StatusCenter card. - S-au copiat {0} element(e) de la "{1}" la "{2}" + {0, plural, one {S-a copiat un element} few {S-au copiat # elemente} other {S-au copiat # de elemente}} de la „{1}” la „{2}” Shown in a StatusCenter card. - Eroare la copierea a {0} element(e) la "{1}" + Eroare la copierea a {0, plural, one {un element} few {# elemente} other {# de elemente}} la „{1}” Shown in a StatusCenter card. - Copierea a {0} element(e) de la "{1}" la "{2}" a eșuat + {0, plural, one {Nu s-a putut copia un element} few {Nu s-au putut copia # elemente} other {Nu s-au putut copia # de elemente}} de la „{1}” la „{2}” Shown in a StatusCenter card. - Se copiază {0} element(e) în {1}" + Se copiază {0, plural, one {un element} few {# elemente} other {# de elemente}} la „{1}” Shown in a StatusCenter card. - Se copiază {0} element(e) de la "{1}" la "{2}" + Se copiază {0, plural, one {un element} few {# elemente} other {# de elemente}} de la „{1}” la „{2}” Shown in a StatusCenter card. + + {0, plural, one {S-a descoperit # element} few {S-au descoperit # elemente} other {S-au descoperit # de elemente}} + Shown in a StatusCenter card during file discovery phase. + - S-a anulat extragerea "{0}" la "{1}" + S-a anulat extragerea „{0}” la „{1}” Shown in a StatusCenter card. - S-a anulat extragerea "{0}" din "{1}" în "{2}" + S-a anulat extragerea „{0}” din „{1}” la „{2}” Shown in a StatusCenter card. - S-a extras "{0}" în "{1}" + S-a extras „{0}” la „{1}” Shown in a StatusCenter card. - S-a extras "{0}" din "{1}" în "{2}" + S-a extras „{0}” din „{1}” la „{2}” Shown in a StatusCenter card. - Eroare la extragerea "{0}" în "{1}" + Eroare la extragerea „{0}” la „{1}” Shown in a StatusCenter card. - Nu s-a putut extrage "{0}" din "{1}" în "{2}" + Nu s-a putut extrage „{0}” din „{1}” la „{2}” Shown in a StatusCenter card. - Se extrage "{0}" în "{1}" + Se extrage „{0}” la „{1}” Shown in a StatusCenter card. - Se extrage "{0}" din "{1}" în "{2}" + Se extrage „{0}” din „{1}” la „{2}” Shown in a StatusCenter card. - S-a anulat ștergerea a {0} element(e) din "{1}" + Ștergerea a {0, plural, one {un element} few {# elemente} other {# de elemente}} de la „{1}” a fost anulată Shown in a StatusCenter card. - S-au șters {0} element(e) din "{1}" + {0, plural, one {S-a șters un element} few {S-au șters # elemente} other {S-au șters # de elemente}} de la „{1}” Shown in a StatusCenter card. - Eroare la ștergerea a {0} element(e) din "{1}" + Eroare la ștergerea a {0, plural, one {un element} few {# elemente} other {# de elemente}} de la „{1}” Shown in a StatusCenter card. - Ștergerea a {0} elemente din {1} a eșuat + {0, plural, one {Nu s-a putut șterge un element} few {Nu s-au putut șterge # elemente} other {Nu s-au putut șterge # de elemente}} de la „{1}” Shown in a StatusCenter card. - Se șterg {0} element(e) din "{1}" + {0, plural, one {Se șterge un element} few {Se șterg # elemente} other {Se șterg # de elemente}} de la „{1}” Shown in a StatusCenter card. - S-a anulat mutarea a {0} element(e) în "{1}" + Mutarea a {0, plural, one {un element} few {# elemente} other {# de elemente}} la „{1}” a fost anulată Shown in a StatusCenter card. - S-a anulat mutarea a {0} element(e) din "{1}" în "{2}" + Mutarea a {0, plural, one {un element} few {# elemente} other {# de elemente}} de la „{1}” la „{2}” a fost anulată Shown in a StatusCenter card. - S-au mutat {0} element(e) în "{1}" + {0, plural, one {A fost mutat # element} few {Au fost mutate # elemente} other {Au fost mutate # de elemente}} la „{1}” Shown in a StatusCenter card. - S-au mutat {0} element(e) din "{1}" în "{2}" + {0, plural, one {A fost mutat un element} few {Au fost mutate # elemente} other {Au fost mutate # de elemente}} de la „{1}” la „{2}” Shown in a StatusCenter card. - Se mută {0} element(e) în "{1}" + Se mută {0, plural, one {un element} few {# elemente} other {# de elemente}} la „{1}” Shown in a StatusCenter card. - Se mută {0} element(e) din "{1}" în "{2}" + Se mută {0, plural, one {un element} few {# elemente} other {# de elemente}} de la „{1}” la „{2}” Shown in a StatusCenter card. - Eroare la mutarea a {0} element(e) în "{1}" + Eroare la mutarea a {0, plural, one {un element} few {# elemente} other {# de elemente}} la „{1}” Shown in a StatusCenter card. - Mutarea a {0} element(e) din "{1}" la "{2}" a eșuat + {0, plural, one {Nu s-a putut muta un element} few {Nu s-au putut muta # elemente} other {Nu s-au putut muta # de elemente}} de la „{1}” la „{2}” Shown in a StatusCenter card. @@ -3549,11 +3758,11 @@ Shown in a StatusCenter card. - {0}/{1} element(e) procesate + {0}/{1, plural, one {# element procesat} few {# elemente procesate} other {# de elemente procesate}} Shown in a StatusCenter card. Used as "8/20 items processed" - Deblochează fișierul descărcat + Deblocare fișier descărcat Fără operațiuni de fișiere în curs @@ -3567,11 +3776,14 @@ Nu s-a putut seta imaginea de fundal + + Nu s-a reușit deschiderea fișierului de setări + Ștergere Ramură Git - Sigur doriți să ștergeți definitiv ramura "{0}"? + Sigur doriți să ștergeți definitiv ramura „{0}”? Conectat la GitHub @@ -3592,25 +3804,25 @@ A apărut o eroare la aplicarea acestei etichete - Extrage elementele din arhivele selectate în folderul curent pentru arhivele cu un singur element, sau într-un folder nou pentru arhivele cu mai multe elemente + Extrageți {0, plural, one {arhiva selectată} few {arhivele selectate} other {arhivele selectate}} aici pentru un singur element sau într-un folder nou pentru mai multe elemente - Extrageți aici (Smart) + Extragere aici (Smart) - Sortați fișierele și folderele împreună + Sortare fișiere și foldere împreună - Sortați fișierele și folderele împreună + Sortare fișiere și foldere în aceeași listă - Sortați mai întâi fișierele + Sortare fișiere mai întâi Sortați mai întâi fișierele apoi folderele - Sortați mai întâi folderele + Sortare foldere mai întâi Sortați mai întâi folderele apoi fișierele @@ -3631,13 +3843,13 @@ Partajarea elementelor a eșuat - Derulează la folderul anterior când navighezi în sus + Derulare la folderul anterior când se navighează în sus Deschideți o fereastră nouă - Schimbați coperta albumului + Modificare copertă album Nu s-a putut redenumi elementul @@ -3657,9 +3869,6 @@ Întrebări și discuții - - Alte mărimi nu sunt încă disponibile pentru Vizualizarea Dale. - Compact Used to describe layout sizes @@ -3728,7 +3937,7 @@ Adăugare comandă - Restabiliți la valorile implicite + Restabilire la valorile implicite Alegeți o acțiune @@ -3752,11 +3961,11 @@ Imagine de fundal - Partea de jos + În jos Image alignment type - Centrare + Centrat Image alignment type @@ -3800,17 +4009,17 @@ This is a type of backdrop for the application background - Arată pentru toate locațiile + Afișare pentru toate locațiile Setting where users can choose to display "Open IDE" button for Git Repos Instrumente pentru dezvoltatori - Configurați butonul "Deschide IDE" pe bara de stare + Configurați butonul „Deschidere IDE” pe bara de stare - Arată pentru Git repos + Afișare pentru repozitoriile Git Setting where users can choose to display "Open IDE" button for all locations. @@ -3818,9 +4027,13 @@ This is the friendly name for DLL files. - Fişier ICO + Fișier ICO This is the friendly name for ICO files. + + Fișier ICL + This is the friendly name for ICL files. + Fișier Zip This is the friendly name for ZIP files. @@ -3829,17 +4042,21 @@ Fișiere Bitmap This is the friendly name for bitmap files. + + Fișiere Imagine + This is the friendly name for image files. + Num - Locațiile rețelei + Locații în rețea - Nu există locații de rețea. Dacă nu utilizați locații de rețea, puteți dezactiva widgetul. + Nu există locații în rețea. Dacă nu utilizați locații în rețea, puteți dezactiva widget-ul. - Dezactivează + Dezactivare Editare în Notepad @@ -3851,55 +4068,313 @@ Dimensiuni: - Statut: + Stare: - Arătați Bara cu Instrumente - Setting that controls if the Toolbar is shown in the main view + Afișați bara de instrumente + Setting that controls if the toolbar is shown in the main view Meniu acțiuni filă - - Add vertical pane + + Divizare verticală - Add a vertical pane + Divizare panou pe verticală - - Add horizontal pane + + Divizare orizontală - - Add a horizontal pane + + Divizare panou pe orizontală - Arrange vertically + Aranjare verticală - Arrange panes vertically + Aranjați panourile vertical - Arrange horizontally + Aranjare orizontală - Arrange panes horizontally + Aranjați panourile orizontal - - Add pane + + Divizare panou - Show tab actions button in the title bar + Afișare buton de acțiuni de filă în bara de titlu - Arrange panes + Aranjați panourile + + + Direcția implicită de divizare a panoului dual - - Default pane arrangement + + Mod panou dual - Horizontal + Orizontal Vertical + + Afișați pictograma Files în System Tray + + + Fire CPU + + + Navigați spre pagina principală + + + Bare de Instrumente + + + ID utilizator + + + Redenumire în masă + + + Comprimați conținutul + + + Afișați opțiunea pentru a crea un flux alternativ de date + + + Creare flux alternativ de date + + + Creați un flux de date alternativ pentru {0, plural, one {elemetul selectat} other {elementele selectate}} + + + Introduceți numele fluxului de date + + + A apărut o eroare la crearea fluxului alternativ de date + + + Vă rugăm să rețineți că fluxurile alternative de date funcționează doar pe unitățile formatate ca NTFS. + + + Fluxurile alternative de date sunt în prezent ascunse + + + Doriți să afișați fluxuri alternative de date? Puteți modifica această setare oricând din pagina de setări a fișierelor și folderelor. + + + Gestionare etichete + + + Disponibil + + + Total + + + Comutați întotdeauna focalizarea la fila nou creată + + + Comutare panou raft + + + Comutați vizibilitatea panoului raft + + + Afișați comutatorul pentru panoul raft în bara de adresă + + + Raft + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + Eliminare elemente + + + Eliminare din raft + + + Adăugare la raft + Tooltip that displays when dragging items to the Shelf Pane + + + Introduceți un hash pentru a compara + Placeholder that appears in the compare hash text box + + + Potriviri {0} + Appears when two compared hashes match, e.g. "Matches SHA256" + + + Nicio potrivire găsită + Appears when two compared hashes don't match + + + Cale sau alias + + + Cale invalidă + + + Integrare test + + + {0} nu a putut fi localizat. Vă rugăm să vă verificați setările și să încercați din nou. + + + IDE-ul configurat nu a putut fi localizat + + + Deschidere setări + + + Visual Studio Code + + + Introduceți o cale sau un alias de lansare + + + Vă rugăm să introduceți un nume pentru IDE + + + Clonare repozitoriu + Clone repo dialog title + + + Clonare + Primary action button in the clone repo dialog + + + URL repozitoriu + URL textbox header in the clone repo dialog + + + Nu se poate clona repozitoriul + Cannot clone repo dialog title + + + Comparați un fișier + Button that appears in file hash properties that allows the user to compare two files + + + Format dimensiune + + + Binar + + + Zecimal + + + Puteți adăuga secțiuni în bara laterală făcând clic dreapta și selectând secțiunile pe care doriți să le adăugați + + + Trageți fișiere sau foldere aici pentru a interacționa cu ele prin diferite file + + + Introduceți o cale pentru a naviga la... + + + Găsiți caracteristici și comenzi... + + + Căutare fișiere și foldere... + + + În timpul operațiunilor de fișier + + + Afișați butonul centru de stare + + + Inel de proces centru de stare + Screen reader name for the status center progress ring + + + Fișiere pictograme + This is the friendly name for a variety of different icon files. + + + Afișare cale compactă (breadcrumbs) + + + Afișare foldere în {0} + + + Afișare foldere în Pornire + + + Nu există comenzi care să conțină {0} + + + Vedeți mai multe + + + Filtrare pentru + + + Nume fișier + + + Semnături + + + Listă semnătură + + + Emis de: + + + Emis către: + + + Valid de la: + + + Valid la: + + + Nici o semnătură găsită. + + + Afișați opțiunea de a deschide foldere în Terminalul Windows + + + Deschidere fișier jurnal + + + Deschideți fișierul jurnal în editorul dvs. implicit + + + Deschideți locația fișierului jurnal în managerul de fișiere implicit + + + Nu s-a putut deschide fișierul jurnal + + + Afișați bara de stare + + + Doar în Vizualizarea coloane + + + Nou {0} + + + Activare defilare lină + + + Defilare + + + Afișați opțiunea de fixare în bara laterală + + + Afișați opțiunea de fixare în Meniul Start + \ No newline at end of file diff --git a/src/Files.App/Strings/ru-RU/Resources.resw b/src/Files.App/Strings/ru-RU/Resources.resw index 83378d23d121..9076c6e38c24 100644 --- a/src/Files.App/Strings/ru-RU/Resources.resw +++ b/src/Files.App/Strings/ru-RU/Resources.resw @@ -121,10 +121,16 @@ Новое окно - Копировать путь + Скопировать путь + + + Копировать путь элемента - Копировать путь с кавычками + Скопировать путь с кавычками + + + Скопировать путь выделенного элемента с кавычками Обзор @@ -160,13 +166,13 @@ Пропустить - Выделить Всё + Выбрать всё Инвертировать выделение - Очистить выделение + Убрать выделение Изменено: @@ -178,7 +184,7 @@ Очистить все - Введите путь для навигации или введите ">", чтобы открыть командную строку + Введите путь для навигации или введите «>», чтобы открыть командную строку Поиск @@ -196,7 +202,7 @@ О приложении - Об исходном коде + Открытый исходный код Формат даты @@ -211,7 +217,7 @@ Показывать скрытые файлы и папки - Показать расширения имен файлов + Показать dot-файлы Внешний вид @@ -249,6 +255,9 @@ Вставить + + Вставить ярлык + Создать @@ -291,8 +300,8 @@ Введите название элемента - - Введите имя + + Создать новый: {0} Светлая @@ -300,6 +309,9 @@ Темная + + Как в системе + По умолчанию @@ -319,7 +331,7 @@ Назад - Вперед + Вперёд Вверх @@ -358,7 +370,7 @@ Не удается открыть свойства этого файла - Файл, к которому Вы пытаетесь получить доступ, возможно, был перемещен или удален. + Файл, который вы пытаетесь открыть, либо был перемещён, либо удалён. Файл не найден @@ -373,7 +385,7 @@ Файл, к которому вы пытаетесь получить доступ, в настоящее время используется {0} - Файл, к которому вы пытаетесь получить доступ, в настоящее время используется другим приложением + Файл, который вы пытаетесь открыть, сейчас используется другим приложением Файл используется @@ -385,7 +397,7 @@ Только чтение - {0, plural, one {# день назад} few {# дней назад} many {# дней назад} other {# дней назад}} + {0, plural, one {# день назад} few {# дня назад} other {# дней назад}} 1 день назад @@ -394,7 +406,7 @@ {0} дней назад - {0, plural, one {# час назад} few {# часов назад} many {# часов назад} other {# часов назад}} + {0, plural, one {# час назад} few {# часа назад} other {# часов назад}} 1 час назад @@ -403,7 +415,7 @@ {0} часов назад - {0, plural, one {# минута назад} few {# минут назад} many {# минут назад} other {# минут назад}} + {0, plural, one {# минута назад} few {# минуты назад} other {# минут назад}} 1 минуту назад @@ -412,7 +424,7 @@ {0} минут назад - {0, plural, one {# секунду назад} few {# секунд назад} many {# секунд назад} other {# секунд назад}} + {0, plural, one {# секунду назад} few {# секунды назад} other {# секунд назад}} 1 секунду назад @@ -430,7 +442,7 @@ {0} свободно из {1} - Отправить в + Отправить Имя элемента не должно содержать следующих знаков: \ / : * ? " < > | @@ -439,11 +451,11 @@ Указанное имя элемента недопустимо - {0, plural, one {# элемент выбран} few {# предметов выбрано} many {# предметов выбрано} other {# предметов выбрано}} + {0, plural, one {# элемент выбран} few {# элемента выбрано} other {# элементов выбрано}} ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural - {0, plural, one {предмет} few {предметы} many {предметы} other {предметы}} + {0, plural, one {элемент} few {элемента} many {элементов} other {элементы}} ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural @@ -477,10 +489,10 @@ Б - {0, number} {0, plural, one {file} other {files}}, {1, number} {1, plural, one {folder} other {folders}} + {0, number} {0, plural, one {файл} few {файла} other {файлов}}, {1, number} {1, plural, one {папка} few {папки} other {папок}} - {0, number} {0, plural, one {file} other {files}}, {1, number} {1, plural, one {folder} other {folders}} from {2, number} {2, plural, one {location} other {locations}} + {0, number} {0, plural, one {файл} few {файла} other {файлов}}, {1, number} {1, plural, one {папка} few {папки} other {папок}} из {2, number} {2, plural, one {места} other {мест}} Запуск от имени другого пользователя @@ -513,10 +525,7 @@ Переместить вкладку - Status - - - Стандартный + Статус Нет результатов @@ -530,6 +539,9 @@ Копировать в {0} + + Клонировать в {0} + Создать ярлык @@ -894,10 +906,10 @@ Скорость потока - Аудио кодирование битрейт + Битрейт кодирования аудио - Битрейт видео кодирования + Битрейт кодирования видео Сжатие @@ -998,6 +1010,9 @@ Результаты поиска в {1} для {0} + + Результаты поиска для `{0}` + Подробно @@ -1029,13 +1044,13 @@ Открывать вкладки в двухпанельном режиме - Показывать опцию открытия папок в новой панели + Показать опцию открытия папок в новой панели Показать опцию копирования пути - Показать опцию создания папки с выделением + Показать опцию создания папки из выделенных элементов Показать опцию создания ярлыка @@ -1052,9 +1067,6 @@ Таблица (Ctrl+Shift+1) - - Плитка (Ctrl+Shift+2) - Дата удаления @@ -1068,22 +1080,37 @@ Имя Коллекции - Показать панель предварительного просмотра + Показать панель предпросмотра Показать панель подробного просмотра - Показать панель деталей для просмотра основных свойств файла + Переключить панель подробного просмотра основных свойств файла Показать информационную панель - Переключить панель информации для просмотра панелей с подробностями / предварительным просмотром + Переключить отображение панелей сведений/предпросмотра - Toggle the Toolbar + Показать/скрыть панель инструментов + + + Переключить видимость панели инструментов + + + Переключить панель фильтров + + + Переключить видимость заголовка фильтра + + + Переключить двойную панель + + + Переключить двухпанельный режим Предпросмотр недоступен @@ -1238,41 +1265,47 @@ Открыть Контроль памяти + + Открыть страницу Контроля памяти в настройках Windows + + + Очистка + Копировать - Copy {0, plural, one {item} other {items}} + Копировать {0, plural, one {элемент} other {элеметы}} - Delete {0, plural, one {item} other {items}} + Удалить {0, plural, one {элемент} other {элеметы}} Переместить - {0, plural, one {One item will be moved} other {# items will be moved}} + {0, plural, one {Один элемент будет перенесён} few {# элемента будет перенесено} other {# элементов будет перенесено}} - Move {0, plural, one {item} other {items}} + Перенести {0, plural, one {элемент} other {элементы}} - {0, plural, one {One item will be deleted} other {# items will be deleted}} + {0, plural, one {Один элемент будет удалён} few {# элемента будет удалено} other {# элементов будет удалено}} Продолжить - {0, plural, one {There is one conflicting file name} other {There are # conflicting file names}} + {0, plural, one {Присутствует один конфликт имени файлов} few {Присутствует # конфликта имён файлов} other {Присутствует # конфликтов имён файлов}} - {0, plural, one {There is one conflicting file name} other {There are # conflicting file names}}, and {1, plural, one {one outgoing item} other {# outgoing items}} + {0, plural, one {Присутствует один конфликт имени файлов} few {Присутствует # конфликта имён файлов} other {Присутствует # конфликтов имён файлов}} и {1, plural, one {один исходящий элемент} few {# исходящих элемента} other {# исходящих элементов}} - Conflicting {0, plural, one {file name} other {file names}} + Конфликт {0, plural, one {имени файла} other {имён фалов}} - {0, plural, one {One item will be copied} other {# items will be copied}} + {0, plural, one {Один элемент будет скопирован} few {# элемента будет скопировано} other {# элементов будет скопировано}} Удалить безвозвратно @@ -1287,7 +1320,7 @@ Сгенерировать новое имя - Создать папку из выбранных элементов + Создать папку из выделенных элементов Тип: @@ -1368,10 +1401,10 @@ Ранее в этом году - Прошлый год + В прошлом год - Год {0} + {0} год {0} элемент @@ -1380,7 +1413,7 @@ {0} элементов - Открыть закрытую вкладку + Восстановить закрытую вкладку Переименовать @@ -1497,7 +1530,7 @@ подпапки - Унаследовано + Унаследованные Разрешения @@ -1587,7 +1620,7 @@ Заменить все записи разрешений дочернего объекта наследуемыми записями разрешений от этого объекта - Close active pane + Закрыть pane Войти в компактный режим @@ -1617,7 +1650,7 @@ Загружает объект из облака и загружает предварительный просмотр - {0, plural, one {# item} other {# items}} ({1, plural, one {# file} other {# files}}, {2, plural, one {# folder} other {# folders}}) + {0, plural, one {# элемент} few {# элемента} other {# элементов}} ({1, plural, one {# файл} few {# файла} other {# файлов}}, {2, plural, one {# папка} few {# папки} other {# папок}}) Несжатый размер @@ -1646,15 +1679,6 @@ Главная - - Ctrl+Shift+1 - - - Ctrl+Shift+2 - - - Ctrl+Shift+6 - Распаковка архива успешно завершена. @@ -1689,7 +1713,7 @@ Ок - Требуются учетные данные + Требуются учётные данные Анонимно @@ -1712,8 +1736,8 @@ Столбцы - - Плитки + + Карточки Открывать папки в новой вкладке @@ -1746,10 +1770,10 @@ Иcпользовать Files как диалог для открытия файлов - Тэг + Метка - Открывать файлы и папки одним нажатием + Открывать файлы одним щелчком Путь к папке @@ -1782,7 +1806,7 @@ Сбросить до заводских - Открывать вкладку в существующем экземпляре File при открытии из другого приложения + Открывать вкладку в существующем экземпляре Files при открытии из другого приложения Настройки запуска @@ -1826,6 +1850,18 @@ Регистрация программы для перезапуска + + Начальное окно + + + Обычное окно + + + Свернутый + + + Развернутый + Запустить от имени администратора @@ -1848,11 +1884,14 @@ Без уменьшения цвета - Third party libraries + Сторонние библиотеки Этот параметр изменяет реестр Windows и может иметь неожиданные побочные эффекты на вашем устройстве. Продолжайте на свой страх и риск. + + Операции с выравниванием структуры являются постоянными и не рекомендуются. Активируйте на свой страх и риск. + Создать Библиотеку @@ -1931,6 +1970,9 @@ Закрыть другие вкладки + + Закрыть все вкладки + Музыка @@ -1985,14 +2027,26 @@ Поведение - - Оценить Files + + Привет! + + + Нравится Files? Пожалуйста, оцените приложение в Microsoft Store. + + + Вам нравится Files? Пожалуйста, подумайте о поддержке проекта на GitHub. + + + Спонсор - - Хотели бы вы оценить Files? + + Оцените нас + + + Закрыть - Сделать фоном рабочего стола + Установить как фон Сделать фоном рабочего стола @@ -2018,8 +2072,8 @@ Экран блокировки - - Открывать папки одним кликом в макете Столбцы + + Открывать папки одним кликом Открытие объектов @@ -2027,6 +2081,21 @@ Сжать + + Переместить все содержимое из подпапок в выбранное место + + + Выровнять структуру папки + + + Выровнять структуру + + + При выравнивании структуры папки всё содержимое подпапок будет перенесено в выбранное место. Эта операция постоянна и не может быть повторена. Используя эту экспериментальную функцию, вы соглашаетесь с данным риском и не имеете претензий к команде Files, не несущую ответственность за потерю данных. + + + Показывать опции выравнивания структуры + Выделение файлов и папок при наведении на них курсора мыши @@ -2037,7 +2106,7 @@ Восстановить все элементы - Вы хотите восстановить {0} выбранный(ые) элемент(ы)? + Вы хотите восстановить {0, plural, one {выбранный предмет} few {{0} Выбранные предметы} many {{0} Выбранные предметы} other {{0} Выбранные предметы}}? Восстановить выбранное @@ -2060,6 +2129,12 @@ Пароль архива + + Кодировка + + + {0} (обнаружено) + Путь @@ -2102,9 +2177,24 @@ Размер тома - + Не разбивать + + Auto + + + Dictionary size + + + Word size + + + Estimated memory usage: {0} + + + Available memory: {0} + CD @@ -2130,10 +2220,10 @@ Синхронизировать колонку статуса - Группировка по + Группировка - Сортировка по + Сортировка Группировать в порядке убывания @@ -2145,7 +2235,7 @@ Создать новый ярлык - Создавайте ярлыки для локальных или сетевых программ, файлов, папок, компьютеров и Интернет-адресов. + Создавайте ярлыки для локальных или сетевых программ, файлов, папок, компьютеров и интернет-адресов. Введите местоположение элемента: @@ -2159,8 +2249,14 @@ Редактировать файл настроек - - Из нового + + Открыть файл настроек в вашем редакторе по умолчанию + + + Список изменений + + + Открыть список изменений Создание ярлыка в этом месте требует права администратора @@ -2178,22 +2274,22 @@ Дважды нажмите на пустую область, чтобы перейти на одну папку назад - Показать еще + Показать ещё - Показывать опцию редактирования Меток + Показать опцию редактирования меток Показать опции сжатия - Показать отправить в меню + Показать опцию отправить - Показывать опцию открытия папок в новой вкладке + Показать опцию открытия папок в новой вкладке - Показывать опцию открытия папок в новом окне + Показать опцию открытия папок в новом окне Быстрый доступ @@ -2226,13 +2322,13 @@ Никогда - Цвет тэга + Цвет метки Новая метка - Создать новую тэг + Создать новую метку Загрузка... @@ -2241,25 +2337,22 @@ Настройки контекстного меню - Показывать расширения файлов - - - Показывать скрытые элементы + Расширения файлов - Форматировать... + Форматировать Помощь - Включить полноэкранный режим + Полноэкранный режим - Вы уверены, что хотите удалить этот тэг? + Вы уверены, что хотите удалить эту метку? - Воспроизвести все + Воспроизвести Высота @@ -2319,7 +2412,7 @@ Увеличить размер - Переключить направление сортировки + Направление сортировки Некорректное имя @@ -2331,13 +2424,13 @@ Видео - Запустить предпросмотр + Запустить предпросмотр во всплывающем окне Включить компактный режим - Открыть страницу онлайн справки в браузере + Открыть справку в браузере Включить полноэкранный режим @@ -2352,67 +2445,82 @@ Включить компактный режим - Перейти в окно поиска + Начать поиск в OmniBar - Вкл/Выкл отображение скрытых элементов + Включить видимость скрытых элементов + + + Включить видимость dot-файлов - Вкл/Выкл отображение скрытых элементов + Включить видимость расширений файлов - Переключить панель предварительного просмотра для просмотра превью файла + Переключить панель предпросмотра для просмотра превью файла - Вкл/Выкл отображение боковой панели + Включить/выключить отображение боковой панели - Копировать элемент(ы) в буфер обмена + Скопируйте выбранное {0, plural, one {предмет} few {предметы} many {предметы} other {предметы}} - Копировать путь выбранных элементов в буфер обмена + Скопировать путь текущей папки + + + Скопировать путь выбранных элементов + + + Скопировать путь выбранных элементов (с ковычками) - Копировать путь выбранных элементов с кавычками в буфер обмена + Скопировать путь текущей папки (с ковычками) - Вырезать элемент(ы) в буфер обмена + Вырезать {0, plural, one {} few {предметы} many {предметы} other {предметы}} - Вставить элемент(ы) из буфера в текущую папку + Вставить элементы в текущую папку + + + Вставить элементы в текущую папку как ярлыки - Вставить элемент(ы) из буфера обмена в выбранную папку + Вставить элементы в выбранную папку + + + Вставить в выбранную папку - Удалить элемент(ы) + Удалите {0, plural, one {предмет} few {предметов} many {предметов} other {предметов}} Создать папку - Создать ярлык для выбранных элемент(-ов) + Создайте новый {0, plural, one {ярлык} few {ярлыков} many {ярлыков} other {ярлыков}} для выбора {0, plural, one {} few {} many {} other {}} Создать ярлык для любого элемента - Очистить корзину + Очистить содержимое Корзины - Открыть меню "форматирование диска" + Открыть меню «Форматирование диска» - Восстановить элемент(ы) из корзины + Восстановить выбранное {0, plural, one {предмет} few {предметы} many {предметы} other {предметы}} из корзины Восстановить все элементы из корзины - Открыть элемент(ы) + Открой {0, plural, one {} few {предметы} many {предметы} other {предметы}} - Открыть элемент(ы) в выбранном приложении + Открой {0, plural, one {} few {предметы} many {предметы} other {предметы}} с выбранным приложением Открыть родительскую папку искомого элемента @@ -2430,25 +2538,25 @@ Инвертировать выделение - Очистить выделение + Убрать выделение Переключить выбор элемента - Отправить выбранные файл(ы) другим + Поделитесь выбранным файлом {0, plural, one {} few {файлами} many {файлами} other {файлами}} с остальными - Закрепить элемент(ы) в меню Пуск + Закрепите {0, plural, one {} few {предметов} many {предметов} other {предметов}} в начальном меню - Открепить элемент(ы) из меню Пуск + Открепить {0, plural, one {} few {предметов} many {предметов} other {предметов}} из начального меню - Закрепить папку(и) на боковую панель + Закрепить {0, plural, one {папку} few {папок} many {папок} other {папок}} на боковой панели - Открепить папку(и) от боковой панели + Открепить {0, plural, one {папку} few {Папки} many {Папки} other {Папки}} на боковой панели Установить как фон рабочего стола @@ -2462,14 +2570,23 @@ Установить выбранное изображение в качестве фона приложения + + Установить шрифт + + + Установить драйвер + + + Установить сертификат + - Установить выбранный(е) шрифт(ы) + Установить выбранный {0, plural, one {шрифт} few {шрифты} many {шрифты} other {шрифты}} - Установить драйвер(ы) с помощью выбранного inf файла(ов) + Установите {0, plural, one {} few {водителей} many {водителей} other {водителей}} используя выбранный inf {0, plural, one {файл} few {файлов} many {файлов} other {файлов}} - Установить выбранный(е) сертификат(ы) + Установите выбранный {0, plural, one {сертификат} few {сертификаты} many {сертификаты} other {сертификаты}} Запустить от имени администратора @@ -2484,28 +2601,28 @@ Запустить предпросмотр во всплывающем окне - Создать архив с выбранными элементами + Создайте архив с выбранным {0, plural, one {предмет} few {предметов} many {предметов} other {предметов}} - Создать 7-zip архив с выбранными элементами + Создайте архив 7z с выбранным {0, plural, one {} few {предметов} many {предметов} other {предметов}} - Создать zip архив с выбранными элементами + Создайте zip-архив с выбранным {0, plural, one {} few {предметов} many {предметов} other {предметов}} - Извлечь элементы из выбранного(ых) архива(ов) в любую папку + Извлечь выбранное {0, plural, one {архив} few {архивы} many {архивы} other {архивы}} в любую папку - Извлечь элементы из выбранного(ых) архива(ов) в текущую папку + Извлечь выбранное {0, plural, one {архив} few {архивы} many {архивы} other {архивы}} в текущую папку - Извлечь элементы из выбранного(ых) архива(ов) в новую папку + Извлечь выбранное {0, plural, one {архив} few {архивы} many {архивы} other {архивы}} в новую папку - Повернуть выбранное(ые) изображение(ия) влево + Вращайте выбранное {0, plural, one {} few {изображения} many {изображения} other {изображения}} слева - Повернуть выбранное(ые) изображение(ия) вправо + Вращайте выбранное {0, plural, one {} few {изображения} many {изображения} other {изображения}} вправо Открыть страницу настроек @@ -2525,8 +2642,8 @@ Переключить на подробный вид - - Режим плиток + + Переключиться на просмотр в виде карточек Переключиться на вид списка @@ -2565,7 +2682,7 @@ Сортировать объекты по их статусу - Сортировать элементы по тэгам + Сортировать элементы по меткам Сортировать объекты по исходной папке @@ -2604,7 +2721,7 @@ Группировать объекты по их статусу - Группировать элементы по тэгам + Группировать элементы по меткам Группировать объекты по исходной папке @@ -2628,10 +2745,10 @@ Открыть новую вкладку - Переход назад в истории навигации + Вернуться назад - Переход вперед в истории навигации + Вернуться вперёд Перейти на одну папку назад @@ -2660,8 +2777,11 @@ Закрыть все вкладки кроме выбранной + + Закрыть все вкладки включая текущую вкладку + - Восстановить последнюю закрытую вкладку + Восстановить закрытую вкладку Переход на предыдущую вкладку @@ -2673,13 +2793,13 @@ Закрыть текущую вкладку - Close active pane + Закрыть текущий pane - Focus other pane + Фокус на другую вкладку - Switch focus to the non active pane + Переключить фокус на другой pane Переключить панель @@ -2884,16 +3004,16 @@ Корзина - Без тегов + Без меток - Перемещает на предыдущую вкладку + Предыдущая вкладка - Перемещает на следующую вкладку + Следующая вкладка - Закрывает текущую вкладку + Закрыть вкладку Изменить путь @@ -2923,7 +3043,7 @@ Отображать чекбоксы при выборе элементов - Сфокусироваться на строчке поиска + Изменить путь в OmniBar Создать новый элемент @@ -2938,7 +3058,7 @@ Удалить навсегда - Удалить элемент(ы) навсегда + Удалить из списка {0, plural, one {} few {предметы} many {предметы} other {предметы}} навсегда Воспроизвести выбранные медиа файлы @@ -3004,7 +3124,7 @@ Группировать элементы по годам с даты изменения - Нажмите 'Расширенные права' для продолжения. + Нажмите «Расширенные права» для продолжения. Вы должны иметь разрешение на чтение для просмотра свойств этого элемента. @@ -3027,6 +3147,15 @@ У вас есть незакоммиченные изменения в этой ветке. Что вы хотите с ними сделать? + + У вас есть текущее слияние с неразрешенными конфликтами. Пожалуйста, устраните конфликты или прервите слияние для переключения ветвей. + + + Прервать слияние и переключиться на '{0}' + + + Оставайтесь на '{0}' и разрешите конфликты + Сменить ветку @@ -3055,14 +3184,20 @@ Переключиться на новую ветку - Создать папку с выбранными в данный момент элементами + Создайте папку с выбранным {0, plural, one {} few {Предметы} many {Предметы} other {Предметы}} - Открыть свойства + Свойства + + + Открыть свойства Проводника Windows Открыть окно свойств + + Открыть окно свойств Проводника Windows + Локальные @@ -3070,7 +3205,7 @@ Удалённые - Translate on Crowdin + Помочь с переводом на Crowdin Fetch @@ -3081,6 +3216,9 @@ Запустить git fetch + + Клонировать git-репозиторий + Запустить git pull @@ -3088,7 +3226,7 @@ Предпросмотр - Статус git + Статус Git Ошибка с Git @@ -3125,7 +3263,7 @@ Mica Alt - Материал фона + Фон Сплошной @@ -3157,17 +3295,17 @@ Files не может получить доступ к GitHub сейчас. - - Открыть папку в VS Code + + Открыть папку в {0} - - Открыть текущую папку в Visual Studio Code + + Открыть текущий каталог в {0} - - Открыть репозиторий в VS Code + + Открыть репозиторий в {0} - - Открыть корень репозитория Git в Visual Studio Code + + Открыть корень репозитория Git в {0} Копировать код @@ -3194,7 +3332,7 @@ Инициализировать репозиторий - Инициализировать Git репозиторий + Сделать текущую папку git-репозиторием Убрать Имя Репозитория @@ -3209,16 +3347,16 @@ Сортировка элементов по пути - Открыть папку в новой панели + Открыть выбранную папку в новом pane - Открыть папку в новой вкладке + Открыть выбранную папку в новой вкладке - Открыть папку в новом окне + Открыть выбранную папку в новом окне - Открыть всё + Открыть все Открыть все отмеченные элементы @@ -3240,10 +3378,10 @@ Команда '{0}' не готова к выполнению. - Список команд + Командны в OmniBar - Открыть список команд + Открыть командны в OmniBar Начало через: @@ -3373,6 +3511,9 @@ Обработка элементов... + + Поиск элементов... + Скорость: @@ -3385,61 +3526,129 @@ Shown in a StatusCenter card. - Сжато {0} элементов в "{1}" + Сжатый {0, plural, one {# элемент} few {# предметов} many {# предметов} other {# предметов}} до "{1}" Shown in a StatusCenter card. - Сжато {0} элементов из "{1}" в "{2}" + Сжатый {0, plural, one {# предмет} few {# предметов} many {# предметов} other {# предметов}} от "{1}" до "{2}" Shown in a StatusCenter card. - Ошибка сжатия {0} элементов в "{1}" + Ошибка сжатия {0, plural, one {# предмет} few {# предметов} many {# предметов} other {# предметов}} до "{1}" Shown in a StatusCenter card. - Не удалось сжать {0} элементов из "{1}" в "{2}" + Не удалось сжать {0, plural, one {# предмет} few {# предметов} many {# предметов} other {# предметов}} из "{1}" до "{2}" Shown in a StatusCenter card. - Сжатие {0} элементов в "{1}" + Сжатие {0, plural, one {# предмет} few {# предметов} many {# предметов} other {# предметов}} до "{1}" Shown in a StatusCenter card. - Сжатие {0} элементов из "{1}" в "{2}" + Сжатие {0, plural, one {# предмет} few {# предметов} many {# предметов} other {# предметов}} от "{1}" до "{2}" + Shown in a StatusCenter card. + + + Клонирование {0} в "{1}" отменено + Shown in a StatusCenter card. + + + Клонирование {0} отменено из "{1}" в "{2}" + Shown in a StatusCenter card. + + + Клонировано "{0}" в "{1}" + Shown in a StatusCenter card. + + + Клонировано {0} из "{1}" в "{2}" + Shown in a StatusCenter card. + + + Ошибка клонирования "{0}" в "{1}" + Shown in a StatusCenter card. + + + Не удалось клонировать {0} из "{1}" в "{2}" + Shown in a StatusCenter card. + + + Клонирование "{0}" в "{1}" + Shown in a StatusCenter card. + + + Клонирование {0} из "{1}" в "{2}" + Shown in a StatusCenter card. + + + Установка {0} шрифтов отменена + Shown in a StatusCenter card. + + + Установка {0, plural, one {# шрифт} few {# шрифтов} many {# шрифтов} other {# шрифтов}} из "{1}" + Shown in a StatusCenter card. + + + Установлено {0, plural, one {# шрифт} few {# шрифтов} many {# шрифтов} other {# шрифтов}} + Shown in a StatusCenter card. + + + Установлено {0, plural, one {# шрифт} few {# шрифтов} many {# шрифтов} other {# шрифтов}} из "{1}" + Shown in a StatusCenter card. + + + Ошибка установки {0, plural, one {# шрифт} few {# шрифтов} many {# шрифтов} other {# шрифтов}} + Shown in a StatusCenter card. + + + Не удалось установить {0, plural, one {# шрифт} few {# шрифтов} many {# шрифтов} other {# шрифтов}} из "{1}" + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} few {# fonts} many {# fonts} other {# fonts}} + Shown in a StatusCenter card. + + + Установка {0, plural, one {# шрифта} few {# шрифтов} many {# шрифтов} other {# шрифтов}} от "{1}" Shown in a StatusCenter card. - Копирование {0} элементов в "{1}" отменено + Копирование {0, plural, one {# предмета} few {# предметов} many {# предметов} other {# предметов}} в "{1}" Shown in a StatusCenter card. - Копирование {0} элементов из "{1}" в "{2}" отменено + Копирование {0, plural, one {# предмет} few {# предметов} many {# предметов} other {# предметов}} из "{1}" до "{2}" Shown in a StatusCenter card. - Скопировано {0} элементов в "{1}" + Скопировано {0, plural, one {# предмет} few {# предметов} many {# предметов} other {# предметов}} в "{1}" Shown in a StatusCenter card. - Скопировано {0} элементов из "{1}" в "{2}" + Скопировано {0, plural, one {# предмет} few {# предметов} many {# предметов} other {# предметов}} от "{1}" до "{2}" Shown in a StatusCenter card. - Ошибка копирования {0} элементов в "{1}" + Ошибка копирования {0, plural, one {# элемента} few {# элементов} many {# элементов} other {# элементов}} до "{1}" Shown in a StatusCenter card. - Не удалось скопировать {0} элементов из "{1}" в "{2}" + Не удалось скопировать {0, plural, one {# предмет} few {# предметов} many {# предметов} other {# предметов}} из "{1}" в "{2}" Shown in a StatusCenter card. - Копирование {0} элементов в "{1}" + Копирование {0, plural, one {# предмета} few {# предметов} many {# предметов} other {# предметов}} в "{1}" Shown in a StatusCenter card. - Копирование {0} элементов из "{1}" в "{2}" + Копирование {0, plural, one {# предмета} few {# предметов} many {# предметов} other {# предметов}} от "{1}" до "{2}" Shown in a StatusCenter card. + + Обнаружен {0, plural, one {# предмет} other {# предметы}} + Shown in a StatusCenter card during file discovery phase. + Извлечение "{0}" в "{1}" отменено Shown in a StatusCenter card. @@ -3473,55 +3682,55 @@ Shown in a StatusCenter card. - Удаление {0} элементов из "{1}" отменено + Удаление {0, plural, one {# предмет} few {# предметов} many {# предметов} other {# предметов}} из "{1}" Shown in a StatusCenter card. - Удалено {0} элементов из "{1}" + Удалено {0, plural, one {# Предмет} few {# Предметов} many {# Предметов} other {# Предметов}} из "{1}" Shown in a StatusCenter card. - Ошибка удаления {0} элементов из "{1}" + Ошибка удаления {0, plural, one {# элемента} few {# элементов} many {# элементов} other {# элементов}} из "{1}" Shown in a StatusCenter card. - Не удалось удалить {0} элементов из {1}" + Не удалось удалить {0, plural, one {# элемент} few {# # предметов} many {# # предметов} other {# # предметов}} из "{1}" Shown in a StatusCenter card. - Удаление {0} элементов из "{1}" + Удаление {0, plural, one {# элемента} few {# элементов} many {# элементов} other {# элементов}} из "{1}" Shown in a StatusCenter card. - Перемещение {0} элементов в "{1}" отменено + Перемещение {0, plural, one {# предмет} few {# предметов} many {# предметов} other {# предметов}} в "{1}" Shown in a StatusCenter card. - Перемещение {0} элементов из "{1}" в "{2}" отменено + Перемещение {0, plural, one {# предмет} few {# предметов} many {# предметов} other {# предметов}} из "{1}" в "{2}" Shown in a StatusCenter card. - Перемещено {0} элементов в "{1}" + {0, plural, one {# Предмет} few {# Предметов} many {# Предметов} other {# Предметов}} до "{1}" Shown in a StatusCenter card. - Перемещено {0} элементов из "{1}" в "{2}" + {0, plural, one {# Предмет} few {# Предметов} many {# Предметов} other {# Предметов}} от "{1}" до "{2}" Shown in a StatusCenter card. - Перемещение {0} элементов в "{1}" + Перемещение {0, plural, one {# предмет} few {# предметов} many {# предметов} other {# предметов}} до "{1}" Shown in a StatusCenter card. - Перемещение {0} элементов из "{1}" в "{2}" + Перемещение {0, plural, one {# предмет} few {# предметов} many {# предметов} other {# предметов}} от "{1}" до "{2}" Shown in a StatusCenter card. - Ошибка перемещения {0} элементов в "{1}" + Ошибка перемещения {0, plural, one {# предмета} few {# предметов} many {# предметов} other {# предметов}} к "{1}" Shown in a StatusCenter card. - Не удалось переместить {0} элементов из "{1}" в "{2}" + Не удалось переместить {0, plural, one {# элемент} few {# предметов} many {# предметов} other {# предметов}} из "{1}" в "{2}" Shown in a StatusCenter card. @@ -3549,7 +3758,7 @@ Shown in a StatusCenter card. - {0}/{1} элементов обработано + {0}/{1, plural, one {# # предмет} few {# предметов} many {# предметов} other {# предметов}} обработано Shown in a StatusCenter card. Used as "8/20 items processed" @@ -3567,6 +3776,9 @@ Не удалось установить обои рабочего стола + + Не удалось открыть файл настроек + Удалить Git ветку @@ -3586,13 +3798,13 @@ Вход - В настоящее время теги совместимы только с дисками, отформатированными в NTFS. + В настоящее время метки совместимы только с дисками, отформатированными в NTFS. - Произошла ошибка при применении этого тега + Произошла ошибка при применении этой метки - Распаковать элементы из выбранных архивов в текущую папку для архива с одним элементом или в новую папку для архива с несколькими элементами + Извлечь выбранный {0, plural, one {архив} few {архивы} many {архивы} other {архивы}} здесь для одного предмета или в новую папку для нескольких предметов Распаковать здесь (Smart) @@ -3652,14 +3864,11 @@ Файлы все еще работают в фоновом режиме для повышения производительности запуска. - Где были файлы? + Где же Files? Вопросы и обсуждение - - Доп. параметры недоступны для плиточного макета. - Компактный Used to describe layout sizes @@ -3740,7 +3949,7 @@ Эта клавиша уже используется, выберите другую клавишу для продолжения. - The key binding you choose cannot be used, please try again using a different key binding. + Выбранная вами комбинация клавиш не может быть использована, пожалуйста, попробуйте другую комбинацию. Настроенный @@ -3821,6 +4030,10 @@ Файл ICO This is the friendly name for ICO files. + + ICL Файл + This is the friendly name for ICL files. + Zip файл This is the friendly name for ZIP files. @@ -3829,8 +4042,12 @@ Растровые файлы This is the friendly name for bitmap files. + + Файлы изображений + This is the friendly name for image files. + - Num + Номер Местоположение сети @@ -3848,58 +4065,316 @@ Редактировать выбранный файл в Блокноте - Dimensions: + Размеры: - Status: + Статус: - Show Toolbar - Setting that controls if the Toolbar is shown in the main view + Показать панель инструментов + Setting that controls if the toolbar is shown in the main view - Tab actions menu + Меню действий вкладки - - Add vertical pane + + Разделить по вертикали - Add a vertical pane + Разделить панель вертикально - - Add horizontal pane + + Разделить по горизонтали - - Add a horizontal pane + + Разделить панель горизонтально - Arrange vertically + Расположить вертикально - Arrange panes vertically + Расположить панели вертикально - Arrange horizontally + Расположить горизонтально - Arrange panes horizontally + Расположение панелей горизонтально - - Add pane + + Раздельная панель - Show tab actions button in the title bar + Показать кнопку действий вкладки в заголовке - Arrange panes + Организовать панели + + + Направление разделения панелей по умолчанию - - Default pane arrangement + + Двухпанельный режим - Horizontal + Горизонтальный - Vertical + Вертикальный + + + Показывать иконку Files в системном трее + + + Потоки процессора + + + Перейти на главную страницу + + + Панели инструментов + + + ID пользователя + + + Массовое переименование + + + Сжать + + + Показать опцию создания альтернативного потока данных + + + Создать альтернативный поток данных + + + Создайте альтернативный поток данных для выбранного {0, plural, one {} few {Предметы} many {Предметы} other {Предметы}} + + + Введите название потока данных + + + Произошла ошибка при создании альтернативного потока данных + + + Обратите внимание, что альтернативные потоки данных работают только на дисках, отформатированных под NTFS. + + + Альтернативные потоки данных в данный момент скрыты + + + Вы хотите отобразить альтернативные потоки данных? Вы можете изменить этот параметр в любое время из настроек файлов и папок. + + + Управление метками + + + Доступно + + + Всего + + + Всегда переключать фокус на только что созданную вкладку + + + Переключить отображение полки + + + Переключить видимость панели полки + + + Показать переключатель панели полки в адресной строке + + + Полка + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + Очистить элементы + + + Убрать из полки + + + Добавить на полку + Tooltip that displays when dragging items to the Shelf Pane + + + Введите хэш для сравнения + Placeholder that appears in the compare hash text box + + + Совпадения {0} + Appears when two compared hashes match, e.g. "Matches SHA256" + + + Совпадения не найдены + Appears when two compared hashes don't match + + + Путь или псевдоним + + + Недопустимый путь + + + Тестирование интеграции + + + {0} не может быть найден. Пожалуйста, проверьте ваши настройки и повторите попытку. + + + Настроенный IDE не может быть найден + + + Открыть настройки + + + Visual Studio Code + + + Введите путь или запустите псевдоним + + + Пожалуйста, введите имя для IDE + + + Клонировать репозиторий + Clone repo dialog title + + + Клонировать + Primary action button in the clone repo dialog + + + Ссылка на репозиторий + URL textbox header in the clone repo dialog + + + Невозможно клонировать репозиторий + Cannot clone repo dialog title + + + Сравнить файл + Button that appears in file hash properties that allows the user to compare two files + + + Размер формата + + + Двоичный + + + Десятичный + + + Вы можете добавить разделы в боковую панель, нажав правой кнопкой мыши и выбрав разделы, которые вы хотите добавить + + + Перетащите сюда файлы или папки для взаимодействия с разными вкладками + + + Введите путь для перехода... + + + Найти функции и команды... + + + Поиск файлов и папок... + + + Во время работы с файлами + + + Показать кнопку статуса центра + + + Статус центра прогресса кольца + Screen reader name for the status center progress ring + + + Файлы иконок + This is the friendly name for a variety of different icon files. + + + Показать свернутые цепочки навигации + + + Показать папки в {0} + + + Показывать папки на Главной + + + Нет команд, содержащих {0} + + + Показать больше + + + Фильтрация для + + + Имя файла + + + Подписи + + + Список подписей + + + Кем выдана: + + + Кому выдана: + + + Действителен c: + + + Действителен до: + + + Подпись не найдена. + + + Показать опцию открытия папок в Windows Terminal + + + Открыть файл журнала + + + Открыть файл журнала в редакторе по умолчанию + + + Открыть папку файла журнала в файловом менеджере по умолчанию + + + Не удалось открыть файл журнала + + + Показать строку состояния + + + Только в виде столбцов + + + Новый {0} + + + Enable smooth scrolling + + + Scrolling + + + Show option to Pin to Sidebar + + + Show option to Pin to the Start Menu \ No newline at end of file diff --git a/src/Files.App/Strings/sk-SK/Resources.resw b/src/Files.App/Strings/sk-SK/Resources.resw index f4a72d7feb72..c267120cb50b 100644 --- a/src/Files.App/Strings/sk-SK/Resources.resw +++ b/src/Files.App/Strings/sk-SK/Resources.resw @@ -123,9 +123,15 @@ Kopírovať cestu + + Kopírovať cestu položky + Kopírovať cestu s úvodzovkami + + Kopírovať cestu vybratej položky v úvodzovkách + Prehľadávať @@ -160,7 +166,7 @@ Preskočiť - Vybrať všetko + Označiť všetko Invertovať výber @@ -196,7 +202,7 @@ O aplikácii - Open source + Otvorený zdrojový kód Formát dátumu @@ -249,6 +255,9 @@ Vložiť + + Vložiť odkaz + Nová položka @@ -291,8 +300,8 @@ Zadajte názov položky - - Nastaviť meno + + Vytvoriť nový Svetlý @@ -300,6 +309,9 @@ Tmavý + + Použiť systémové nastavenia + Aplikácia @@ -316,13 +328,13 @@ Tento priečinok je prázdny. - Späť (Alt + šípka doľava) + Späť - Dopredu (Alt + šípka doprava) + Dopredu - Hore (Alt + šípka hore) + Hore Obnoviť @@ -385,40 +397,40 @@ Iba na čítanie - {0, plural, one {# day ago} other {# days ago}} + {0, plural, one {Pred # dňom} other {Pred # dňami}} - 1 day ago + Pred 1 dňom - {0} days ago + Pred {0} dňami - {0, plural, one {# hour ago} other {# hours ago}} + {0, plural, one {Pred # hodinou} other {Pred # hodinami}} - 1 hour ago + Pred 1 hodinou - {0} hours ago + Pred {0} hodinami - {0, plural, one {# minute ago} other {# minutes ago}} + {0, plural, one {Pred # minútou} other {Pred # minútami}} - 1 minute ago + Pred 1 minútou - {0} minutes ago + Pred {0} minútami - {0, plural, one {# second ago} other {# seconds ago}} + {0, plural, one {Pred # sekundou} other {Pred # sekundami}} - 1 second ago + Pred 1 sekundou - {0} seconds ago + Pred {0} sekundami Teraz @@ -439,11 +451,11 @@ Názov položky je neplatný - {0, plural, one {# item selected} few {# items selected} many {# items selected} other {# items selected}} + {0, plural, one {# položka označená} few {# položky označené} other {# položiek označených}} ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural - {0, plural, one {item} few {items} many {items} other {items}} + ť {0, plural, one {položka} few {položky} other {položiek}} ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural @@ -513,10 +525,7 @@ Presunúť priečinok sem - Status - - - Predvolené vo Windowse + Stav Žiadne výsledky @@ -530,6 +539,9 @@ Prekopírovať do {0} + + Prekopírovať do {0} + Vytvoriť odkaz @@ -618,7 +630,7 @@ Aby sa aplikovali nastavenia jazyka, je potrebné aplikáciu reštartovať. Chcete ju reštartovať? - Podporte nás na GitHube + Sponzorujte nás na GitHube Nepodarilo sa vytvoriť túto položku @@ -894,10 +906,10 @@ Prenosová rýchlosť - Audio Encoding Bitrate + Prenosová rýchlosť zvuku - Video Encoding Bitrate + Prenosová rýchlosť videa Kompresia @@ -1000,6 +1012,9 @@ + + Search results for `{0}` + Detaily @@ -1010,13 +1025,13 @@ Zobraziť chránené systémové súbory - Synchronizovať predvoľby rozvrhnutie a radenie naprieč adresármi + Synchronizovať predvoľby rozvrhnutia a radenia naprieč priečinkami Prizpôsobiť kontextové menu - Originálne umiestnenie + Pôvodné umiestnenie Pridať @@ -1054,9 +1069,6 @@ Detaily (Ctrl+Shift+1) - - Dlaždice (Ctrl+Shift+2) - Odstránené @@ -1079,13 +1091,28 @@ Prepnite panel detailov, aby zobrazoval základné vlastnosti súborov - Prepnite info panel + Prepnúť informačný panel - Prepnúť detail/náhlaď + Zobraziť/skryť panel detailov/náhľadov - Toggle the Toolbar + Prepnúť panel s nástrojmi + + + Prepnúť viditeľnosť panelu s nástrojmi + + + Toggle filter header + + + Toggle visibility of the filter header + + + Toggle dual pane + + + Toggle dual pane mode Žiaden náhľad k dispozícii @@ -1172,7 +1199,7 @@ Datum vytvorenia - Daľšie možnosti + Ďalšie možnosti Mapovať sieťový disk @@ -1240,26 +1267,32 @@ Otvoriť senzor úložiska + + Otvoriť stránku Inteligentného úložiska v nastaveniach Windows + + + Vyčistiť + Kopírovať - Copy {0, plural, one {item} other {items}} + Kopírovať {0, plural, one {položku} few {položky} other {položiek}} - Delete {0, plural, one {item} other {items}} + Zmazať {0, plural, one {položku} other {položky}} Presunúť - {0, plural, one {One item will be moved} other {# items will be moved}} + {0, plural, one {Jedna položka bude presunutá} few {# položky budou presunuté} other {# položiek bude presunutých}} - Move {0, plural, one {item} other {items}} + Presunúť {0, plural, one {položku} other {položky}} - {0, plural, one {One item will be deleted} other {# items will be deleted}} + {0, plural, one {Jedna položka bude zmazaná} few {# položky budou zmazané} other {# položiek bude zmazaných}} Pokračovať @@ -1355,19 +1388,19 @@ Včera - Začiatkom tohto týždňa + Skôr tento týždeň Minulý týždeň - Začiatkom tohto mesiaca + Skôr tento mesiaca Minulý mesiac - Začiatkom tohto roku + Skôr tohto roku Minulý rok @@ -1382,7 +1415,7 @@ Počet položiek: {0} - Znovu otvoriť zatvorenú kartu + Znovu otvoriť zavretú kartu Premenovať @@ -1589,7 +1622,7 @@ Nahraďte všetky položky povolení podriadených objektov položkami zdedených povolení z tohto objektu - Close active pane + Zavrieť panel Zadajte kompaktné prekrytie @@ -1648,15 +1681,6 @@ Domov - - Ctrl+Shift+1 - - - Ctrl+Shift+2 - - - Ctrl+Shift+6 - Rozbalenie archívu bolo úspešne dokončené. @@ -1714,8 +1738,8 @@ Stĺpce - - Dlaždice + + Karty Otvoriť priečinky na novej karte @@ -1751,7 +1775,7 @@ Značka súboru - Otvoriť položky jedným kliknutím + Open files with a single click Cesta ku zložke @@ -1820,41 +1844,56 @@ Spustiť v rozlíšení 640 x 480 pixlov - Override high DPI scaling behavior + Prepísať správanie pri škálovaní vysokého DPI - Reduced color mode + Režim redukovaných farieb - Register this program for restart + Registrovať tento program pre reštart + + + Spustenie okna + + + Normálne okno + + + Minimalizované + + + Maximalizované Spustiť ako správca - Run compatibility troubleshooter + Spustiť riešenie problémov - Use DPI settings of the main monitor + Použiť nastavenia DPI hlavnej obrazovky Neprispôsobovať DPI - Do not override DPI + Neprepisovať DPI Režim kompatibility - No reduced color + Bez redukovaných farieb - Third party libraries + Knižnice tretích strán Toto nastavenie upravuje systémové súbory a môže mať neočakávané vedľajšie účinky na vaše zariadenie. Vývojári nenesú žiadnu zodpovednosť v prípade, že sa v dôsledku toho vyskytne problém. Pokračovanie v tejto možnosti znamená uznanie rizík spojených s touto akciou. + + The flatten operations are permanent and not recommended. Continue at your own risk. + Vytvoriť knižnicu @@ -1933,6 +1972,9 @@ Zavrieť ostatné karty + + Zavrieť všetky karty + Hudba @@ -1970,7 +2012,7 @@ Nastaviť ako prezentáciu na pracovnej ploche - Size all columns to fit + Nastaviť veľkosť všetkých stĺpcov tak aby sa vošli Automatically choose the best layout @@ -1987,11 +2029,23 @@ Správania - - Ohodnotiť Files + + Ahoj! + + + Užívate si Files? Zvážte prosím ohodnotenie v Microsoft Store. + + + Užívate si Files? Zvážte prosím podporu projektu na GitHube + + + Sponzor - - Chceli by ste ohodnotiť Files? + + Ohodnoďte nás + + + Zrušiť Nastaviť ako pozadie @@ -2020,8 +2074,8 @@ Obrazovka uzamknutia - - Otvoriť priečinky jedným kliknutím v rozložení stĺpcov + + Open folders with a single click Otváranie položiek @@ -2029,6 +2083,21 @@ Zabaliť + + Move all contents from subfolders into the selected location + + + Zlúčiť priečinok + + + Zlúčiť + + + Flattening a folder will move all contents from its subfolders to the selected location. This operation is permanent and cannot be undone. By using this experimental feature, you acknowledge the risk and agree not to hold the Files team responsible for any data loss. + + + Zobraziť možnosti zlúčenia + Vybrať súbory a priečinky, keď na ne umiestnite kurzor myši @@ -2039,7 +2108,7 @@ Obnoviť všetky položky - Chcete obnoviť {0} vybraných položiek? + Do you want to restore the {0, plural, one {selected item} other {{0} selected items}}? Obnoviť výber @@ -2062,6 +2131,12 @@ Heslo archívu + + Kódovánie + + + {0} (zistených) + Cesta @@ -2104,9 +2179,24 @@ Rozdeľovacia veľkosť - + Nerozdeľovať + + Auto + + + Dictionary size + + + Word size + + + Estimated memory usage: {0} + + + Available memory: {0} + CD @@ -2161,8 +2251,14 @@ Upraviť nastavenia súborov - - Čo je nové + + Open settings file in your default editor + + + Poznámky k vydaniu + + + Otvoriť poznámky k vydaniu Vytvorenie odkazu na tomto mieste vyžaduje administrátorské oprávnenia @@ -2201,7 +2297,7 @@ Rýchly prístup - Enter your credentials to connect to: {0} + Zadajte vaše prihlasovacie údaje pre pripojenie k: {0} Zadajte poverenia pre server @@ -2243,25 +2339,22 @@ Možnosti kontextovej ponuky - Zobraziť prípony súborov - - - Zobraziť skryté položky + Prípony súborov - Formátovať... + Formátovať Pomoc - Prepnúť režim celej obrazovky + Celá obrazovka Ste si istý, že chcete vymazať túto značku? - Prehrať všetko + Prehrať Výška @@ -2321,7 +2414,7 @@ Zväčšiť veľkosť - Prepnúť smer radenia + Sort direction Neplatné meno @@ -2333,34 +2426,37 @@ Videá - Launch preview popup + Preview popup Toggle compact overlay - Otvoriť webovú stránku s nápovedou v prehliadači + Open the online help page in your browser - Prepnúť režim celej obrazovky + Toggle full screen mode - Zadajte kompaktné prekrytie + Enter compact overlay mode - Ukončite kompaktné prekrytie + Exit compact overlay mode - Toggle compact overlay + Toggle compact overlay mode - Ísť do vyhľadávacieho poľa + Začať vyhľadávanie v OmniBare - Prepnite, či chcete zobraziť skryté súbory + Toggle visibility of hidden items + + + Toggle visibility of dot files - Prepnite, či chcete zobraziť prípony súborov + Toggle visibility of file extensions Toggle the preview pane to view file previews @@ -2369,52 +2465,64 @@ Prepnite, či chcete zobraziť bočný panel - Skopírovať položky do schránky + Copy selected {0, plural, one {item} other {items}} - Skopírovať cestu vybraných položiek do schránky + Copy path of the current directory + + + Copy path of selected items + + + Copy path of selected items with quotes - Skopírovať cestu vybraných položiek s úvodzkovami do schránky + Copy path of the current directory with quotes - Vystrihnúť položky do schránky + Cut selected {0, plural, one {item} other {items}} - Prilepiť položky zo schránky do aktuálneho priečinka + Paste items to the current folder + + + Paste items to the current folder as shortcuts - Prilepiť položky zo schránky do vybraného priečinka + Paste items to the selected folder + + + Paste to selected folder - Odstrániť položku(y) + Delete selected {0, plural, one {item} other {items}} Vytvoriť nový priečinok - Create new shortcut(s) to selected item(s) + Create new {0, plural, one {shortcut} other {shortcuts}} to selected {0, plural, one {item} other {items}} Create new shortcut to any item - Vysypať kôš + Empty the contents of Recycle Bin Otvoriť menu "formátovať disk" pre zvolenú položku - Obnoviť vybrané položku(y) z koša + Restore selected {0, plural, one {item} other {items}} from recycle bin Obnoviť všetky položky z koša - Otvoriť položku(y) + Open {0, plural, one {item} other {items}} - Otvoriť položku(y) vo vybranej aplikácii + Open {0, plural, one {item} other {items}} with selected application Otvoriť rodičovský priečinok hľadanej položky @@ -2429,28 +2537,28 @@ Vybrať všetky položky - Invertovať výber + Invert selected items - Vymazať výber + Clear selected items Prepnúť výber - Zdieľať vybraný súbor(y) s ostatnými + Share selected {0, plural, one {file} other {files}} with others - Pripnúť do ponuky Štart + Pin {0, plural, one {item} other {items}} to the Start Menu - Odopnúť z ponuky Štart + Unpin {0, plural, one {item} other {items}} from the Start Menu - Pin folder(s) to Sidebar + Pin {0, plural, one {folder} other {folders}} to Sidebar - Unpin folder(s) from Sidebar + Unpin {0, plural, one {folder} other {folders}} from Sidebar Nastaviť vybraný obrázok ako pozadie plochy @@ -2464,14 +2572,23 @@ Set selected picture as the app background + + Install font + + + Install driver + + + Install certificate + - Nainštalovať vybrané písmo(a) + Install selected {0, plural, one {font} other {fonts}} - Nainštalovať ovládač(e) použitým vybraných inf súborov + Install {0, plural, one {driver} other {drivers}} using selected inf {0, plural, one {file} other {files}} - Nainštalovať vybraný certifikát(y) + Install selected {0, plural, one {certificate} other {certificates}} Spustiť vybranú aplikáciu ako správca @@ -2486,28 +2603,28 @@ Launch preview in popup window - Vytvoriť archív z vybraných položiek + Create archive with selected {0, plural, one {item} other {items}} - Vytvoriť 7z archív z vybraných položiek + Create 7z archive with selected {0, plural, one {item} other {items}} - Vytvoriť zip archív z vybraných položiek + Create zip archive with selected {0, plural, one {item} other {items}} - Rozbaliť položky z vybraných archívov do ľubovóľneho priečinka + Extract selected {0, plural, one {archive} other {archives}} to any folder - Rozbaliť položky z vybraných archívov do aktuálneho priečinka + Extract selected {0, plural, one {archive} other {archives}} to the current folder - Rozbaliť položky z vybraných archívov do nového priečinka + Extract selected {0, plural, one {archive} other {archives}} to new folder - Otočiť vybrané obrázky do ľava + Rotate selected {0, plural, one {image} other {images}} to the left - Otočiť vybrané obrázky do prava + Rotate selected {0, plural, one {image} other {images}} to the right Otvoriť stránku s nastavením @@ -2527,8 +2644,8 @@ Prepnuť na zobrazenie podrobností - - Switch to tiles view + + Switch to cards view Prepnúť na zobrazenie zoznamu @@ -2630,10 +2747,10 @@ Otvoriť novú kartu - Navigate backward in navigation history + Navigate backward - Navigate forward in navigation history + Navigate forward Navigate up one directory @@ -2662,8 +2779,11 @@ Close tabs other than selected tab + + Close all tabs including the current tab + - Otvoriť naposledy zatvorenú kartu + Reopen recently closed tab Move to the previous tab @@ -2675,13 +2795,13 @@ Zavrieť aktuálnu kartu - Close active pane + Close the active pane Focus other pane - Switch focus to the non active pane + Switch focus to the other pane Toggle the sidebar @@ -2889,13 +3009,13 @@ Neoznačené - Moves to the previous tab + Previous tab - Moves to the next tab + Next tab - Zavrieť aktuálnu kartu + Close tab Upraviť cestu @@ -2925,7 +3045,7 @@ Zobraziť začiarkávacie políčko pri výbere položiek - Focus path bar + Edit path in the OmniBar Vytvoriť novú položku @@ -2940,7 +3060,7 @@ Delete permanently - Delete item(s) permanently + Delete selected {0, plural, one {item} other {items}} permanently Prehrať vybrané mediálne súbory @@ -3029,6 +3149,15 @@ V tejto vetvy máte nevykonané zmeny. Čo s nimi chcete urobiť? + + You have an ongoing merge with unresolved conflicts. Please resolve the conflicts or abort the merge to switch branches. + + + Abort merge and switch to '{0}' + + + Stay on '{0}' and resolve conflicts + Switch branch @@ -3057,14 +3186,20 @@ Prepnúť na novú vetvu - Vytvoriť priečinok s aktuálne vybranými položkami + Create a folder with the currently selected {0, plural, one {item} other {items}} - Otvoriť vlastnosti + Vlastnosti + + + Open File Explorer properties Otvoriť okno vlastností + + Open File Explorer properties window + Lokálne @@ -3083,6 +3218,9 @@ Run git fetch + + Clone a git repo + Run git pull @@ -3118,16 +3256,16 @@ Git - Acrylic + Akrylový Mica - Mica Alt + Mica alternatívny - Backdrop Material + Backdrop Solid @@ -3159,17 +3297,17 @@ Súbory nemôžu momentálne získať prístup do GitHub-u. - - Otvoriť priečinok vo VS Code + + Open folder in {0} - - Otvoriť aktuálny adresár vo Visual Studio Code + + Open the current directory in {0} - - Otvoriť repozitár vo VS Code + + Open repo in {0} - - Open the root of the Git repo in Visual Studio Code + + Open the root of the Git repo in {0} Kopírovať kód @@ -3196,7 +3334,7 @@ Inicializovať repozitár - Inicializovať Git repozitár + Initialize current folder as a git repository Názov vzdialeného repozitára @@ -3211,13 +3349,13 @@ Usporiadať položky podľa cesty - Open directory in new pane + Open selected directory in a new pane - Otvoriť priečinok na novej karte + Open selected directory in a new tab - Otvoriť priečinok v novom okne + Open selected directory in a new window Otvoriť všetko @@ -3242,10 +3380,10 @@ The '{0}' command is not ready to be executed. - Command palette + Command Palette - Open command palette + Open Command Palette in the OmniBar Začať v: @@ -3375,6 +3513,9 @@ Spracuvávam položky... + + Discovering items... + Rýchlosť: @@ -3387,61 +3528,129 @@ Shown in a StatusCenter card. - Compressed {0} item(s) to "{1}" + Compressed {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Compressed {0} item(s) from "{1}" to "{2}" + Compressed {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error compressing {0} item(s) to "{1}" + Error compressing {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to compress {0} item(s) from "{1}" to "{2}" + Failed to compress {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Compressing {0} item(s) to "{1}" + Compressing {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Compressing {0} item(s) from "{1}" to "{2}" + Compressing {0, plural, one {# item} other {# items}} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Canceled cloning {0} to "{1}" + Shown in a StatusCenter card. + + + Canceled cloning {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Cloned "{0}" to "{1}" + Shown in a StatusCenter card. + + + Cloned {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Error cloning "{0}" to "{1}" + Shown in a StatusCenter card. + + + Failed to clone {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Cloning "{0}" to "{1}" + Shown in a StatusCenter card. + + + Cloning {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Canceled installing {0} fonts + Shown in a StatusCenter card. + + + Canceled installing {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Error installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Failed to install {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} from "{1}" Shown in a StatusCenter card. - Canceled copying {0} item(s) to "{1}" + Canceled copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Canceled copying {0} item(s) from "{1}" to "{2}" + Canceled copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Copied {0} item(s) to "{1}" + Copied {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Copied {0} item(s) from "{1}" to "{2}" + Copied {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error copying {0} item(s) to "{1}" + Error copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to copy {0} item(s) from "{1}" to "{2}" + Failed to copy {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Kopíruje sa {0} položiek do {1}" + Copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Presúva sa {0} položiek z {1} do {2}" + Copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. + + Discovered {0, plural, one {# item} other {# items}} + Shown in a StatusCenter card during file discovery phase. + Canceled extracting "{0}" to "{1}" Shown in a StatusCenter card. @@ -3475,55 +3684,55 @@ Shown in a StatusCenter card. - Canceled deleting {0} item(s) from "{1}" + Canceled deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Deleted {0} item(s) from "{1}" + Deleted {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Chyba pri odstraňovaní {0} položiek z „{1}“ + Error deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Nepodarilo sa odstrániť {0} položiek z "{1}" + Failed to delete {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Odstraňuje sa {0} položiek z {1}" + Deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Canceled moving {0} item(s) to "{1}" + Canceled moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Canceled moving {0} item(s) from "{1}" to "{2}" + Canceled moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Moved {0} item(s) to "{1}" + Moved {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Moved {0} item(s) from "{1}" to "{2}" + Moved {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Moving {0} item(s) to "{1}" + Moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Moving {0} item(s) from "{1}" to "{2}" + Moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error moving {0} item(s) to "{1}" + Error moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to move {0} item(s) from "{1}" to "{2}" + Failed to move {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. @@ -3551,7 +3760,7 @@ Shown in a StatusCenter card. - Spracované položky {0}/{1} + {0}/{1, plural, one {# item} other {# items}} processed Shown in a StatusCenter card. Used as "8/20 items processed" @@ -3569,6 +3778,9 @@ Nepodarilo sa nastaviť pozadie + + Failed to open the settings file + Delete Git branch @@ -3594,7 +3806,7 @@ There was an error applying this tag - Extract items from selected archive(s) to current folder for single-item archive, or to new folder for multi-item archive + Extract selected {0, plural, one {archive} other {archives}} here for single-item or to new folder for multi-item Rozbaliť tu (chytré) @@ -3603,7 +3815,7 @@ Sort files and folders together - Usporiadať spoločne súbory aj priečinky + Sort files and folders in the same list Usporiadať najprv súbory @@ -3659,9 +3871,6 @@ Questions & discussions - - Additional sizes are not yet available for the Tiles View. - Compact Used to describe layout sizes @@ -3721,7 +3930,7 @@ Layout type - Actions + Akcie Commands @@ -3758,7 +3967,7 @@ Image alignment type - Center + Na stred Image alignment type @@ -3766,7 +3975,7 @@ Image stretch type - Horizontal alignment + Horizontálne zarovnanie Image fit @@ -3823,6 +4032,10 @@ ICO File This is the friendly name for ICO files. + + ICL File + This is the friendly name for ICL files. + Zip File This is the friendly name for ZIP files. @@ -3831,6 +4044,10 @@ Bitmap Files This is the friendly name for bitmap files. + + Image Files + This is the friendly name for image files. + Num @@ -3856,23 +4073,23 @@ Status: - Show Toolbar - Setting that controls if the Toolbar is shown in the main view + Show toolbar + Setting that controls if the toolbar is shown in the main view Tab actions menu - - Add vertical pane + + Split vertically - Add a vertical pane + Split pane vertically - - Add horizontal pane + + Split horizontally - - Add a horizontal pane + + Split pane horizontally Arrange vertically @@ -3886,8 +4103,8 @@ Arrange panes horizontally - - Add pane + + Split pane Show tab actions button in the title bar @@ -3895,8 +4112,11 @@ Arrange panes - - Default pane arrangement + + Default dual pane split direction + + + Dual pane mode Horizontal @@ -3904,4 +4124,259 @@ Vertical + + Show Files icon in the System Tray + + + CPU threads + + + Navigate to the home page + + + Toolbars + + + User ID + + + Bulk rename + + + Compress contents + + + Show option to create alternate data stream + + + Create alternate data stream + + + Create alternate data stream for the selected {0, plural, one {item} other {items}} + + + Enter data stream name + + + There was an error creating the alternate data stream + + + Please note that alternate data streams only work on drives formatted as NTFS. + + + Alternate data streams are currently hidden + + + Would you like to display alternate data streams? You can modify this setting anytime from the files and folders settings page. + + + Manage tags + + + Available + + + Total + + + Always switch focus to newly created tab + + + Toggle shelf pane + + + Toggle the visibility of the shelf pane + + + Show shelf pane toggle in address bar + + + Shelf + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + Clear items + + + Remove from shelf + + + Add to shelf + Tooltip that displays when dragging items to the Shelf Pane + + + Enter a hash to compare + Placeholder that appears in the compare hash text box + + + Matches {0} + Appears when two compared hashes match, e.g. "Matches SHA256" + + + No matches found + Appears when two compared hashes don't match + + + Path or alias + + + Invalid path + + + Test integration + + + {0} could not be located. Please check your settings and try again. + + + The configured IDE could not be located + + + Open settings + + + Visual Studio Code + + + Enter a path or launch alias + + + Please enter a name for the IDE + + + Clone repo + Clone repo dialog title + + + Clone + Primary action button in the clone repo dialog + + + Repository URL + URL textbox header in the clone repo dialog + + + Cannot clone repo + Cannot clone repo dialog title + + + Compare a file + Button that appears in file hash properties that allows the user to compare two files + + + Size format + + + Binary + + + Decimal + + + You can add sections to the sidebar by right-clicking and selecting the sections you want to add + + + Drag files or folders here to interact with them across different tabs + + + Enter a path to navigate to... + + + Find features and commands... + + + Search for files and folders... + + + During file operations + + + Show status center button + + + Status center progress ring + Screen reader name for the status center progress ring + + + Icon files + This is the friendly name for a variety of different icon files. + + + Show collapsed path breadcrumbs + + + Show folders in {0} + + + Show folders in Home + + + There are no commands containing {0} + + + See more + + + Filtering for + + + Filename + + + Signatures + + + Signature list + + + Issued by: + + + Issued to: + + + Valid from: + + + Valid to: + + + No signature was found. + + + Show option to open folders in Windows Terminal + + + Open log file + + + Open log file in your default editor + + + Open log file location in your default file manager + + + Unable to open the log file + + + Show status bar + + + Only in Columns View + + + New {0} + + + Enable smooth scrolling + + + Scrolling + + + Show option to Pin to Sidebar + + + Show option to Pin to the Start Menu + \ No newline at end of file diff --git a/src/Files.App/Strings/sq-AL/Resources.resw b/src/Files.App/Strings/sq-AL/Resources.resw index 5f1a71b2db94..bd8b4ffe8354 100644 --- a/src/Files.App/Strings/sq-AL/Resources.resw +++ b/src/Files.App/Strings/sq-AL/Resources.resw @@ -123,14 +123,20 @@ Kopjo vendndodhjen + + Kopjo shtegun e elementit + - Kopje vendndodhjen me thonjëza + Kopje vendndodhjen me thonjëza + + + Kopjo shtegun e elementit të zgjedhur me thonjëza Shfleto - Madhësia + Madhësia Krijuar: @@ -139,34 +145,34 @@ Vendndodhja - Madhësia: + Madhësia: - Madhësia në disk: + Madhësia në disk: - Madhësia e pakompresuar: + Madhësia e pakompresuar: - Ky veprim nuk mund të zhbëhet + Ky veprim nuk mund të zhbëhet - Dosja mbërritëse + Dosja mbërritëse - është një nëndosje e dosjes burim + është një nëndosje e dosjes burim Anashkalo - Përzgjidhi të gjitha + Zgjidh të gjitha - Përkëmbe përzgjedhjen + Përkëmbe përzgjedhjen - Pastro përzgjedhjen + Pastro përzgjedhjen Modifikuar: @@ -175,64 +181,64 @@ Qasur: - Pastro të gjithë artikujt + Pastro të gjithë artikujt - Fut vendndodhjen ku kërkon të shkosh ose shkruaj ">" për të hapur panelin e komandës + Fut vendndodhjen ku kërkon të shkosh ose shkruaj ">" për të hapur panelin e komandës - Kërko + Kërko - Skedarët që ke qasur më parë do të shfaqen këtu + Skedarët që ke qasur më parë do të shfaqen këtu - Hiqe këtë artikull + Hiqe këtë artikull depo GitHub - Rreth kësaj + Rreth kësaj Burim i hapur - Formati i datës + Formati i datës Paraqitja - Shfaq prapashtesat për tipet e njohura të skedarëve + Shfaq prapashtesat për tipet e njohura të skedarëve - Shfaq skedarët dhe dosjet e fshehura + Shfaq skedarët dhe dosjet e fshehura - Shfaq skedarët që fillojnë me pikë + Shfaq skedarët me pikë Pamja e jashtme - Ngjyra në sfond + Ngjyra në sfond I avancuar - Vazhdo aty ku e kishe lënë + Vazhdo aty ku e kishe lënë - Hap një skedë të re + Hap një skedë të re - Hap një ose disa faqe specifike + Hap një ose disa faqe specifike - Desktop + Desktopi Documentat @@ -244,28 +250,31 @@ Emrit - Në ngjitje + Në ngjitje Ngjit + + Ngjit shkurtoren + i/e Re - Cilësimet + Cilësimet Hap - Hap në një skedë të re + Hap në një skedë të re - Hap në një dritare të re + Hap në një dritare të re - Shpërndaje + Shpërndaje Prite @@ -274,189 +283,192 @@ Fshije - Pin to Sidebar + Fiksoje te shiriti anësor - Welcome to Files! + Mirë se vini në Files! - Grant permission + Jep lejen - To get started, you'll need to grant us permission to display your files. This will open a Settings page where you can grant us this permission. You'll need to reopen the app once you've completed this step. + Për të filluar, duhet të na jepni leje që të shfaqim skedarët tuaj. Kjo do të hapë një faqe të Cilësimeve ku mund ta jepni këtë leje. Pasi ta keni përfunduar këtë hap, do t'ju duhet ta rihapni aplikacionin. - Display + Ekrani - Enter an item name + Shkruani emrin e një elementi - - Set name + + Krijo {0} të ri - Light + E çelët - Dark + E errët + + + Përdor cilësimin e sistemit - Application + Aplikacion - System + Sistemi - New folder + Dosje e re - New file + Skedar i ri - This folder is empty. + Kjo dosje është bosh. - Back + Mbrapa - Forward + Përpara - Up + Lart - Refresh + Rifresko - An item with this name already exists in this directory. + Një element me këtë emër ekziston tashmë në këtë drejtori. - Replace existing item + Zëvendëso elementin ekzistues - Item already exists + Elementi ekziston tashmë - Set as lockscreen background + Vendose si sfond të ekranit të kyçjes - Set as app background + Vendose si sfond të aplikacionit - We weren't able to delete this item + Nuk arritëm ta fshinim këtë element - Please insert the necessary drive to access this item. + Fut diskun e nevojshëm për të hyrë në këtë artikull. - Drive unplugged + Drejtoni i shkëputur nga priza - The file you are attempting to access may have been moved or deleted. + Skedari që po përpiqeni të përdorni mund të jetë zhvendosur ose fshirë. - Cannot open properties for this file + Nuk mund të hapen vetitë për këtë skedar - The file you are attempting to access may have been moved or deleted. + Skedari që po përpiqeni të përdorni mund të jetë zhvendosur ose fshirë. - File Not Found + Skedari nuk u gjet - The folder you are attempting to access may have been moved or deleted. + Dosja në të cilën po përpiqeni të përdorni mund të jetë zhvendosur ose fshirë. - Did you delete this folder? + E fshive këtë dosje? - The file you are attempting to access is currently being used by {0} + Skedari ku po përpiqeni të qaseni aktualisht po përdoret nga {0} - The file you are attempting to access is currently being used by another application + Skedari në të cilin po përpiqeni të aksesoni aktualisht po përdoret nga një aplikacion tjetër - File is in use + Skedari është në përdorim - Layout + Paraqitja - Read-only + Vetëm për lexim {0, plural, one {# day ago} other {# days ago}} - 1 day ago + 1 dite me pare - {0} days ago + {0} ditë më parë {0, plural, one {# hour ago} other {# hours ago}} - 1 hour ago + 1 orë më parë - {0} hours ago + {0} orë më parë {0, plural, one {# minute ago} other {# minutes ago}} - 1 minute ago + 1 minutë më parë - {0} minutes ago + {0} minuta më parë {0, plural, one {# second ago} other {# seconds ago}} - 1 second ago + 1 sekondë më parë - {0} seconds ago + {0} sekonda më parë - Now + Tani - The requested operation is not supported + Operacioni i kërkuar nuk mbështetet - {0} free of {1} + {1} pa {1} - Send to + Dërgo në - The item name must not contain the following characters: \ / : * ? " < > | + Emri i artikullit nuk duhet të përmbajë karakteret e mëposhtme: \ / : * ? " < > | - The item name specified is invalid + Emri i artikullit të specifikuar është i pavlefshëm {0, plural, one {# item selected} other {# items selected}} ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural - {0, plural, one {item} other {items}} + {0, plural, one {arti} other {artikuj}} ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural - Yes + po - Are you sure you want to permanently delete all these items? + Jeni i sigurt që dëshironi t'i fshini përgjithmonë të gjithë këta artikuj? - Empty recycle bin + Kosh riciklimi bosh - bytes + byte KB @@ -471,7 +483,7 @@ TB - PB + BP B @@ -483,442 +495,442 @@ {0, number} {0, plural, one {file} other {files}}, {1, number} {1, plural, one {folder} other {folders}} from {2, number} {2, plural, one {location} other {locations}} - Run as another user + Ekzekutoni si një përdorues tjetër - All type of {0} + Të gjitha llojet e {0} - Different types + Llojet e ndryshme - All in {0} + Të gjitha në {0} - Used space: + Hapësira e përdorur: - Free space: + Hapësirë e lirë: - Capacity: + Kapaciteti: - File system: + Sistemi i skedarëve: - Recent + E fundit - Move tab here + Zhvendos skedën këtu - Status - - - Windows default + Statusi - No results + Nuk ka rezultate - Can't access any items to display + Nuk mund të qaset asnjë artikull për t'u shfaqur - Move to {0} + Lëviz te {0} - Copy to {0} + Kopjo në {0} + + + Klononi në {0} - Create shortcut + Krijo shkurtore - Open file location + Hap vendndodhjen e skedarit - Arguments: + Argumentet: - Destination: + Destinacioni: - Shortcut type: + Lloji i shkurtores: - Web link + Lidhje në internet - General + Gjeneral - Shortcut + Shkurtore - Internet shortcut + Shkurtorja e internetit - {0} - shortcut + {0} - shkurtore - Files ran into a problem that the developers didn't prepare for yet. + Skedarët hasën në një problem për të cilin zhvilluesit nuk ishin përgatitur ende. - Something went wrong! + Diçka shkoi keq! - Report this issue + Raportoni këtë çështje - The item referenced is either invalid or inaccessible.{0}Error message:{0}{1} + Artikulli i referuar është ose i pavlefshëm ose i paarritshëm.{1}Mesazhi i gabimit:{1}{1} - Invalid item + Artikull i pavlefshëm - Version: + Versioni: - There's nothing to share right now... + Nuk ka asgjë për të ndarë tani ... - The items you've selected will be shared + Artikujt që keni zgjedhur do të ndahen - The selected item will be shared + Artikulli i zgjedhur do të ndahet - Sharing {0} + Ndani {0} - Sharing {0} {1} + Ndani {1} {1} - The item you're attempting to rename no longer exists. Please verify the correct location of the item you need to rename. + Artikulli që po përpiqeni të riemërtoni nuk ekziston më. Ju lutemi verifikoni vendndodhjen e saktë të artikullit që duhet të riemërtoni. - Item no longer exists + Artikulli nuk ekziston më - The name specified was invalid. Please check the desired item name and try again. + Emri i specifikuar ishte i pavlefshëm. Ju lutemi kontrolloni emrin e dëshiruar të artikullit dhe provoni përsëri. - Invalid item name + Emër i pavlefshëm artikulli - The length of the item name specified exceeds the maximum limit. Please check the desired name and try again. + Gjatësia e emrit të artikullit të specifikuar e kalon kufirin maksimal. Ju lutemi kontrolloni emrin e dëshiruar dhe provoni përsëri. - Item name was too long + Emri i artikullit ishte shumë i gjatë - Show more options + Shfaq më shumë opsione - The application needs to be restarted in order to apply these settings, would you like to restart the app? + Aplikacioni duhet të riniset për të aplikuar këto cilësime, dëshironi ta rinisni aplikacionin? - Sponsor us on GitHub + Na sponsorizoni në GitHub - We weren't able to create this item + Nuk mundëm ta krijonim këtë artikull - Access Denied + Qasja u refuzua - Set as + Cakto si - Cancel + Anulo - Create a new item + Krijo një artikull të ri - Choose a type for this new item below + Zgjidhni një lloj për këtë artikull të ri më poshtë - File + Skedari - Creates an empty file + Krijon një skedar bosh - Folder + Dosja - Creates an empty folder + Krijon një dosje bosh - Refresh the directory + Rifresko drejtorinë - Language + Gjuha - Move shell extensions into a sub menu + Zhvendosni shtesat e guaskës në një nënmenu - Show confirmation dialog when deleting items + Shfaq dialogun e konfirmimit kur fshin artikujt - There was an issue saving some properties. + Pati një problem me ruajtjen e disa pronave. - Error + Gabim - Close anyway + Mbylle gjithsesi - Rating + Vlerësimi - Item Path + Shtegu i artikullit - Item Type + Lloji i artikullit - Title + Titulli - Subject + Subjekti - Comment + Koment - Copyright + E drejta e autorit - Date Modified + Data e modifikimit - Bit Depth + Thellësia e Bitit - Dimensions + Dimensionet - Horizontal Size + Madhësia horizontale - Vertical Size + Madhësia vertikale - Horizontal Resolution + Rezolucioni horizontal - Vertical Resolution + Rezolucioni vertikal - Color Space + Hapësirë me ngjyra sRGB - Unspecified + E paspecifikuar - Longitude Decimal + Decimal i gjatësisë gjeografike - Latitude Decimal + Gjerësia gjeografike dhjetore - Altitude + Lartësia mbidetare - Date Taken + Data e Marrjes - Camera Manufacturer + Prodhuesi i kamerave - Camera Model + Modeli i kamerës - Exposure Time + Koha e ekspozimit - Focal Length + Gjatësia Fokale - Aperture + Apertura - People Names + Emrat e njerëzve - Channel Count + Numri i kanaleve - Format + Formati - Sample Rate + Norma e mostrës - Album Artist + Artist i albumit - Album Title + Titulli i albumit Artist - Beats Per Minute + Rrahjet në minutë - Composer + Kompozitor - Conductor + Dirigjent - Disc Number + Numri i diskut - Genre + Zhanri - Track Number + Numri i gjurmës - Duration + Kohëzgjatja - Frame Count + Numërimi i kornizës - Protection Type + Lloji i mbrojtjes - Author Url + Url i autorit - Content Distributor + Shpërndarësi i përmbajtjes - Date Released + Data e publikimit - Series Name + Emri i serisë - Season Number + Numri i sezonit - Episode Number + Numri i episodit - Producer + Prodhuesi - Promotion Url + Url-ja e promovimit - Provider Style + Stili i ofruesit - Publisher + Botues - Thumbnail Large Path + Miniatur Shtegu i madh - Thumbnail Large Uri + Miniatura Uri i madh - Thumbnail Small Path + Miniatur Shtegu i Vogël - Thumbnail Small Uri + Miniatura Uri i vogël - User Web Url + Urlja e ueb-faqes së përdoruesit - Writer + Shkrimtar - Year + viti - Contributor + Kontribues - Last Author + Autori i fundit - Revision Number + Numri i rishikimit - Version + Versioni - Date Created + Data e krijimit - Total Editing Time + Koha totale e redaktimit - Template + shabllon - Word Count + Numri i fjalëve - Character Count + Numri i karaktereve - Line Count + Numërimi i rreshtave - Paragraph Count + Numri i paragrafëve - Page Count + Numri i faqeve - Slide Count + Numërimi i rrëshqitjeve - Frame Rate + Shpejtësia e kornizës - Encoding Bitrate + Kodimi i shpejtësisë së biteve - Audio Encoding Bitrate + Shpejtësia e biteve të kodimit të audios - Video Encoding Bitrate + Shpejtësia e biteve të kodimit të videos - Compression + Kompresimi - Frame Width + Gjerësia e kornizës - Frame Height + Lartësia e kornizës - Orientation + Orientimi - Core + Bërthama - Image + Imazhi - Photo + Foto GPS @@ -930,337 +942,358 @@ Audio - Music + Muzikë Video - Document + Dokumenti - Address + Adresa - Selection options + Opsionet e përzgjedhjes - Select + Zgjidhni - Recycle bin item + Artikulli i koshit të riciklimit - Shortcut item + Artikulli i shkurtores - Drives + Disqet - Move here + Lëvizni këtu - Safe to remove hardware + I sigurt për të hequr harduerin - The device can now be safely removed from the computer. + Pajisja tani mund të hiqet në mënyrë të sigurt nga kompjuteri. - Problem Ejecting Device + Problem me nxjerrjen e pajisjes - This device is currently in use. Close any programs, windows or tabs that might be using the device, and then try again. + Kjo pajisje është aktualisht në përdorim. Mbyllni çdo program, dritare ose skedë që mund të jetë duke përdorur pajisjen dhe më pas provoni përsëri. - Eject + Nxjerr - Duplicate tab + Skeda e kopjuar - Move tab to new window + Zhvendos skedën në dritare të re - New tab + Skeda e re - Some properties may contain personal information. + Disa prona mund të përmbajnë informacion personal. - Clear All Properties + Pastro të gjitha vetitë - Check the status of file operations here + Kontrolloni statusin e operacioneve të skedarëve këtu - Status center + Qendra e statusit - Search results in {1} for {0} + Rezultatet e kërkimit në {0} për {0} + + + Rezultatet e kërkimit për `{0}` - Details + Detajet - No + Nr - Show protected system files + Shfaq skedarët e mbrojtur të sistemit - Sync layout and sorting preferences across directories + Sinkronizoni preferencat e paraqitjes dhe renditjes nëpër drejtori - Customize the right click context menu + Personalizojeni menynë e kontekstit të klikimit me të djathtën - Original path + Rruga origjinale - Add + Shtoni - Edit + Redakto - Remove + Hiq - Open tabs in dual pane mode + Hapni skedat në modalitetin e panelit të dyfishtë - Show option to open folders in a new pane + Shfaq opsionin për të hapur dosjet në një panel të ri - Show option to copy path + Shfaq opsionin për të kopjuar rrugën - Show option to create folder with selection + Shfaq opsionin për të krijuar dosje me përzgjedhje - Show option to create shortcut + Shfaq opsionin për të krijuar shkurtore - New pane + Xhami i ri - Open in new pane + Hapni në panelin e ri - Files & folders + Skedarët dhe dosjet - Details (Ctrl+Shift+1) - - - Tiles (Ctrl+Shift+2) + Detajet (Ctrl+Shift+1) - Date deleted + Data e fshirjes - Cloud Drives + Disqet në renë kompjuterike - Confirm + Konfirmo - Desired name + Emri i deshiruar - Toggle the preview pane + Ndrysho panelin e shikimit paraprak - Toggle the details pane + Ndrysho panelin e detajeve - Toggle the details pane to view basic file properties + Ndryshoni panelin e detajeve për të parë vetitë bazë të skedarit - Toggle the info pane + Ndrysho panelin e informacionit - Toggle the info pane to view the detail/preview panes + Ndrysho dukshmërinë e paneleve të detajeve/paraafishimit - Toggle the Toolbar + Ndrysho shiritin e veglave + + + Ndrysho dukshmërinë e shiritit të veglave + + + Ndrysho kokën e filtrit + + + Ndrysho dukshmërinë e kokës së filtrit + + + Ndrysho panelin e dyfishtë + + + Ndrysho modalitetin e panelit të dyfishtë - No preview available + Nuk ka pamje paraprake - Item Name + Emri i artikullit - Unpin from Sidebar + Zhgozhdoni nga Shiriti anësor - Network + Rrjeti - File details + Detajet e skedarit - File preview + Pamja paraprake e skedarit - Selected file preview pane + Paneli i përzgjedhur i pamjes paraprake të skedarit Feedback - Available when online + Në dispozicion kur jeni në linjë - Documentation + Dokumentacioni - Available offline + E disponueshme jashtë linje - Partially available offline + Pjesërisht e disponueshme jashtë linje - Syncing + Po sinkronizohet - Excluded from sync + Përjashtuar nga sinkronizimi - Not calculated + E pa llogaritur - Unknown + E panjohur - If you change a file extension, the file might become unusable. Are you sure you want to change it? + Nëse ndryshoni një shtesë skedari, skedari mund të bëhet i papërdorshëm. Je i sigurt që dëshiron ta ndryshosh? - CD ROM Drive + Disku i CD ROM-it Cloud Drive - Fixed Disk Drive + Disku i fiksuar - Floppy Disk Drive + Disku i diskut - Network Drive + Rrjeti Drive - Unmounted Drive + Disku i çmontuar RAM Disk Drive - Removable Storage Device + Pajisje ruajtëse e lëvizshme - Unknown + E panjohur - Virtual Drive + Disku virtual - Date created + Data e krijimit - More options... + Më shumë opsione... - Map network drive + Harta e rrjetit të makinës - Pinned + E ngulitur - Libraries + Bibliotekat - No item selected + Nuk është zgjedhur asnjë artikull - Target + Synimi - Arguments + Argumentet - Default + E paracaktuar - Item count + Numri i artikujve - This action requires administrator rights + Ky veprim kërkon të drejtat e administratorit - Would you like to continue as administrator? + Dëshironi të vazhdoni si administrator? - Columns (Ctrl+Shift+6) + Kolonat (Ctrl+Shift+6) - Pin to the Start Menu + Vendosni në menynë Start - Unpin from the Start Menu + Zhgozhdoni nga menyja Start - Library + Biblioteka - Locations: + Vendndodhjet: - Set as default save path + Cakto si shtegun e paracaktuar të ruajtjes - No locations + Nuk ka vendndodhje - Input field cannot be empty! + Fusha e hyrjes nuk mund të jetë bosh! - The name must not contain the following characters: \ / : * ? " < > | + Emri nuk duhet të përmbajë karakteret e mëposhtme: \ / : * ? " < > | - Using this name is not allowed! + Përdorimi i këtij emri nuk lejohet! - Library with the same name already exists! + Biblioteka me të njëjtin emër ekziston tashmë! - Open Storage Sense + Hap "Storage Sense". + + + Hapni faqen Storage Sense në Cilësimet e Windows + + + Pastrim - Copy + Kopjo - Copy {0, plural, one {item} other {items}} + Copy {0, plural, one {artikull} other {items}} - Delete {0, plural, one {item} other {items}} + Delete {0, plural, one {artikull} other {items}} - Move + Lëvizni {0, plural, one {One item will be moved} other {# items will be moved}} - Move {0, plural, one {item} other {items}} + Move {0, plural, one {artikull} other {items}} {0, plural, one {One item will be deleted} other {# items will be deleted}} - Continue + Vazhdoni {0, plural, one {There is one conflicting file name} other {There are # conflicting file names}} @@ -1269,841 +1302,898 @@ {0, plural, one {There is one conflicting file name} other {There are # conflicting file names}}, and {1, plural, one {one outgoing item} other {# outgoing items}} - Conflicting {0, plural, one {file name} other {file names}} + Conflicting {0, plural, one {emri i skedarit} other {emrat e skedarëve}} {0, plural, one {One item will be copied} other {# items will be copied}} - Permanently delete + Fshije përgjithmonë - ISO speed + shpejtësi ISO - Replace existing + Zëvendësoni ekzistuesin - Generate new name + Krijo një emër të ri - Create folder with selection + Krijo dosje me përzgjedhje - Type: + Lloji: - Date modified: + Data e modifikimit: - Open with + Hap me - File icon + Ikona e skedarit - Original path column + Kolona origjinale e rrugës - Item type column + Kolona e llojit të artikullit - Date modified column + Kolona e modifikimit të datës - Date deleted column + Data e fshirjes së kolonës - Sort + Rendit - Date modified + Data e modifikuar - Original folder + Dosja origjinale - Descending + Duke zbritur - Huge + I madh - Very large + Shumë i madh - Large + I madh - Medium + E mesme - Small + I vogël - Tiny + I vogel - Future + e ardhmja - Today + Sot - Yesterday + Dje - Earlier this week + Në fillim të kësaj jave - Last week + Javën e kaluar - Earlier this month + Në fillim të këtij muaji - Last month + Muajin e kaluar - Earlier this year + Në fillim të këtij viti - Last year + Vitin e kaluar - Year {0} + Viti {0} - {0} item + {0} artikull - {0} items + {0} artikuj - Reopen closed tab + Rihap skedën - Rename + Riemërto - Privacy + Privatësia - the Recycle Bin + koshin e riciklimit - Canceling + Duke anuluar - Clear + E qartë Widgets - Sync status + Statusi i sinkronizimit - Security + Siguria - Advanced permissions + Lejet e avancuara - Allow + Lejo - Deny + Moho - Full control + Kontroll i plotë - List directory contents + Listoni përmbajtjen e drejtorisë - Modify + Modifiko - Permissions for {0} + Lejet për {0} - Read and execute + Lexoni dhe ekzekutoni - Read + Lexoni - Group or user names + Emrat e grupeve ose përdoruesve - Write + Shkruani - Unknown account + Llogari e panjohur - You do not have permissions to view the security properties of this object. Click "Advanced permissions" to proceed. + Ju nuk keni leje për të parë vetitë e sigurisë së këtij objekti. Klikoni "Lejet e avancuara" për të vazhduar. - Owner: + Pronari: - Unknown owner + Pronar i panjohur - Extract archive + Ekstrakt arkiv - Open destination folder when complete + Hapni dosjen e destinacionit kur të përfundojë - Extract to {0}\ + Ekstrakt në {0}\ - Create new library + Krijo bibliotekë të re - Restore default libraries + Rivendos bibliotekat e paracaktuara - Are you sure you want to restore the default libraries? All your files will remain on your storage. + Jeni i sigurt që dëshironi të rivendosni bibliotekat e paracaktuara? Të gjithë skedarët tuaj do të mbeten në hapësirën ruajtëse. - Restore Libraries + Rivendosja e bibliotekave - Owner + Pronari - Special + E veçanta - Access + Qasja - Applies to + Zbatohet për - Principal + Kryesorja - files + dosjet - this folder + këtë dosje - subfolders + nëndosjet - Inherited + I trashëguar - Permissions + Lejet - You do not have permissions to view the security properties of this object. You can try to take ownership of this object. + Ju nuk keni leje për të parë vetitë e sigurisë së këtij objekti. Mund të përpiqeni të merrni pronësinë e këtij objekti. - This folder and files + Kjo dosje dhe skedarë - This folder, subfolders and files + Kjo dosje, nëndosje dhe skedarë - Only files + Vetëm skedarë - Only subfolders + Vetëm nëndosjet - Only subfolders and files + Vetëm nëndosjet dhe skedarët - Only this folder + Vetëm kjo dosje - This folder and subfolders + Kjo dosje dhe nëndosjet - Append data + Shtoni të dhëna - Change permission + Ndrysho lejen - Create folders + Krijo dosje - Create files + Krijo skedarë - Delete subdirectories and files + Fshini nëndirektoritë dhe skedarët - Execute files + Ekzekutoni skedarët - Read attributes + Lexoni atributet - Read data + Lexoni të dhënat - Read extended attributes + Lexoni atributet e zgjeruara - Read permissions + Leximi i lejeve - Take ownership + Merrni pronësinë - Visit folder + Vizitoni dosjen - Write attributes + Shkruani atributet - Write data + Shkruani të dhëna - Write extended attributes + Shkruani atribute të zgjeruara - Convert inherited permissions into explicit permissions + Konvertoni lejet e trashëguara në leje të qarta - Enable inheritance + Aktivizo trashëgiminë - Remove all inherited permissions + Hiq të gjitha lejet e trashëguara - Reset child permissions + Rivendos lejet e fëmijëve - Replace all child object permissions entries with inheritable permission entries from this object + Zëvendësoni të gjitha hyrjet e lejeve të objektit fëmijë me hyrjet e lejeve të trashëgueshme nga ky objekt - Close active pane + Mbylle panelin - Enter compact overlay + Fut mbivendosjen kompakte - Exit compact overlay + Dilni nga mbivendosja kompakte - File description + Përshkrimi i skedarit - Company + Kompania - Language + Gjuha - Trademarks + Markat tregtare - File version + Versioni i skedarit - Load full preview + Ngarko pamjen paraprake të plotë - Downloads the item from the cloud and loads the preview + Shkarkon artikullin nga cloud dhe ngarkon pamjen paraprake {0, plural, one {# item} other {# items}} ({1, plural, one {# file} other {# files}}, {2, plural, one {# folder} other {# folders}}) - Uncompressed size + Madhësia e pakompresuar WSL - Hide {0} section + Fshih seksionin {0} - Add file + Shto skedar - Updates available + Ofrohen përditësime - Updates are ready to install + Përditësimet janë gati për t'u instaluar - Close + Mbylle - Update + Përditëso - Home - - - Ctrl+Shift+1 - - - Ctrl+Shift+2 - - - Ctrl+Shift+6 + Shtëpi - The archive extraction completed successfully. + Nxjerrja e arkivit përfundoi me sukses. - Extracting archive + Nxjerrja e arkivit - Extracting complete! + Nxjerrja e plotë! - Open parent folder + Hap dosjen prind - Unknown + E panjohur - Edit tags + Redakto etiketat - Part of set + Pjesë e kompletit - Open drive + Hapni diskun - Please insert a disc into drive {0} + Fut një disk në diskun {0} - Insert a disc + Fusni një disk OK - Credential required + Kërkohet kredenciale - Anonymous + Anonim - Provide your credential: + Jepni kredencialet tuaja: - UserName + Emri i përdoruesit - No items found + Nuk u gjet asnjë artikull - Open log location + Hap vendndodhjen e regjistrit - Create link in {0} + Krijo lidhje në {0} - Columns + Kolonat - - Tiles + + Kartat - Open folders in new tab + Hapni dosjet në skedën e re - Status center + Qendra e statusit - Date created column + Kolona e datës së krijimit - Item size column + Kolona e madhësisë së artikullit - Experimental feature flags + Flamujt e tipareve eksperimentale - Help and support + Ndihmë dhe mbështetje - Submit feature request + Paraqisni kërkesën për veçori - Submit bug report + Paraqisni raportin e gabimeve - Set Files as the default file manager + Cakto Files si menaxherin e parazgjedhur të skedarëve - Use Files as Open File Dialog + Përdorni skedarët si dialogun e skedarit të hapur - Tag + Etiketë - Open items with a single click + Hapni skedarët me një klik të vetëm - Folder path + Rruga e dosjes - Export settings + Cilësimet e eksportit - Import settings + Cilësimet e importit - Couldn't import settings. The settings file is corrupted. + Cilësimet nuk mund të importoheshin. Skedari i cilësimeve është i dëmtuar. - Error importing settings + Gabim gjatë importimit të cilësimeve - Empty Recycle Bin + Kosh i zbrazët i riciklimit - Sidebar + Shiriti anësor - Choose a custom folder icon + Zgjidhni një ikonë dosjeje të personalizuar - Customization + Përshtatje - Restore default + Rivendosni parazgjedhjen - Open tab in existing instance when opening Files from another app + Hapni skedën në shembullin ekzistues kur hapni Skedarët nga një aplikacion tjetër - Startup settings + Cilësimet e nisjes - Compatibility + Përputhshmëria - None + Asnjë - On Windows login + Në hyrjen në Windows - On this program start + Në këtë program filloni - System + Sistemi - System (Enhanced) + Sistemi (i përmirësuar) - 16-bit (65536) color + Ngjyra 16-bit (65536). - 8-bit (256) color + Ngjyra 8-bit (256). - Disable full-screen optimizations + Çaktivizo optimizimet e ekranit të plotë - Run in 640 x 480 screen resolution + Ekzekutoni në rezolucion të ekranit 640 x 480 - Override high DPI scaling behavior + Anuloni sjelljen e shkallës së lartë të DPI - Reduced color mode + Modaliteti i reduktuar i ngjyrave - Register this program for restart + Regjistrojeni këtë program për rinisje + + + Dritarja e fillimit + + + Dritare normale + + + Minimizuar + + + Maksimizuar - Run as administrator + Drejtoni si administrator - Run compatibility troubleshooter + Ekzekutoni zgjidhjen e problemeve të përputhshmërisë - Use DPI settings of the main monitor + Përdorni cilësimet DPI të monitorit kryesor - Do not adjust DPI + Mos e rregulloni DPI - Do not override DPI + Mos e anashkaloni DPI - Compatibility mode + Modaliteti i përputhshmërisë - No reduced color + Nuk ka ngjyrë të reduktuar - Third party libraries + Bibliotekat e palëve të treta - This option modifies the system registry and can have unexpected side effects on your device. Continue at your own risk. + Ky opsion modifikon regjistrin e sistemit dhe mund të ketë efekte anësore të papritura në pajisjen tuaj. Vazhdoni në rrezikun tuaj. + + + Operacionet e sheshimit janë të përhershme dhe nuk rekomandohen. Vazhdoni në rrezikun tuaj. - Create Library + Krijo Bibliotekë - Enter Library name + Fut emrin e bibliotekës - Calculate folder sizes + Llogaritni madhësinë e dosjeve - Recent files + Skedarët e fundit - Open Files on Windows startup + Hapni skedarët në fillimin e Windows - Attributes + Atributet - Hidden + I fshehur - More details + Më shumë detaje - Read only + Lexo vetëm - Cleanup your drive contents + Pastroni përmbajtjen e diskut - Type + Lloji - Bytes + Bajt - Extract + Ekstrakt - Extract files + Ekstrakt skedarët - Extract here + Ekstrakt këtu Ctrl+E - Extract to {0} + Ekstrakt në {0} - Run script + Ekzekutoni skriptin - Run with PowerShell + Ekzekutoni me PowerShell - Calculating folder sizes is resource intensive and may cause your CPU usage to increase. + Llogaritja e madhësive të dosjeve kërkon burime intensive dhe mund të shkaktojë rritjen e përdorimit të CPU-së. - Rotate left + Rrotullo majtas - Rotate right + Rrotullo djathtas - Install + Instaloni - Close tabs to the left + Mbyllni skedat në të majtë - Close tabs to the right + Mbyllni skedat në të djathtë - Close other tabs + Mbyllni skedat e tjera + + + Mbyllni të gjitha skedat - Music + Muzikë - Pictures + Fotot - Update Files + Përditëso skedarët - Tags + Etiketat - Universal + Universale - ex: {0}, {1} + p.sh.: {1}, {1} - Show thumbnails + Shfaq miniaturë - Retry + Provo sërish - Move operation is not supported in this context. Do you want to copy the items instead? + Operacioni i zhvendosjes nuk mbështetet në këtë kontekst. Dëshironi të kopjoni artikujt në vend të kësaj? - Hidden items + Artikuj të fshehur - Custom + Me porosi - Set as desktop slideshow + Cakto si rrëshqitje në desktop - Size all columns to fit + Madhësia e të gjitha kolonave për t'u përshtatur - Adaptive layout + Paraqitja adaptive - Adaptive layout (Ctrl+Shift+7) + Struktura përshtatëse (Ctrl+Shift+7) Ctrl+Shift+7 - Show alternate data streams + Shfaq rrjedhjet alternative të të dhënave - Behaviors + Sjelljet + + + Përshëndetje! + + + Të pëlqejnë skedarët? Ju lutemi, merrni parasysh rishikimin në Dyqanin e Microsoft. + + + Të pëlqejnë skedarët? Ju lutemi merrni parasysh mbështetjen e projektit në GitHub. - - Review Files + + Sponsor - - Would you like to review Files? + + Na vlerësoni + + + Largoje - Set as background + Cakto si sfond - Set as desktop background + Cakto si sfond të desktopit - Set as default + Cakto si parazgjedhje - Date column + Kolona e datës - Date created column + Kolona e datës së krijimit - Size column + Kolona e madhësisë - Tag column + Kolona etiketë - Type column + Lloji kolonë - Lockscreen + Ekrani i kyçur - - Open folders with a single click in the Columns Layout + + Hapni dosjet me një klik të vetëm - Opening items + Hapja e artikujve - Compress + Kompresoni + + + Zhvendosni të gjitha përmbajtjet nga nëndosjet në vendndodhjen e zgjedhur + + + Rrafshoni dosjen + + + Rrafshoj + + + Rrafshimi i një dosjeje do të zhvendosë të gjithë përmbajtjen nga nëndosjet e tij në vendndodhjen e zgjedhur. Ky operacion është i përhershëm dhe nuk mund të zhbëhet. Duke përdorur këtë veçori eksperimentale, ju e pranoni rrezikun dhe pranoni të mos mbani përgjegjës ekipin e Files për çdo humbje të të dhënave. + + + Shfaq opsionet e rrafshimit - Select files and folders when hovering over them + Zgjidhni skedarët dhe dosjet kur rri pezull mbi to - Are you sure you want to restore all items in the recycle bin? + Jeni i sigurt që dëshironi të rivendosni të gjithë artikujt në koshin e riciklimit? - Restore all items + Rivendos të gjithë artikujt - Do you want to restore the {0} selected item(s)? + Do you want to restore the {0, plural, one {selected item} other {{0} selected items}}? - Restore selection + Rivendos përzgjedhjen - Restore All Items + Rivendos të gjithë artikujt - Restore + Rivendos - Shortcut cannot be opened + Shkurtorja nuk mund të hapet - The target destination cannot be found {0}. Do you want to delete this shortcut? + Destinacioni i synuar nuk mund të gjendet {0}. Dëshiron ta fshish këtë shkurtore? - You don't have permission to access this folder. + Ju nuk keni leje për të hyrë në këtë dosje. - Archive password + Fjalëkalimi i arkivit + + + Kodimi + + + {0} (zbuluar) - Path + Vendndodhja - Sorting and grouping + Renditja dhe grupimi - Create archive + Krijo arkiv - Create + Krijo - Create {0} + Krijo {0} - Enter a name + Fut një emër - Compression level + Niveli i kompresimit Ultra - High + Lartë - Normal + Normale - Low + E ulët - Fast + Shpejt - None + Asnjë - Splitting size + Madhësia e ndarjes + + + Mos u ndani + + + Automatik + + + Madhësia e fjalorit - - Do not split + + Madhësia e fjalës + + + Përdorimi i vlerësuar i memories: {0} + + + Memoria e disponueshme: {0} CD @@ -2112,577 +2202,607 @@ DVD - FAT + YNDYRA Blu-ray - Encryption + Enkriptimi - Password + Fjalëkalimi - Format + Formati - Sync status column + Sinkronizoni kolonën e statusit - Group by + Grupi sipas - Sort by + Rendit sipas - Group in descending order + Grupi në rend zbritës - Sort in descending order + Renditni në rend zbritës - Create a new shortcut + Krijo një shkurtore të re - Create shortcuts to local or network programs, files, folders, computers or Internet addresses. + Krijoni shkurtore për programet lokale ose të rrjetit, skedarët, dosjet, kompjuterët ose adresat e internetit. - Enter the location of the item: + Shkruani vendndodhjen e artikullit: - Creates a shortcut + Krijon një shkurtore - Recently used files is currently disabled in Windows File Explorer. + Skedarët e përdorur së fundi janë aktualisht të çaktivizuara në Windows File Explorer. - Edit settings file + Redakto skedarin e cilësimeve + + + Hapni skedarin e cilësimeve në redaktorin tuaj të paracaktuar - - What's new + + Shënimet e publikimit + + + Hapni Shënimet e publikimit - Creating a shortcut in this location requires administrator privileges + Krijimi i një shkurtoreje në këtë vendndodhje kërkon privilegje administratori - Would you like to create the shortcut on the desktop instead? + Dëshironi të krijoni shkurtoren në desktop? - The specified item name is invalid + Emri i artikullit të specifikuar është i pavlefshëm - Settings + Cilësimet - Double click on a blank space to go up one directory + Klikoni dy herë në një hapësirë boshe për të ngjitur një drejtori - View more + Shiko më shumë - Show edit tags flyout + Shfaq etiketat e redaktimit që fluturojnë - Show compression options + Shfaq opsionet e kompresimit - Show send to menu + Shfaq dërgo te menyja - Show option to open folders in a new tab + Shfaq opsionin për të hapur dosjet në një skedë të re - Show option to open folders in a new window + Shfaq opsionin për të hapur dosjet në një dritare të re - Quick access + Qasje e shpejtë - Enter your credentials to connect to: {0} + Futni kredencialet tuaja për t'u lidhur me: {0} - Enter network credentials + Futni kredencialet e rrjetit - Remember my credentials + Mbani mend kredencialet e mia - Network folder error + Gabim i dosjes së rrjetit - App version + Versioni i aplikacionit - Windows version + Versioni i Windows - Always + Gjithmonë - Permanent deletion only + Vetëm fshirje e përhershme - Never + kurrë - Tag color + Ngjyra e etiketës - New tag + Etiketë e re - Create new tag + Krijo etiketë të re - Loading... + Po ngarkohet... - Context menu options + Opsionet e menysë së kontekstit - Show file extensions - - - Show hidden items + Zgjerimet e skedarëve - Format... + Formati - Help + Ndihmë - Toggle full screen + Ekrani i plotë - Are you sure you want to delete this tag? + Jeni i sigurt që dëshironi ta fshini këtë etiketë? - Play all + Luaj - Height + Lartësia - Width + Gjerësia - Apply this action to all conflicting items + Zbato këtë veprim për të gjithë artikujt në konflikt - Open in Windows Terminal + Hapni në Windows Terminal - Open in Windows Terminal as administrator + Hape në Windows Terminal si administrator - Save + Ruaj - Multiselect + Zgjedhja e shumëfishtë - Reorder sidebar items + Renditni sërish artikujt e shiritit anësor - Hashes + Hashe - An error occurred during the calculation + Ndodhi një gabim gjatë llogaritjes - Failed to calculate the hash, please close the file and try again. + Llogaritja e hash-it dështoi, mbylle skedarin dhe provo sërish. - Hashes aren't available for online files + Hash-et nuk ofrohen për skedarët në linjë - Algorithm + Algoritmi - Hash value + Vlera hash - Select hashes to show + Zgjidh hash-et për të shfaqur - Calculating... + Po llogaritet... - Pinned items + Artikujt e gozhduar - Decrease size + Zvogëloni madhësinë - Increase size + Rritja e madhësisë - Toggle sort direction + Rendit drejtimin - Invalid name + Emër i pavlefshëm - Name must not be empty or start or end with a period. + Emri nuk duhet të jetë bosh ose të fillojë ose të përfundojë me një pikë. - Videos + Videot - Launch preview popup + Parapamje kërcyese - Toggle compact overlay + Ndrysho mbivendosjen kompakte - Open online help page in browser + Hapni faqen e ndihmës në internet në shfletuesin tuaj - Toggle full screen + Ndrysho modalitetin e ekranit të plotë - Enter compact overlay + Hyni në modalitetin e mbivendosjes kompakte - Exit compact overlay + Dilni nga modaliteti i mbivendosjes kompakte - Toggle compact overlay + Ndrysho modalitetin e mbivendosjes kompakte - Go to search box + Filloni kërkimin në OmniBar - Toggle whether to show hidden items + Ndrysho dukshmërinë e artikujve të fshehur + + + Ndrysho dukshmërinë e skedarëve me pika - Toggle whether to show file extensions + Ndrysho dukshmërinë e shtesave të skedarëve - Toggle the preview pane to view file previews + Ndryshoni panelin e shikimit paraprak për të parë pamjet paraprake të skedarëve - Toggle whether to show sidebar + Ndrysho nëse do të shfaqet shiriti anësor - Copy item(s) to clipboard + Kopjo selected {0, plural, one {item} other {items}} - Copy path of selected items to the clipboard + Kopjoni rrugën e drejtorisë aktuale + + + Kopjoni rrugën e artikujve të zgjedhur + + + Kopjoni rrugën e artikujve të zgjedhur me thonjëza - Copy path of selected items with quotes to the clipboard + Kopjoni rrugën e drejtorisë aktuale me thonjëza - Cut item(s) to clipboard + Pritini selected {0, plural, one {item} other {items}} - Paste item(s) from clipboard to current folder + Ngjitni artikujt në dosjen aktuale + + + Ngjitni artikujt në dosjen aktuale si shkurtore - Paste item(s) from clipboard to selected folder + Ngjitni artikujt në dosjen e zgjedhur + + + Ngjitni në dosjen e zgjedhur - Delete item(s) + Fshi selected {0, plural, one {item} other {items}} - Create new folder + Krijo dosje të re - Create new shortcut(s) to selected item(s) + Create new {0, plural, one {shortcut} other {shortcuts}} to selected {0, plural, one {item} other {items}} - Create new shortcut to any item + Krijo shkurtore të re për çdo artikull - Empty recycle bin + Zbrazni përmbajtjen e koshit të riciklimit - Open "Format Drive" menu for selected item + Hap menynë "Format Drive" për artikullin e zgjedhur - Restore selected item(s) from recycle bin + Rivendos selected {0, plural, one {item} other {items}} nga koshi i riciklimit - Restore all items from recycle bin + Rivendos të gjithë artikujt nga koshi i riciklimit - Open item(s) + Open {0, plural, one {artikull} other {items}} - Open item(s) with selected application + Open {0, plural, one {artikull} other {items}} me aplikacionin e zgjedhur - Open parent folder of searched item + Hap dosjen prind të artikullit të kërkuar - Refresh page contents + Rifresko përmbajtjen e faqes - Rename selected item + Riemërto artikullin e zgjedhur - Select all items + Zgjidhni të gjithë artikujt - Invert item selection + Inverto artikujt e zgjedhur - Clear item selection + Pastro artikujt e zgjedhur - Toggle item selection + Ndrysho zgjedhjen e artikullit - Share selected file(s) with others + Ndani selected {0, plural, one {file} other {files}} me të tjerët - Pin item(s) to the Start Menu + Pin {0, plural, one {artikull} other {items}} në menynë Start - Unpin item(s) from the Start Menu + Unpin {0, plural, one {artikull} other {items}} nga menyja Start - Pin folder(s) to Sidebar + Pin {0, plural, one {folder} other {folders}} në shiritin anësor - Unpin folder(s) from Sidebar + Unpin {0, plural, one {folder} other {folders}} nga Shiriti anësor - Set selected picture as desktop background + Cakto foton e zgjedhur si sfond të desktopit - Set selected pictures as desktop slideshow + Cakto fotografitë e zgjedhura si rrëshqitje në desktop - Set selected picture as lockscreen background + Cakto fotografinë e zgjedhur si sfond të ekranit të kyçur - Set selected picture as the app background + Cakto fotografinë e zgjedhur si sfond të aplikacionit + + + Instaloni fontin + + + Instaloni shoferin + + + Instaloni certifikatën - Install selected font(s) + Install selected {0, plural, one {font} other {fonts}} - Install driver(s) using selected inf file(s) + Install {0, plural, one {driver} other {drivers}} using selected inf {0, plural, one {file} other {files}} - Install selected certificate(s) + Install selected {0, plural, one {certificate} other {certificates}} - Run selected application as administrator + Ekzekutoni aplikacionin e zgjedhur si administrator - Run selected application as another user + Ekzekutoni aplikacionin e zgjedhur si një përdorues tjetër - Run selected PowerShell script + Ekzekutoni skriptin e zgjedhur të PowerShell - Launch preview in popup window + Nis pamjen paraprake në dritaren kërcyese - Create archive with selected item(s) + Create archive with selected {0, plural, one {item} other {items}} - Create 7z archive instantly with selected item(s) + Create 7z archive with selected {0, plural, one {item} other {items}} - Create zip archive instantly with selected item(s) + Create zip archive with selected {0, plural, one {item} other {items}} - Extract items from selected archive(s) to any folder + Ekstrakt selected {0, plural, one {archive} other {archives}} në çdo dosje - Extract items from selected archive(s) to current folder + Ekstrakt selected {0, plural, one {archive} other {archives}} në dosjen aktuale - Extract items from selected archive(s) to new folder + Ekstrakt selected {0, plural, one {archive} other {archives}} në dosje të re - Rotate selected image(s) to the left + Rotate selected {0, plural, one {image} other {images}} to the left - Rotate selected image(s) to the right + Rotate selected {0, plural, one {image} other {images}} to the right - Open settings page + Hapni faqen e cilësimeve - Open folder in terminal + Hapni dosjen në terminal - Open folder in terminal as administrator + Hapni dosjen në terminal si administrator - Decrease item size in the current view + Zvogëloni madhësinë e artikullit në pamjen aktuale - Increase item size in the current view + Rritni madhësinë e artikullit në pamjen aktuale - Switch to details view + Kalo te pamja e detajeve - - Switch to tiles view + + Kalo te pamja e kartave - Switch to list view + Kalo në pamjen e listës - List + Lista - Grid + Rrjeti - Switch to grid view + Kalo në pamjen e rrjetit - Switch to columns view + Kalo te pamja e kolonave - Switch views adaptively + Ndërroni pamjet në mënyrë adaptive - Sort items by name + Rendit artikujt sipas emrit - Sort items by date modified + Rendit artikujt sipas datës së modifikimit - Sort items by date created + Rendit artikujt sipas datës së krijimit - Sort items by size + Renditni artikujt sipas madhësisë - Sort items by type + Rendit artikujt sipas llojit - Sort items by sync status + Rendit artikujt sipas statusit të sinkronizimit - Sort items by tags + Rendit artikujt sipas etiketave - Sort items by original folder + Rendit artikujt sipas dosjes origjinale - Sort items by date deleted + Rendit artikujt sipas datës së fshirjes - Sort items in ascending order + Renditni artikujt në rend rritës - Sort items in descending order + Renditni artikujt në rend zbritës - Toggle item sort direction + Ndrysho drejtimin e renditjes së artikullit - List items without grouping + Listoni artikujt pa grupim - Group items by name + Gruponi artikujt sipas emrave - Group items by date modified + Gruponi artikujt sipas datës së modifikuar - Group items by date created + Gruponi artikujt sipas datës së krijimit - Group items by size + Gruponi artikujt sipas madhësisë - Group items by type + Gruponi artikujt sipas llojit - Group items by sync status + Gruponi artikujt sipas statusit të sinkronizimit - Group items by tags + Gruponi artikujt sipas etiketave - Group items by original folder + Gruponi artikujt sipas dosjes origjinale - Group items by date deleted + Gruponi artikujt sipas datës së fshirjes - Group items by folder path + Gruponi artikujt sipas rrugës së dosjes - Sort groups in ascending order + Rendit grupet në rend rritës - Sort groups in descending order + Rendit grupet në rend zbritës - Toggle group sort direction + Ndrysho drejtimin e renditjes në grup - Open new tab + Hap një skedë të re - Navigate backward in navigation history + Navigoni prapa - Navigate forward in navigation history + Navigoni përpara - Navigate up one directory + Lundroni lart një drejtori - Duplicate current tab + Dubliko skedën aktuale - Duplicate selected tab + Dubliko skedën e zgjedhur - Close tabs to the left of current tab + Mbyllni skedat në të majtë të skedës aktuale - Close tabs to the left of selected tab + Mbyllni skedat në të majtë të skedës së zgjedhur - Close tabs to the right of current tab + Mbyllni skedat në të djathtë të skedës aktuale - Close tabs to the right of selected tab + Mbyllni skedat në të djathtë të skedës së zgjedhur - Close tabs other than current tab + Mbyllni skeda të tjera nga skeda aktuale - Close tabs other than selected tab + Mbyllni skeda të tjera nga skeda e zgjedhur + + + Mbyllni të gjitha skedat duke përfshirë skedën aktuale - Reopen last closed tab + Rihap skedën e mbyllur së fundmi - Move to the previous tab + Kaloni në skedën e mëparshme - Move to the next tab + Kaloni në skedën tjetër - Close current tab + Mbyll skedën aktuale - Close active pane + Mbyllni panelin aktiv - Focus other pane + Fokusoni panelin tjetër - Switch focus to the non active pane + Kaloni fokusin në panelin tjetër - Toggle the sidebar + Ndrysho shiritin anësor Alt @@ -2693,19 +2813,19 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Shift + Ndërrimi Key name for hotkeys in menus. Use abbreviation if possible. - Win + Fito Key name for hotkeys in menus. Use abbreviation if possible. - Enter + Hyni Key name for hotkeys in menus. Use abbreviation if possible. - Space + Hapësirë Key name for hotkeys in menus. Use abbreviation if possible. @@ -2729,27 +2849,27 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Left + Majtas Key name for hotkeys in menus. Use abbreviation if possible. - Right + E drejta Key name for hotkeys in menus. Use abbreviation if possible. - Down + Poshtë Key name for hotkeys in menus. Use abbreviation if possible. - Up + Lart Key name for hotkeys in menus. Use abbreviation if possible. - Home + Shtëpi Key name for hotkeys in menus. Use abbreviation if possible. - End + fund Key name for hotkeys in menus. Use abbreviation if possible. @@ -2761,51 +2881,51 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Sep + shtator Key name for hotkeys in menus. Use abbreviation if possible. - Pause + Ndalo Key name for hotkeys in menus. Use abbreviation if possible. - Sleep + Flini Key name for hotkeys in menus. Use abbreviation if possible. - Clear + E qartë Key name for hotkeys in menus. Use abbreviation if possible. - Print + Printo Key name for hotkeys in menus. Use abbreviation if possible. - Help + Ndihmë Key name for hotkeys in menus. Use abbreviation if possible. - Mouse4 + Miu 4 Key name for hotkeys in menus. Use abbreviation if possible. - Mouse5 + Miu 5 Key name for hotkeys in menus. Use abbreviation if possible. - App + Aplikacioni Key name for hotkeys in menus. Use abbreviation if possible. - App1 + Aplikacioni 1 Key name for hotkeys in menus. Use abbreviation if possible. - App2 + Aplikacioni 2 Key name for hotkeys in menus. Use abbreviation if possible. - Mail + Postë Key name for hotkeys in menus. Use abbreviation if possible. @@ -2821,7 +2941,7 @@ Key name for hotkeys in menus. Use abbreviation if possible. - BrowserRefresh + Rifresko shfletuesin Key name for hotkeys in menus. Use abbreviation if possible. @@ -2829,11 +2949,11 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Kërko + Kërko Key name for hotkeys in menus. Use abbreviation if possible. - Favorites + Të preferuarat Key name for hotkeys in menus. Use abbreviation if possible. @@ -2849,7 +2969,7 @@ Key name for hotkeys in menus. Use abbreviation if possible. - NextTrack + Tjetër Track Key name for hotkeys in menus. Use abbreviation if possible. @@ -2857,7 +2977,7 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Mute + Hesht Key name for hotkeys in menus. Use abbreviation if possible. @@ -2869,439 +2989,457 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Change + Ndryshimi - Toggle selection + Ndrysho zgjedhjen - Show warning when changing file extensions + Shfaq paralajmërimin kur ndryshoni shtesat e skedarëve - This PC + Ky PC - Recycle Bin + Koshi i riciklimit - Untagged + I pa etiketuar - Moves to the previous tab + Skeda e mëparshme - Moves to the next tab + Skeda tjetër - Closes current tab + Mbyll skedën - Edit path + Redakto rrugën - Redo + Ribëje - Undo + Zhbër - Invalid location + Vendndodhja e pavlefshme - Are you sure you want to copy these files without their properties? + Jeni i sigurt që dëshironi t'i kopjoni këta skedarë pa vetitë e tyre? - These files have properties that can't be copied to the new location + Këta skedarë kanë veti që nuk mund të kopjohen në vendndodhjen e re - These files have properties that can't be moved to the new location + Këta skedarë kanë veti që nuk mund të zhvendosen në vendndodhjen e re - Are you sure you want to move these files without their properties? + Jeni i sigurt që dëshironi t'i zhvendosni këta skedarë pa vetitë e tyre? - Show checkboxes when selecting items + Shfaq kutitë e zgjedhjes kur zgjidhni artikuj - Focus path bar + Ndrysho shtegun në OmniBar - Create new item + Krijo artikull të ri - No groups or users have permission to access this object. However, the owner of this object can assign permissions. + Asnjë grup ose përdorues nuk ka leje për të hyrë në këtë objekt. Megjithatë, pronari i këtij objekti mund të caktojë leje. - Open the item's location + Hapni vendndodhjen e artikullit - Delete permanently + Fshije përgjithmonë - Delete item(s) permanently + Fshi selected {0, plural, one {item} other {items}} përgjithmonë - Play the selected media files + Luaj skedarët e zgjedhur të mediave - Undo the last file operation + Zhbër operacionin e fundit të skedarit - Redo the last file operation + Ribëni operacionin e fundit të skedarit - Location: + Vendndodhja: - Group items by month + Gruponi artikujt sipas muajve - Group items by year + Gruponi artikujt sipas vitit - Month + muaj - Day + Dita - Toggle unit for grouping by date + Ndrysho njësinë për grupimin sipas datës - Toggle grouping unit + Ndrysho njësinë e grupimit - Year + viti - Group by date unit + Grupi sipas njësisë së datës - Group items by day of date created + Gruponi artikujt sipas ditës së datës së krijimit - Group items by month of date created + Gruponi artikujt sipas muajit të datës së krijuar - Group items by year of date created + Gruponi artikujt sipas vitit të datës së krijuar - Group items by day of date deleted + Gruponi artikujt sipas ditës së datës së fshirë - Group items by month of date deleted + Gruponi artikujt sipas muajit të datës që u fshinë - Group items by year of date deleted + Gruponi artikujt sipas vitit të datës u fshinë - Group items by day of date modified + Gruponi artikujt sipas ditës së datës së modifikuar - Group items by month of date modified + Gruponi artikujt sipas muajit të datës së modifikuar - Group items by year of date modified + Gruponi artikujt sipas vitit të datës të modifikuar - Click 'Advanced permissions' to continue. + Klikoni "Lejet e avancuara" për të vazhduar. - You must have Read permissions to view the properties of this item. + Duhet të kesh leje leximi për të parë vetitë e këtij artikulli. - To try taking ownership of the item, which includes permission to view its properties, click Change above. + Për të provuar të merrni pronësinë e artikullit, i cili përfshin lejen për të parë vetitë e tij, kliko "Ndrysho" më lart. - Unable to display permissions. + Nuk mund të shfaqen lejet. - Leave my changes on '{0}' + Lëri ndryshimet e mia në '{0}' - Discard my changes + Hidhni poshtë ndryshimet e mia - Bring my changes to '{0}' + Sill ndryshimet e mia në '{0}' - You have uncommitted changes on this branch. What would you like to do with them? + Ju keni ndryshime të pazbatuara në këtë degë. Çfarë do të dëshironit të bënit me ta? + + + Ju keni një bashkim të vazhdueshëm me konflikte të pazgjidhura. Ju lutemi zgjidhni konfliktet ose anuloni bashkimin për të ndërruar degët. + + + Anuloni bashkimin dhe kaloni në '{0}' + + + Qëndroni në '{0}' dhe zgjidhni konfliktet - Switch branch + Ndërroni degën - Branches + Degët - Switch + Ndërro - New branch + Dega e re - Invalid branch name + Emër i pavlefshëm dege - Create branch + Krijo degë - Create branch + Krijo degë - Based on + Bazuar në - Switch to new branch + Kalo në degë të re - Create a folder with the currently selected item(s) + Create a folder with the currently selected {0, plural, one {item} other {items}} - Open properties + Cilësimet + + + Hapni vetitë e File Explorer - Open properties window + Hap dritaren e vetive + + + Hap dritaren e vetive të File Explorer - Locals + vendasit - Remotes + Telekomanda - Translate on Crowdin + Përkthe në Crowdin - Fetch + Merr - Pull + Tërhiqe - Run git fetch + Ekzekuto git fetch + + + Klononi një depo git Run git pull - Preview + Parapamje - Git status + Statusi Git - Git error + Gabim Git - git pull failed due to a timeout error + tërheqja e git dështoi për shkak të një gabimi kohor - (multiple values) + (vlera të shumta) Text indicating that multiple selected files have different metadata values. - Author + Autori - Date committed + Data e kryerjes - Commit message + Detyroni mesazh - Commit SHA + Angazhoni SHA Git - Acrylic + Akrilik - Mica + Mika - Mica Alt + Mika Alt - Backdrop Material + Sfondi - Solid + Të ngurta - Manage branches + Menaxhoni degët - Push + Shtyni Run git push - Sync + Sinkronizimi - Run git pull and then git push + Run git pull dhe më pas git push - {0} outgoing / {1} incoming commits + {1} në dalje / {1} kryerje hyrëse - Connect to GitHub + Lidhu me GitHub - Enter the following code at the link below to start working with GitHub repositories. + Futni kodin e mëposhtëm në lidhjen më poshtë për të filluar punën me depot e GitHub. - Files cannot access GitHub right now. + Skedarët nuk mund të hyjnë në GitHub për momentin. - - Open folder in VS Code + + Hap dosjen në {0} - - Open the current directory in Visual Studio Code + + Hap direktorinë aktuale në {0} - - Open repo in VS Code + + Hap depon në {0} - - Open the root of the Git repo in Visual Studio Code + + Hapni rrënjën e repos Git në {0} - Copy code + Kopjo kodin - Added + Shtuar - Deleted + Fshirë - Modified + Modifikuar - Untracked + E pa gjurmuar - Unable to display current owner. + Nuk mund të shfaqet pronari aktual. - Great! You are now authorized. + E shkëlqyeshme! Tani jeni i autorizuar. - Initialize repo + Inicializoni repo - Initialize a Git repository + Inicializoni dosjen aktuale si një depo git - Remote Repository Name + Emri i depove në distancë - Current Branch + Dega aktuale - Path column + Kolona e rrugës - Sort items by path + Rendit artikujt sipas rrugës - Open directory in new pane + Hapni drejtorinë e zgjedhur në një panel të ri - Open directory in new tab + Hapni drejtorinë e zgjedhur në një skedë të re - Open directory in new window + Hapni drejtorinë e zgjedhur në një dritare të re - Open all + Hap të gjitha - Open all tagged items + Hapni të gjithë artikujt e etiketuar - Drive ({0}) + Me makinë ({0}) {0} is the drive letter. - Invalid command + Komanda e pavlefshme - '{0}' is not recognized as a command. + '{0}' nuk njihet si komandë. - Command not executable + Komanda nuk është e ekzekutueshme - The '{0}' command is not ready to be executed. + Komanda '{0}' nuk është gati për t'u ekzekutuar. - Command palette + Paleta e komandave - Open command palette + Hapni paletën e komandave në OmniBar - Start in: + Filloni në: - Turn on BitLocker + Aktivizo BitLocker - Manage BitLocker + Menaxho BitLocker - The following items are too large to be copied to this drive + Artikujt e mëposhtëm janë shumë të mëdhenj për t'u kopjuar në këtë disk - Format drive + Formatoni diskun - Don't show again + Mos u shfaq më - Files is running as administrator + Files po ekzekutohet si administrator - Due to a limitation with Windows and WinAppSdk, drag and drop isn't available when running Files as admin. If you wish to use drag and drop, you can work around this limitation by opening UAC (User Account Control) from the Start Menu and selecting the third level, and restarting Windows. Otherwise, you can keep using Files without drag & drop. + Për shkak të një kufizimi me Windows dhe WinAppSdk, tërhiq dhe lësho nuk ofrohet kur ekzekuton Files si administrator. Nëse dëshironi të përdorni drag and drop, mund të zgjidhni këtë kufizim duke hapur UAC (User Account Control) nga menyja Start dhe duke zgjedhur nivelin e tretë dhe duke rifilluar Windows. Përndryshe, mund të vazhdoni të përdorni skedarët pa zvarritje dhe lëshim. - Leave app running in the background when the window is closed + Lëreni aplikacionin të funksionojë në sfond kur dritarja është e mbyllur - Blue + Blu One of the custom color themes - Blue Gray + Blu gri One of the custom color themes - Brick Red + E kuqe me tulla One of the custom color themes - Camouflage + Kamuflazh One of the custom color themes - Cool Blue Bright + E ftohtë Blu e ndritshme One of the custom color themes - Gray + Gri One of the custom color themes - Gray Dark + Gri E errët One of the custom color themes - Green + E gjelbër One of the custom color themes @@ -3309,7 +3447,7 @@ One of the custom color themes - Mint Light + Drita e nenexhikut One of the custom color themes @@ -3317,15 +3455,15 @@ One of the custom color themes - Orange Bright + Portokalli e ndritshme One of the custom color themes - Overcast + I mbuluar me re One of the custom color themes - Red + E kuqe One of the custom color themes @@ -3333,451 +3471,522 @@ One of the custom color themes - Seafoam + Shkumë deti One of the custom color themes - Storm + Stuhi One of the custom color themes - Violet Red Light + Drita e kuqe vjollce One of the custom color themes - Yellow Gold + Ari i Verdhe One of the custom color themes - Clear completed + Pastrimi i përfunduar - Name: + Emrit: - {0} of {1} processed + {1} nga {1} të përpunuara Shown in a StatusCenter card. Used as "64 MB of 1 GB processed" - items + artikuj - Files can't initialize this directory as a Git repository. + Skedarët nuk mund ta inicializojnë këtë direktori si një depo Git. - Contributing Artist + Artist kontribues - Add page + Shto faqe - Processing items... + Po përpunohen artikujt... + + + Po zbulohen artikujt... - Speed: + Shpejtësia: - Canceled compressing {0} items to "{1}" + Kompresimi i {1} artikujve në "{1}" u anulua Shown in a StatusCenter card. - Canceled compressing {0} items from "{1}" to "{2}" + Kompresimi i {2} artikujve u anulua nga "{2}" në "{2}" Shown in a StatusCenter card. - Compressed {0} item(s) to "{1}" + Compressed {0, plural, one {# artikull} other {# artikuj}} te "{1}" Shown in a StatusCenter card. - Compressed {0} item(s) from "{1}" to "{2}" + Compressed {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error compressing {0} item(s) to "{1}" + Gabim compressing {0, plural, one {# artikull} other {# artikuj}} te "{1}" Shown in a StatusCenter card. - Failed to compress {0} item(s) from "{1}" to "{2}" + Failed to compress {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Compressing {0} item(s) to "{1}" + Compressing {0, plural, one {# artikull} other {# artikuj}} te "{1}" Shown in a StatusCenter card. - Compressing {0} item(s) from "{1}" to "{2}" + Compressing {0, plural, one {# item} other {# items}} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Klonimi u anulua {1} në "{1}" + Shown in a StatusCenter card. + + + Klonimi u anulua {2} nga "{2}" në "{2}" + Shown in a StatusCenter card. + + + Klonuar "{1}" në "{1}" + Shown in a StatusCenter card. + + + Klonuar {2} nga "{2}" në "{2}" + Shown in a StatusCenter card. + + + Gabim në klonimin "{1}" në "{1}" + Shown in a StatusCenter card. + + + Dështoi në klonimin e {2} nga "{2}" në "{2}" + Shown in a StatusCenter card. + + + Klonimi "{1}" në "{1}" + Shown in a StatusCenter card. + + + Klonimi {2} nga "{2}" në "{2}" + Shown in a StatusCenter card. + + + Instalimi i shkronjave {0} u anulua + Shown in a StatusCenter card. + + + Anuluar installing {0, plural, one {# font} other {# fontet}} nga "{1}" + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Error installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Failed to install {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} from "{1}" Shown in a StatusCenter card. - Canceled copying {0} item(s) to "{1}" + Anuluar copying {0, plural, one {# artikull} other {# artikuj}} te "{1}" Shown in a StatusCenter card. - Canceled copying {0} item(s) from "{1}" to "{2}" + Canceled copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Copied {0} item(s) to "{1}" + Copied {0, plural, one {# artikull} other {# artikuj}} te "{1}" Shown in a StatusCenter card. - Copied {0} item(s) from "{1}" to "{2}" + Copied {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error copying {0} item(s) to "{1}" + Gabim copying {0, plural, one {# artikull} other {# artikuj}} te "{1}" Shown in a StatusCenter card. - Failed to copy {0} item(s) from "{1}" to "{2}" + Failed to copy {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Copying {0} item(s) to "{1}" + Copying {0, plural, one {# artikull} other {# artikuj}} te "{1}" Shown in a StatusCenter card. - Copying {0} item(s) from "{1}" to "{2}" + Copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. + + Discovered {0, plural, one {# item} other {# items}} + Shown in a StatusCenter card during file discovery phase. + - Canceled extracting "{0}" to "{1}" + U anulua nxjerrja e "{1}" në "{1}" Shown in a StatusCenter card. - Canceled extracting "{0}" from "{1}" to "{2}" + U anulua nxjerrja e "{2}" nga "{2}" në "{2}" Shown in a StatusCenter card. - Extracted "{0}" to "{1}" + U nxorr "{1}" në "{1}" Shown in a StatusCenter card. - Extracted "{0}" from "{1}" to "{2}" + U nxorr "{2}" nga "{2}" në "{2}" Shown in a StatusCenter card. - Error extracting "{0}" to "{1}" + Gabim gjatë nxjerrjes së "{1}" në "{1}" Shown in a StatusCenter card. - Failed to extract "{0}" from "{1}" to "{2}" + Dështoi nxjerrja e "{2}" nga "{2}" në "{2}" Shown in a StatusCenter card. - Extracting "{0}" to "{1}" + Duke nxjerrë "{1}" në "{1}" Shown in a StatusCenter card. - Extracting "{0}" from "{1}" to "{2}" + Duke nxjerrë "{2}" nga "{2}" në "{2}" Shown in a StatusCenter card. - Canceled deleting {0} item(s) from "{1}" + Anuluar deleting {0, plural, one {# artikull} other {# artikuj}} nga "{1}" Shown in a StatusCenter card. - Deleted {0} item(s) from "{1}" + Deleted {0, plural, one {# artikull} other {# artikuj}} nga "{1}" Shown in a StatusCenter card. - Error deleting {0} item(s) from "{1}" + Gabim deleting {0, plural, one {# artikull} other {# artikuj}} nga "{1}" Shown in a StatusCenter card. - Failed to delete {0} item(s) from "{1}" + Dështoi në delete {0, plural, one {# artikull} other {# artikuj}} nga "{1}" Shown in a StatusCenter card. - Deleting {0} item(s) from "{1}" + Deleting {0, plural, one {# artikull} other {# artikuj}} nga "{1}" Shown in a StatusCenter card. - Canceled moving {0} item(s) to "{1}" + Anuluar moving {0, plural, one {# artikull} other {# artikuj}} te "{1}" Shown in a StatusCenter card. - Canceled moving {0} item(s) from "{1}" to "{2}" + Canceled moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Moved {0} item(s) to "{1}" + Moved {0, plural, one {# artikull} other {# artikuj}} te "{1}" Shown in a StatusCenter card. - Moved {0} item(s) from "{1}" to "{2}" + Moved {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Moving {0} item(s) to "{1}" + Moving {0, plural, one {# artikull} other {# artikuj}} te "{1}" Shown in a StatusCenter card. - Moving {0} item(s) from "{1}" to "{2}" + Moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error moving {0} item(s) to "{1}" + Gabim moving {0, plural, one {# artikull} other {# artikuj}} te "{1}" Shown in a StatusCenter card. - Failed to move {0} item(s) from "{1}" to "{2}" + Failed to move {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Canceled emptying Recycle Bin + Zbrazja e koshit të riciklimit u anulua Shown in a StatusCenter card. - Emptied Recycle Bin + Koshi i riciklimit i zbrazur Shown in a StatusCenter card. - Emptying Recycle Bin + Zbrazja e koshit të riciklimit Shown in a StatusCenter card. - Error emptying Recycle Bin + Gabim gjatë zbrazjes së koshit të riciklimit Shown in a StatusCenter card. - Failed to empty Recycle Bin + Zbrazja e koshit të riciklimit dështoi Shown in a StatusCenter card. - Preparing the operation... + Përgatitja e operacionit... Shown in a StatusCenter card. - {0}/{1} item(s) processed + {0}/{1, plural, one {# item} other {# items}} processed Shown in a StatusCenter card. Used as "8/20 items processed" - Unblock downloaded file + Zhblloko skedarin e shkarkuar - No ongoing file operations + Nuk ka operacione të vazhdueshme të skedarëve - The option to open Files on Windows Startup is not available due to your system settings or group policy. To re-enable, open the startup page in Task Manager. + Opsioni për të hapur skedarët në fillimin e Windows nuk është i disponueshëm për shkak të cilësimeve të sistemit ose politikës së grupit. Për ta riaktivizuar, hapni faqen e nisjes në Task Manager. - Failed to restore items from Recycle Bin + Rivendosja e artikujve nga koshi i riciklimit dështoi - Failed to set the background wallpaper + Vendosja e sfondit të sfondit dështoi + + + Dështoi në hapjen e skedarit të cilësimeve - Delete Git branch + Fshi degën Git - Are you sure you want to permanently delete "{0}" branch? + Jeni i sigurt që dëshironi të fshini përgjithmonë degën "{0}"? - Connected to GitHub + Lidhur me GitHub - Logout + Dilni - Connect to GitHub + Lidhu me GitHub - Login + Identifikohu - Tags are currently only compatible on drives formatted as NTFS. + Etiketat janë aktualisht të përputhshme vetëm në disqet e formatuar si NTFS. - There was an error applying this tag + Pati një gabim gjatë aplikimit të kësaj etikete - Extract items from selected archive(s) to current folder for single-item archive, or to new folder for multi-item archive + Ekstrakt selected {0, plural, one {archive} other {archives}} këtu për një artikull të vetëm ose në dosjen e re për shumë artikuj - Extract here (Smart) + Ekstrakt këtu (Smart) - Sort files and folders together + Rendit skedarët dhe dosjet së bashku - Sort files and folders together + Renditni skedarët dhe dosjet në të njëjtën listë - Sort files first + Rendit skedarët së pari - Sort files first then folders + Renditni skedarët së pari, pastaj dosjet - Sort folders first + Renditni dosjet së pari - Sort folders first then files + Renditni së pari dosjet dhe më pas skedarët - Sort priority + Rendit prioritetin - Failed to rotate the image + Rrotullimi i imazhit dështoi - Restart + Rinis - Quit + Hiq dorë - Failed to share items + Ndarja e artikujve dështoi - Scroll to previous folder when navigating up + Lëvizni te dosja e mëparshme kur lundroni lart - Open new window + Hap dritaren e re - Change album cover + Ndrysho kopertinën e albumit - Failed to rename item + Riemërtimi i artikullit dështoi - Editing "{0}" requires additional permissions + Redaktimi i "{0}" kërkon leje shtesë - Edit permissions + Redakto lejet - Files is still running in the background to improve startup performance. + Files po ekzekutohet ende në sfond për të përmirësuar performancën e nisjes. - Where did Files go? + Ku shkuan Files? - Questions & discussions - - - Additional sizes are not yet available for the Tiles View. + Pyetje dhe diskutime - Compact + Kompakt Used to describe layout sizes - Small + I vogël Used to describe layout sizes - Medium + E mesme Used to describe layout sizes - Medium + + E mesme + Used to describe layout sizes - Medium ++ + E mesme ++ Used to describe layout sizes - Medium +++ + E mesme +++ Used to describe layout sizes - Medium ++++ + E mesme ++++ Used to describe layout sizes - Medium +++++ + E mesme +++++ Used to describe layout sizes - Large + I madh Used to describe layout sizes - Large + + I madh + Used to describe layout sizes - Large ++ + E madhe ++ Used to describe layout sizes - Large +++ + I madh +++ Used to describe layout sizes - Extra large + Tepër i madh Used to describe layout sizes - Details view + Pamja e detajeve - Layout type + Lloji i paraqitjes - Actions + Veprimet - Commands + Komandat - Add command + Shto komandë - Restore defaults + Rikthe standardet - Choose an action + Zgjidhni një veprim - Are you sure you want to restore the default key bindings? This action cannot be undone. + Jeni i sigurt që dëshironi të rivendosni lidhjet e paracaktuara të çelësave? Ky veprim nuk mund të zhbëhet. - This key binding is already being used, please choose a different key binding to continue. + Kjo lidhje kyçe po përdoret tashmë, ju lutemi zgjidhni një lidhje tjetër çelësi për të vazhduar. - The key binding you choose cannot be used, please try again using a different key binding. + Lidhja e çelësit që zgjidhni nuk mund të përdoret, ju lutemi provoni përsëri duke përdorur një lidhje tjetër çelësi. - Customized + Të personalizuara - Adaptive layout is not available when preferences are synced, the default layout has been changed to Details. + Struktura përshtatëse nuk disponohet kur preferencat sinkronizohen, paraqitja e paracaktuar është ndryshuar në Detaje. - Background image + Imazhi i sfondit - Bottom + Poshtë Image alignment type - Center + Qendra Image alignment type - Fill + Plotësoni Image stretch type - Horizontal alignment + Rreshtimi horizontal - Image fit + Përshtatja e imazhit - Left + Majtas Image alignment type - Opacity + Opaciteti - Right + E drejta Image alignment type @@ -3785,121 +3994,387 @@ Image alignment type - Uniform + Uniformë Image stretch type - Uniform to fill + Uniformë për të mbushur Image stretch type - Vertical alignment + Rreshtimi vertikal - Thin Acrylic + Akrilik i hollë This is a type of backdrop for the application background - Show for all locations + Shfaq për të gjitha vendndodhjet Setting where users can choose to display "Open IDE" button for Git Repos - Developer tools + Mjetet e zhvilluesit - Configure the "Open IDE" button on the status bar + Konfiguro butonin "Open IDE" në shiritin e statusit - Show for Git repos + Shfaq për depot e Git Setting where users can choose to display "Open IDE" button for all locations. - Application extension + Zgjatja e aplikacionit This is the friendly name for DLL files. - ICO File + Skedari ICO This is the friendly name for ICO files. + + Skedari ICL + This is the friendly name for ICL files. + - Zip File + Skedar Zip This is the friendly name for ZIP files. - Bitmap Files + Skedarët Bitmap This is the friendly name for bitmap files. + + Skedarët e imazhit + This is the friendly name for image files. + Num - Network locations + Vendndodhjet e rrjetit - There are no network locations. If you don't use network locations, you can disable the widget. + Nuk ka vendndodhje rrjeti. Nëse nuk përdorni vendndodhjet e rrjetit, mund ta çaktivizoni miniaplikacionin. - Disable + Çaktivizo - Edit in Notepad + Redakto në Notepad - Edit the selected file in Notepad + Redaktoni skedarin e zgjedhur në Notepad - Dimensions: + Dimensionet: - Status: + Statusi: - Show Toolbar - Setting that controls if the Toolbar is shown in the main view + Shfaq shiritin e veglave + Setting that controls if the toolbar is shown in the main view - Tab actions menu + Menyja e veprimeve të skedës - - Add vertical pane + + Ndarë vertikalisht - Add a vertical pane + Ndarja e panelit vertikalisht - - Add horizontal pane + + Ndani horizontalisht - - Add a horizontal pane + + Ndani panelin horizontalisht - Arrange vertically + Rregulloni vertikalisht - Arrange panes vertically + Rregulloni xhamat vertikalisht - Arrange horizontally + Rregulloni horizontalisht - Arrange panes horizontally + Rregulloni xhamat horizontalisht - - Add pane + + Xhami i ndarë - Show tab actions button in the title bar + Shfaq butonin e veprimeve të skedës në shiritin e titullit - Arrange panes + Rregulloni xhamat - - Default pane arrangement + + Drejtimi i parazgjedhur i ndarjes së panelit të dyfishtë + + + Modaliteti i panelit të dyfishtë - Horizontal + Horizontale - Vertical + Vertikale + + + Shfaq ikonën Files në System Tray + + + Fijet e CPU-së + + + Navigoni në faqen kryesore + + + Shiritat e veglave + + + ID e përdoruesit + + + Riemërtimi në masë + + + Kompresoni përmbajtjen + + + Shfaq opsionin për të krijuar rrymë alternative të të dhënave + + + Krijo rrymë alternative të të dhënave + + + Create alternate data stream for the selected {0, plural, one {item} other {items}} + + + Fut emrin e transmetimit të të dhënave + + + Pati një gabim në krijimin e rrjedhës alternative të të dhënave + + + Ju lutemi vini re se transmetimet alternative të të dhënave funksionojnë vetëm në disqet e formatuar si NTFS. + + + Transmetimet alternative të të dhënave janë aktualisht të fshehura + + + Dëshironi të shfaqni transmetime alternative të të dhënave? Mund ta modifikoni këtë cilësim në çdo kohë nga faqja e cilësimeve të skedarëve dhe dosjeve. + + + Menaxho etiketat + + + Në dispozicion + + + Gjithsej + + + Gjithmonë ndërroni fokusin në skedën e krijuar rishtazi + + + Ndrysho panelin e raftit + + + Ndrysho dukshmërinë e panelit të raftit + + + Shfaq ndryshimin e panelit të raftit në shiritin e adresave + + + Raft + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + Pastro artikujt + + + Hiqeni nga rafti + + + Shto në raft + Tooltip that displays when dragging items to the Shelf Pane + + + Fut një hash për të krahasuar + Placeholder that appears in the compare hash text box + + + Ndeshjet {0} + Appears when two compared hashes match, e.g. "Matches SHA256" + + + Nuk u gjet asnjë përputhje + Appears when two compared hashes don't match + + + Rruga ose pseudonimi + + + Rrugë e pavlefshme + + + Integrimi i testit + + + {0} nuk mund të gjendej. Ju lutemi kontrolloni cilësimet tuaja dhe provoni përsëri. + + + IDE e konfiguruar nuk mund të gjendet + + + Hap cilësimet + + + Kodi Visual Studio + + + Fut një shteg ose nis pseudonimin + + + Ju lutemi shkruani një emër për IDE + + + Repo e klonimit + Clone repo dialog title + + + Klon + Primary action button in the clone repo dialog + + + URL e depove + URL textbox header in the clone repo dialog + + + Repo nuk mund të klonohet + Cannot clone repo dialog title + + + Krahasoni një skedar + Button that appears in file hash properties that allows the user to compare two files + + + Formati i madhësisë + + + Binar + + + dhjetore + + + Ju mund të shtoni seksione në shiritin anësor duke klikuar me të djathtën dhe duke zgjedhur seksionet që dëshironi të shtoni + + + Zvarritni skedarët ose dosjet këtu për të bashkëvepruar me ta nëpër skeda të ndryshme + + + Fut një shteg për të lundruar te... + + + Gjeni veçori dhe komanda... + + + Kërko për skedarë dhe dosje... + + + Gjatë operacioneve të skedarëve + + + Shfaq butonin e qendrës së statusit + + + Unaza e përparimit të qendrës së statusit + Screen reader name for the status center progress ring + + + Skedarët e ikonave + This is the friendly name for a variety of different icon files. + + + Shfaq copëzat e bukës së shtegut të palosur + + + Shfaq dosjet në {0} + + + Shfaq dosjet në Home + + + Nuk ka komanda që përmbajnë {0} + + + Shih më shumë + + + Filtrimi për + + + Emri i skedarit + + + Nënshkrimet + + + Lista e nënshkrimeve + + + Lëshuar nga: + + + Lëshuar për: + + + E vlefshme nga: + + + E vlefshme për: + + + Nuk u gjet asnjë nënshkrim. + + + Shfaq opsionin për të hapur dosjet në Windows Terminal + + + Hap skedarin e regjistrit + + + Hapni skedarin e regjistrit në redaktorin tuaj të paracaktuar + + + Hapni vendndodhjen e skedarit të regjistrit në menaxherin tuaj të paracaktuar të skedarëve + + + Skedari i regjistrit nuk mund të hapet + + + Shfaq shiritin e statusit + + + Vetëm në pamjen e kolonave + + + E re {0} + + + Aktivizo lëvizjen e qetë + + + Lëvizje + + + Shfaq opsionin për të gozhduar në shiritin anësor + + + Shfaq opsionin për të gozhduar në menynë e fillimit \ No newline at end of file diff --git a/src/Files.App/Strings/sr-Cyrl/Resources.resw b/src/Files.App/Strings/sr-Cyrl/Resources.resw index fd4fee9a4466..c73c46bdf83c 100644 --- a/src/Files.App/Strings/sr-Cyrl/Resources.resw +++ b/src/Files.App/Strings/sr-Cyrl/Resources.resw @@ -123,9 +123,15 @@ Копирај путању + + Копирај путању ставке + Копирај путању са наводницима + + Копирај путању ставке са наводницима + Потражи @@ -211,13 +217,13 @@ Прикажи скривене датотеке и фасцикле - Прикажи датотеке без екстензије + Прикажи фајлове који почињу са тачком Изглед - Background color + Позадинска боја Напредна подешавања @@ -249,6 +255,9 @@ Налепи + + Пречица за лепљење + Нов @@ -291,8 +300,8 @@ Унеси нови назив - - Подеси назив + + Креирај нову {0} Светла тема @@ -300,6 +309,9 @@ Тамна тема + + Користи системска подешавања + Формат по апликацији @@ -340,7 +352,7 @@ Постави као позадина закључаног екрана - Set as app background + Постави као позадина апликације Нисмо могли да обришемо ову датотеку @@ -349,13 +361,13 @@ Молимо привежите потребан диск како бисте приступили овој ставци. - Drive unplugged + Диск је искључен Датотека којој покушавате да приступите је можда премештена или обрисана. - Cannot open properties for this file + Није могуће приказати својства за ову датотеку Датотека којој покушавате да приступите је можда премештена или обрисана. @@ -373,7 +385,7 @@ Датотеку којој покушавате да приступите тренутно користи програм {0} - The file you are attempting to access is currently being used by another application + Датотека којој покушавате да приступите се тренутно користи од стране друге апликације Датотека је у употреби @@ -385,43 +397,43 @@ Само за читање - {0, plural, one {# day ago} other {# days ago}} + {0, plural, one {# дан} other {# дана}} - 1 day ago + Пре једног дана - {0} days ago + Пре {0} дана - {0, plural, one {# hour ago} other {# hours ago}} + {0, plural, one {пре # сат} few {пре # сата} other {пре # сати}} - 1 hour ago + Пре сат времена - {0} hours ago + Пре {0} сата - {0, plural, one {# minute ago} other {# minutes ago}} + {0, plural, one {пре # минут} other {пре # минута}} - 1 minute ago + Пре једног минута - {0} minutes ago + Пре {0} минута - {0, plural, one {# second ago} other {# seconds ago}} + {0, plural, one {пре # секунд} few {пре # секунде} other {пре # секунди}} - 1 second ago + Пре једне секунде - {0} seconds ago + Пре {0} секунди - Now + Сада Затражена радња није подржана @@ -430,13 +442,13 @@ {0} слободно од {1} - Send to + Пошаљи Име ставке не сме да садржи следеће карактере: \ / : * ? " < > | - The item name specified is invalid + Дато име ставке није валидно {0, plural, one {# item selected} few {# items selected} other {# items selected}} @@ -477,127 +489,127 @@ B - {0, number} {0, plural, one {file} other {files}}, {1, number} {1, plural, one {folder} other {folders}} + {0, number} {0, plural, one {фајл} other {фајлова}} и {1, number} {1, plural, one {фасцикла} few {фасцикле} other {фасцикли}} - {0, number} {0, plural, one {file} other {files}}, {1, number} {1, plural, one {folder} other {folders}} from {2, number} {2, plural, one {location} other {locations}} + {0, number} {0, plural, one {фајл} other {фајлова}}, {1, number} {1, plural, one {фасцикла} few {фасцикле} other {фасцикли}} са {2, number} {2, plural, one {локације} few{локације} other {локација}} - Run as another user + Покрени као други корисник - All type of {0} + Сви типови {0} - Different types + Друге врсте - All in {0} + Све у {0} - Used space: + Искоришћени простор: - Free space: + Слободни простор: - Capacity: + Капацитет: - File system: + Систем датотека: - Recent + Недавно - Move tab here + Помери картицу овде - Status - - - Језик Windows окружења + Стање - No results + Нема резултата - Can't access any items to display + Не може да се приступи било којим ставкама да се прикажу - Move to {0} + Помери у {0} - Copy to {0} + Копирај у {0} + + + Копирај у {0} - Create shortcut + Направи пречицу Отвори извор датотеке - Arguments: + Аргументи: - Destination: + Одредиште: - Shortcut type: + Врста пречице: - Web link + Веб линк Општа - Shortcut + Пречицa - Internet shortcut + Интернет пречица - {0} - shortcut + {0} - пречица - Files ran into a problem that the developers didn't prepare for yet. + Files је наишао на проблем за који се програмери нису спремили. - Something went wrong! + Нешто је пошло по злу! - Report this issue + Пријави овај проблем - The item referenced is either invalid or inaccessible.{0}Error message:{0}{1} + Ставка која је референцирана или није валидна или је неприступачна. {0}Порука грешке: {0}{1} - Invalid item + Неважећа ставка - Version: + Верзија: - There's nothing to share right now... + Нема ништа да се подели тренутно... - The items you've selected will be shared + Ставке које сте изабрали ће бити подељене - The selected item will be shared + Изабрана ставка ће бити дељена - Sharing {0} + Дељење {0} - Sharing {0} {1} + Дељење {0} {1} - The item you're attempting to rename no longer exists. Please verify the correct location of the item you need to rename. + Ставка коју покушавате да преименујете више не постоји. Молимо вас да верификујете тренутну локацију ставке коју хоћете да преименујете. - Item no longer exists + Ставка више не постоји Унето име садржи грешке. Молимо проверите унето име и покушајте поново. @@ -618,7 +630,7 @@ Потребно је поново покренути програм како бисте применили ова подешавања. Да ли желите да поново покренете програм? - Sponsor us on GitHub + Спонзориши нас на GitHub-у Нисмо могли да креирамо ову ставку @@ -729,7 +741,7 @@ Latitude Decimal - Altitude + Висина Датум сликања @@ -741,7 +753,7 @@ Модел камере - Exposure Time + Време Експозиције Focal Length @@ -753,7 +765,7 @@ Имена људи - Channel Count + Број канала Формат @@ -771,7 +783,7 @@ Извођач - Beats Per Minute + Ударца по секунди Композитор @@ -858,7 +870,7 @@ Број ревизије - Version + Верзија Датум прављења @@ -998,6 +1010,9 @@ Резултати претраге "{0}" у фасцикли {1} + + Search results for `{0}` + Details @@ -1052,9 +1067,6 @@ Details (Ctrl+Shift+1) - - Tiles (Ctrl+Shift+2) - Date deleted @@ -1080,10 +1092,25 @@ Toggle the info pane - Toggle the info pane to view the detail/preview panes + Toggle visibility of the detail/preview panes - Toggle the Toolbar + Toggle toolbar + + + Toggle visibility of the toolbar + + + Toggle filter header + + + Toggle visibility of the filter header + + + Toggle dual pane + + + Toggle dual pane mode Брзи преглед датотеке није доступан @@ -1238,6 +1265,12 @@ Отвори подешавања Storage Sense + + Open the Storage Sense page in Windows Settings + + + Cleanup + Копирај @@ -1380,7 +1413,7 @@ {0} датотеке - Отвори малопре затворени таб + Reopen tab Преименовање @@ -1587,7 +1620,7 @@ Replace all child object permissions entries with inheritable permission entries from this object - Close active pane + Close pane Enter compact overlay @@ -1646,15 +1679,6 @@ Home - - Ctrl+Shift+1 - - - Ctrl+Shift+2 - - - Ctrl+Shift+6 - Архива је успешно отпакована. @@ -1712,8 +1736,8 @@ Columns - - Tiles + + Cards Open folders in new tab @@ -1749,7 +1773,7 @@ Tag - Open items with a single click + Open files with a single click Folder path @@ -1826,6 +1850,18 @@ Register this program for restart + + Start window + + + Normal window + + + Minimized + + + Maximized + Покрени као администратор @@ -1853,6 +1889,9 @@ This option modifies the system registry and can have unexpected side effects on your device. Continue at your own risk. + + The flatten operations are permanent and not recommended. Continue at your own risk. + Create Library @@ -1931,6 +1970,9 @@ Close other tabs + + Close all tabs + Музика @@ -1985,11 +2027,23 @@ Понашање - - Review Files + + Hello! - - Would you like to review Files? + + Enjoying Files? Please consider reviewing in the Microsoft Store. + + + Enjoying Files? Please consider supporting the project on GitHub. + + + Sponsor + + + Rate us + + + Dismiss Set as background @@ -2018,8 +2072,8 @@ Lockscreen - - Open folders with a single click in the Columns Layout + + Open folders with a single click При отварању датотека @@ -2027,6 +2081,21 @@ Compress + + Move all contents from subfolders into the selected location + + + Flatten folder + + + Flatten + + + Flattening a folder will move all contents from its subfolders to the selected location. This operation is permanent and cannot be undone. By using this experimental feature, you acknowledge the risk and agree not to hold the Files team responsible for any data loss. + + + Show flatten options + Селектуј датотеке и фасцикле преласком миша @@ -2037,7 +2106,7 @@ Restore all items - Do you want to restore the {0} selected item(s)? + Do you want to restore the {0, plural, one {selected item} other {{0} selected items}}? Restore selection @@ -2060,6 +2129,12 @@ Archive password + + Encoding + + + {0} (detected) + Path @@ -2102,9 +2177,24 @@ Splitting size - + Do not split + + Auto + + + Dictionary size + + + Word size + + + Estimated memory usage: {0} + + + Available memory: {0} + CD @@ -2159,8 +2249,14 @@ Edit settings file - - What's new + + Open settings file in your default editor + + + Release Notes + + + Open Release Notes Creating a shortcut in this location requires administrator privileges @@ -2241,25 +2337,22 @@ Подешавања контекстуалног менија - Show file extensions - - - Show hidden items + File extensions - Format... + Формат Help - Toggle full screen + Full screen Are you sure you want to delete this tag? - Play all + Play Height @@ -2319,7 +2412,7 @@ Increase size - Toggle sort direction + Sort direction Invalid name @@ -2331,34 +2424,37 @@ Videos - Launch preview popup + Preview popup Toggle compact overlay - Open online help page in browser + Open the online help page in your browser - Toggle full screen + Toggle full screen mode - Enter compact overlay + Enter compact overlay mode - Exit compact overlay + Exit compact overlay mode - Toggle compact overlay + Toggle compact overlay mode - Go to search box + Start search in the OmniBar - Toggle whether to show hidden items + Toggle visibility of hidden items + + + Toggle visibility of dot files - Toggle whether to show file extensions + Toggle visibility of file extensions Toggle the preview pane to view file previews @@ -2367,52 +2463,64 @@ Toggle whether to show sidebar - Copy item(s) to clipboard + Copy selected {0, plural, one {item} other {items}} - Copy path of selected items to the clipboard + Copy path of the current directory + + + Copy path of selected items + + + Copy path of selected items with quotes - Copy path of selected items with quotes to the clipboard + Copy path of the current directory with quotes - Cut item(s) to clipboard + Cut selected {0, plural, one {item} other {items}} - Paste item(s) from clipboard to current folder + Paste items to the current folder + + + Paste items to the current folder as shortcuts - Paste item(s) from clipboard to selected folder + Paste items to the selected folder + + + Paste to selected folder - Обриши датотеку(е) + Delete selected {0, plural, one {item} other {items}} Create new folder - Create new shortcut(s) to selected item(s) + Create new {0, plural, one {shortcut} other {shortcuts}} to selected {0, plural, one {item} other {items}} Create new shortcut to any item - Испразни корпу за отпатке + Empty the contents of Recycle Bin Open "Format Drive" menu for selected item - Restore selected item(s) from recycle bin + Restore selected {0, plural, one {item} other {items}} from recycle bin Restore all items from recycle bin - Open item(s) + Open {0, plural, one {item} other {items}} - Open item(s) with selected application + Open {0, plural, one {item} other {items}} with selected application Open parent folder of searched item @@ -2427,28 +2535,28 @@ Select all items - Invert item selection + Invert selected items - Clear item selection + Clear selected items Toggle item selection - Share selected file(s) with others + Share selected {0, plural, one {file} other {files}} with others - Pin item(s) to the Start Menu + Pin {0, plural, one {item} other {items}} to the Start Menu - Unpin item(s) from the Start Menu + Unpin {0, plural, one {item} other {items}} from the Start Menu - Pin folder(s) to Sidebar + Pin {0, plural, one {folder} other {folders}} to Sidebar - Unpin folder(s) from Sidebar + Unpin {0, plural, one {folder} other {folders}} from Sidebar Set selected picture as desktop background @@ -2462,14 +2570,23 @@ Set selected picture as the app background + + Install font + + + Install driver + + + Install certificate + - Install selected font(s) + Install selected {0, plural, one {font} other {fonts}} - Install driver(s) using selected inf file(s) + Install {0, plural, one {driver} other {drivers}} using selected inf {0, plural, one {file} other {files}} - Install selected certificate(s) + Install selected {0, plural, one {certificate} other {certificates}} Run selected application as administrator @@ -2484,28 +2601,28 @@ Launch preview in popup window - Create archive with selected item(s) + Create archive with selected {0, plural, one {item} other {items}} - Create 7z archive instantly with selected item(s) + Create 7z archive with selected {0, plural, one {item} other {items}} - Create zip archive instantly with selected item(s) + Create zip archive with selected {0, plural, one {item} other {items}} - Extract items from selected archive(s) to any folder + Extract selected {0, plural, one {archive} other {archives}} to any folder - Extract items from selected archive(s) to current folder + Extract selected {0, plural, one {archive} other {archives}} to the current folder - Extract items from selected archive(s) to new folder + Extract selected {0, plural, one {archive} other {archives}} to new folder - Rotate selected image(s) to the left + Rotate selected {0, plural, one {image} other {images}} to the left - Rotate selected image(s) to the right + Rotate selected {0, plural, one {image} other {images}} to the right Open settings page @@ -2525,8 +2642,8 @@ Switch to details view - - Switch to tiles view + + Switch to cards view Switch to list view @@ -2625,13 +2742,13 @@ Toggle group sort direction - Open new tab + Отвори нову картицу - Navigate backward in navigation history + Navigate backward - Navigate forward in navigation history + Navigate forward Navigate up one directory @@ -2660,8 +2777,11 @@ Close tabs other than selected tab + + Close all tabs including the current tab + - Reopen last closed tab + Reopen recently closed tab Move to the previous tab @@ -2673,13 +2793,13 @@ Close current tab - Close active pane + Close the active pane Focus other pane - Switch focus to the non active pane + Switch focus to the other pane Toggle the sidebar @@ -2887,13 +3007,13 @@ Untagged - Moves to the previous tab + Previous tab - Moves to the next tab + Next tab - Closes current tab + Close tab Edit path @@ -2923,7 +3043,7 @@ Прикажи опције за селектовање приликом селектовања ставки - Focus path bar + Edit path in the OmniBar Create new item @@ -2938,7 +3058,7 @@ Delete permanently - Delete item(s) permanently + Delete selected {0, plural, one {item} other {items}} permanently Play the selected media files @@ -3027,6 +3147,15 @@ You have uncommitted changes on this branch. What would you like to do with them? + + You have an ongoing merge with unresolved conflicts. Please resolve the conflicts or abort the merge to switch branches. + + + Abort merge and switch to '{0}' + + + Stay on '{0}' and resolve conflicts + Switch branch @@ -3055,14 +3184,20 @@ Switch to new branch - Create a folder with the currently selected item(s) + Create a folder with the currently selected {0, plural, one {item} other {items}} - Open properties + Подаци о датотеци + + + Open File Explorer properties Open properties window + + Open File Explorer properties window + Locals @@ -3081,6 +3216,9 @@ Run git fetch + + Clone a git repo + Run git pull @@ -3125,7 +3263,7 @@ Mica Alt - Backdrop Material + Backdrop Solid @@ -3157,17 +3295,17 @@ Files cannot access GitHub right now. - - Open folder in VS Code + + Open folder in {0} - - Open the current directory in Visual Studio Code + + Open the current directory in {0} - - Open repo in VS Code + + Open repo in {0} - - Open the root of the Git repo in Visual Studio Code + + Open the root of the Git repo in {0} Copy code @@ -3194,7 +3332,7 @@ Initialize repo - Initialize a Git repository + Initialize current folder as a git repository Remote Repository Name @@ -3209,13 +3347,13 @@ Sort items by path - Open directory in new pane + Open selected directory in a new pane - Open directory in new tab + Open selected directory in a new tab - Open directory in new window + Open selected directory in a new window Open all @@ -3240,10 +3378,10 @@ The '{0}' command is not ready to be executed. - Command palette + Command Palette - Open command palette + Open Command Palette in the OmniBar Start in: @@ -3373,6 +3511,9 @@ Processing items... + + Discovering items... + Speed: @@ -3385,61 +3526,129 @@ Shown in a StatusCenter card. - Compressed {0} item(s) to "{1}" + Compressed {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Compressed {0} item(s) from "{1}" to "{2}" + Compressed {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error compressing {0} item(s) to "{1}" + Error compressing {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to compress {0} item(s) from "{1}" to "{2}" + Failed to compress {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Compressing {0} item(s) to "{1}" + Compressing {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Compressing {0} item(s) from "{1}" to "{2}" + Compressing {0, plural, one {# item} other {# items}} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Canceled cloning {0} to "{1}" + Shown in a StatusCenter card. + + + Canceled cloning {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Cloned "{0}" to "{1}" + Shown in a StatusCenter card. + + + Cloned {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Error cloning "{0}" to "{1}" + Shown in a StatusCenter card. + + + Failed to clone {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Cloning "{0}" to "{1}" + Shown in a StatusCenter card. + + + Cloning {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Canceled installing {0} fonts + Shown in a StatusCenter card. + + + Canceled installing {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Error installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Failed to install {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} from "{1}" Shown in a StatusCenter card. - Canceled copying {0} item(s) to "{1}" + Canceled copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Canceled copying {0} item(s) from "{1}" to "{2}" + Canceled copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Copied {0} item(s) to "{1}" + Copied {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Copied {0} item(s) from "{1}" to "{2}" + Copied {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error copying {0} item(s) to "{1}" + Error copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to copy {0} item(s) from "{1}" to "{2}" + Failed to copy {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Copying {0} item(s) to "{1}" + Copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Copying {0} item(s) from "{1}" to "{2}" + Copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. + + Discovered {0, plural, one {# item} other {# items}} + Shown in a StatusCenter card during file discovery phase. + Canceled extracting "{0}" to "{1}" Shown in a StatusCenter card. @@ -3473,55 +3682,55 @@ Shown in a StatusCenter card. - Canceled deleting {0} item(s) from "{1}" + Canceled deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Deleted {0} item(s) from "{1}" + Deleted {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Error deleting {0} item(s) from "{1}" + Error deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Failed to delete {0} item(s) from "{1}" + Failed to delete {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Deleting {0} item(s) from "{1}" + Deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Canceled moving {0} item(s) to "{1}" + Canceled moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Canceled moving {0} item(s) from "{1}" to "{2}" + Canceled moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Moved {0} item(s) to "{1}" + Moved {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Moved {0} item(s) from "{1}" to "{2}" + Moved {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Moving {0} item(s) to "{1}" + Moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Moving {0} item(s) from "{1}" to "{2}" + Moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error moving {0} item(s) to "{1}" + Error moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to move {0} item(s) from "{1}" to "{2}" + Failed to move {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. @@ -3549,7 +3758,7 @@ Shown in a StatusCenter card. - {0}/{1} item(s) processed + {0}/{1, plural, one {# item} other {# items}} processed Shown in a StatusCenter card. Used as "8/20 items processed" @@ -3567,6 +3776,9 @@ Failed to set the background wallpaper + + Failed to open the settings file + Delete Git branch @@ -3592,7 +3804,7 @@ There was an error applying this tag - Extract items from selected archive(s) to current folder for single-item archive, or to new folder for multi-item archive + Extract selected {0, plural, one {archive} other {archives}} here for single-item or to new folder for multi-item Extract here (Smart) @@ -3601,7 +3813,7 @@ Sort files and folders together - Sort files and folders together + Sort files and folders in the same list Sort files first @@ -3657,9 +3869,6 @@ Questions & discussions - - Additional sizes are not yet available for the Tiles View. - Compact Used to describe layout sizes @@ -3821,6 +4030,10 @@ ICO File This is the friendly name for ICO files. + + ICL File + This is the friendly name for ICL files. + Zip File This is the friendly name for ZIP files. @@ -3829,6 +4042,10 @@ Bitmap Files This is the friendly name for bitmap files. + + Image Files + This is the friendly name for image files. + Num @@ -3854,23 +4071,23 @@ Status: - Show Toolbar - Setting that controls if the Toolbar is shown in the main view + Show toolbar + Setting that controls if the toolbar is shown in the main view Tab actions menu - - Add vertical pane + + Split vertically - Add a vertical pane + Split pane vertically - - Add horizontal pane + + Split horizontally - - Add a horizontal pane + + Split pane horizontally Arrange vertically @@ -3884,8 +4101,8 @@ Arrange panes horizontally - - Add pane + + Split pane Show tab actions button in the title bar @@ -3893,8 +4110,11 @@ Arrange panes - - Default pane arrangement + + Default dual pane split direction + + + Dual pane mode Horizontal @@ -3902,4 +4122,259 @@ Vertical + + Show Files icon in the System Tray + + + CPU threads + + + Navigate to the home page + + + Toolbars + + + User ID + + + Bulk rename + + + Compress contents + + + Show option to create alternate data stream + + + Create alternate data stream + + + Create alternate data stream for the selected {0, plural, one {item} other {items}} + + + Enter data stream name + + + There was an error creating the alternate data stream + + + Please note that alternate data streams only work on drives formatted as NTFS. + + + Alternate data streams are currently hidden + + + Would you like to display alternate data streams? You can modify this setting anytime from the files and folders settings page. + + + Manage tags + + + Available + + + Total + + + Always switch focus to newly created tab + + + Toggle shelf pane + + + Toggle the visibility of the shelf pane + + + Show shelf pane toggle in address bar + + + Shelf + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + Clear items + + + Remove from shelf + + + Add to shelf + Tooltip that displays when dragging items to the Shelf Pane + + + Enter a hash to compare + Placeholder that appears in the compare hash text box + + + Matches {0} + Appears when two compared hashes match, e.g. "Matches SHA256" + + + No matches found + Appears when two compared hashes don't match + + + Path or alias + + + Invalid path + + + Test integration + + + {0} could not be located. Please check your settings and try again. + + + The configured IDE could not be located + + + Open settings + + + Visual Studio Code + + + Enter a path or launch alias + + + Please enter a name for the IDE + + + Clone repo + Clone repo dialog title + + + Clone + Primary action button in the clone repo dialog + + + Repository URL + URL textbox header in the clone repo dialog + + + Cannot clone repo + Cannot clone repo dialog title + + + Compare a file + Button that appears in file hash properties that allows the user to compare two files + + + Size format + + + Binary + + + Decimal + + + You can add sections to the sidebar by right-clicking and selecting the sections you want to add + + + Drag files or folders here to interact with them across different tabs + + + Enter a path to navigate to... + + + Find features and commands... + + + Search for files and folders... + + + During file operations + + + Show status center button + + + Status center progress ring + Screen reader name for the status center progress ring + + + Icon files + This is the friendly name for a variety of different icon files. + + + Show collapsed path breadcrumbs + + + Show folders in {0} + + + Show folders in Home + + + There are no commands containing {0} + + + See more + + + Filtering for + + + Filename + + + Signatures + + + Signature list + + + Issued by: + + + Issued to: + + + Valid from: + + + Valid to: + + + No signature was found. + + + Show option to open folders in Windows Terminal + + + Open log file + + + Open log file in your default editor + + + Open log file location in your default file manager + + + Unable to open the log file + + + Show status bar + + + Only in Columns View + + + New {0} + + + Enable smooth scrolling + + + Scrolling + + + Show option to Pin to Sidebar + + + Show option to Pin to the Start Menu + \ No newline at end of file diff --git a/src/Files.App/Strings/sv-SE/Resources.resw b/src/Files.App/Strings/sv-SE/Resources.resw index 7ac9706d7a67..2edfff463862 100644 --- a/src/Files.App/Strings/sv-SE/Resources.resw +++ b/src/Files.App/Strings/sv-SE/Resources.resw @@ -123,9 +123,15 @@ Kopiera sökväg + + Copy item path + Kopiera sökväg med citattecken + + Copy selected item path with quotes + Bläddra @@ -160,7 +166,7 @@ Hoppa över - Markera alla + Select all Omvänd markering @@ -211,7 +217,7 @@ Visa dolda filer och mappar - Visa punktfiler + Show dot files Utseende @@ -249,6 +255,9 @@ Klistra in + + Klistra in genväg + Nya @@ -291,8 +300,8 @@ Ange ett namn för objektet - - Ange namn + + Skapa ny {0} Ljust @@ -300,6 +309,9 @@ Mörkt + + Använd systeminställning + App @@ -515,9 +527,6 @@ Status - - Windows-standard - Inga sökresultat @@ -530,6 +539,9 @@ Kopiera till {0} + + Klona till {0} + Skapa genväg @@ -998,6 +1010,9 @@ Sökresultat i + + Sökresultat för `{0}` + Information @@ -1052,9 +1067,6 @@ Detaljvy - - Panelvy - Borttagningsdatum @@ -1080,10 +1092,25 @@ Slå av eller på informationspanelen - Växla infopanelen för att se detaljerna/förhandsgranskningsrutorna + Toggle visibility of the detail/preview panes - Växla verktygsfältet + Växla verktygsfält + + + Toggle visibility of the toolbar + + + Toggle filter header + + + Toggle visibility of the filter header + + + Toggle dual pane + + + Toggle dual pane mode Ingen förhandsgranskning är tillgänglig @@ -1238,6 +1265,12 @@ Öppna Storage Sense + + Open the Storage Sense page in Windows Settings + + + Cleanup + Kopiera @@ -1380,7 +1413,7 @@ {0} objekt - Öppna stängd flik igen + Reopen tab Byt namn @@ -1587,7 +1620,7 @@ Ersätt alla underordnade objekträttighetsposter med ärftliga rättighetsposter från detta objekt - Close active pane + Close pane Öppna kompakt överlägg @@ -1617,7 +1650,7 @@ Hämtar objektet från molnet och laddar förhandsgranskningen - {0, plural, one {# item} other {# items}} ({1, plural, one {# file} other {# files}}, {2, plural, one {# folder} other {# folders}}) + {0, plural, one {# objekt} other {# objekt}} ({1, plural, one {# fil} other {# filer}}, {2, plural, one {# mapp} other {# mappar}}) Okomprimerad storlek @@ -1646,15 +1679,6 @@ Hem - - Ctrl+Skift+1 - - - Ctrl+Skift+2 - - - Ctrl+Skift+6 - Arkivet har extraherats. @@ -1712,8 +1736,8 @@ Kolumner - - Paneler + + Kort Öppna mappar i ny flik @@ -1749,7 +1773,7 @@ Tagg - Enkelklicka för att öppna + Open files with a single click Mappsökväg @@ -1826,6 +1850,18 @@ Registrera detta program för omstart + + Start window + + + Normalt fönster + + + Minimerad + + + Maximerad + Kör som administratör @@ -1848,11 +1884,14 @@ Ingen reducerad färg - Third party libraries + Tredjepartsbibliotek Det här alternativet ändrar systemregistret och kan ha oväntade effekter på din enhet. Fortsätt på egen risk. + + The flatten operations are permanent and not recommended. Continue at your own risk. + Skapa bibliotek @@ -1931,6 +1970,9 @@ Stäng övriga flikar + + Stäng alla flikar + Musik @@ -1985,11 +2027,23 @@ Beteenden - - Betygsätt Files + + Hej! - - Vill du betygsätta Files? + + Enjoying Files? Please consider reviewing in the Microsoft Store. + + + Enjoying Files? Please consider supporting the project on GitHub. + + + Sponsor + + + Rate us + + + Dismiss Ange som bakgrund @@ -2018,8 +2072,8 @@ Låsskärm - - Öppna mappar med enkelklick i Kolumnlayouten + + Open folders with a single click Öppnar objekt @@ -2027,6 +2081,21 @@ Komprimera + + Move all contents from subfolders into the selected location + + + Flatten folder + + + Flatten + + + Flattening a folder will move all contents from its subfolders to the selected location. This operation is permanent and cannot be undone. By using this experimental feature, you acknowledge the risk and agree not to hold the Files team responsible for any data loss. + + + Show flatten options + Välj filer och mappar när man hovrar över dem @@ -2037,7 +2106,7 @@ Återställ alla objekt - Vill du återställa {0} valda objekt? + Do you want to restore the {0, plural, one {selected item} other {{0} selected items}}? Återställ valda @@ -2060,6 +2129,12 @@ Arkivlösenord + + Kodning + + + {0} (detected) + Sökväg @@ -2102,8 +2177,23 @@ Delningsstorlek - - Dela inte upp + + Do not split + + + Auto + + + Dictionary size + + + Word size + + + Estimated memory usage: {0} + + + Available memory: {0} CD @@ -2159,8 +2249,14 @@ Redigera inställningsfil - - Vad är nytt + + Open settings file in your default editor + + + Release Notes + + + Open Release Notes Att skapa en genväg på den här platsen kräver administratörsrättigheter @@ -2241,25 +2337,22 @@ Inställningar för innehållsmeny - Visa filnamnstillägg - - - Visa dolda objekt + Filändelser - Formatera... + Format Hjälp - Växla helskärm + Fullskärm Är du säker på att du vill radera den här taggen? - Spela alla + Spela Höjd @@ -2319,7 +2412,7 @@ Öka storlek - Växla sorteringsriktning + Sort direction Ogiltigt namn @@ -2331,34 +2424,37 @@ Videor - Öppna förhandsgranskningspopup + Preview popup Växla kompakt överlägg - Öppna onlinehjälp i en webbläsare + Open the online help page in your browser - Växla helskärm + Växla fullskärmsläge - Öppna kompakt överlägg + Enter compact overlay mode - Stäng kompakt överlägg + Exit compact overlay mode - Växla kompakt överlägg + Toggle compact overlay mode - Hoppa till sökrutan + Start search in the OmniBar - Växla om dolda objekt ska visas + Toggle visibility of hidden items + + + Toggle visibility of dot files - Växla om filnamnstillägg ska visas + Toggle visibility of file extensions Slå på förhandsgranskningsrutan för att visa förhandsgranskningar av filer @@ -2367,52 +2463,64 @@ Växla om sidofältet ska visas - Kopiera objekt(en) till urklipp + Copy selected {0, plural, one {item} other {items}} - Kopiera sökväg för valda objekt till urklipp + Copy path of the current directory + + + Copy path of selected items + + + Copy path of selected items with quotes - Kopiera sökväg för valda objekt med citat till urklipp + Copy path of the current directory with quotes - Klipp ut objekt till urklipp + Cut selected {0, plural, one {item} other {items}} - Klistra in objekt från urklipp till nuvarande mapp + Paste items to the current folder + + + Paste items to the current folder as shortcuts - Klistra in objekt från urklipp till den valda mappen + Paste items to the selected folder + + + Paste to selected folder - Ta bort objekt + Delete selected {0, plural, one {item} other {items}} Skapa ny mapp - Skapa nya genvägar till valda objekt + Create new {0, plural, one {shortcut} other {shortcuts}} to selected {0, plural, one {item} other {items}} Skapa ny genväg till ett objekt - Töm papperskorgen + Empty the contents of Recycle Bin Öppna formateringsmenyn för valt objekt - Återställ valda objekt från papperskorgen + Restore selected {0, plural, one {item} other {items}} from recycle bin Återställ alla objekt från papperskorgen - Öppna objekt + Open {0, plural, one {item} other {items}} - Öppna objekt med vald applikation + Open {0, plural, one {item} other {items}} with selected application Öppna överordnad mapp av sökt objekt @@ -2427,28 +2535,28 @@ Markera alla objekt - Omvänd markering + Invert selected items - Rensa markering + Clear selected items Växla val av objekt - Dela markerade fil(er) med andra + Share selected {0, plural, one {file} other {files}} with others - Fäst objekt i Start-menyn + Pin {0, plural, one {item} other {items}} to the Start Menu - Lossa objekt från Start-menyn + Unpin {0, plural, one {item} other {items}} from the Start Menu - Fäst mapp(ar) till sidofält + Pin {0, plural, one {folder} other {folders}} to Sidebar - Lossa mapp(er) från sidofält + Unpin {0, plural, one {folder} other {folders}} from Sidebar Ställ in vald bild som skrivbordsbakgrund @@ -2462,14 +2570,23 @@ Ange vald bild som appbakgrund + + Installera teckensnitt + + + Installera drivrutin + + + Installera certifikat + - Installera valda teckensnitt + Install selected {0, plural, one {font} other {fonts}} - Installera drivrutin(er) med valda 'Inf' fil(er) + Install {0, plural, one {driver} other {drivers}} using selected inf {0, plural, one {file} other {files}} - Installera markerade certifikat + Install selected {0, plural, one {certificate} other {certificates}} Kör vald applikation som administratör @@ -2484,28 +2601,28 @@ Starta förhandsgranskning i popup-fönster - Skapa arkiv med valda objekt + Create archive with selected {0, plural, one {item} other {items}} - Skapa ett 7z arkiv direkt med valda objekt + Create 7z archive with selected {0, plural, one {item} other {items}} - Skapa ett zip-arkiv direkt med valda objekt + Create zip archive with selected {0, plural, one {item} other {items}} - Extrahera objekt från valda arkiv till valfri mapp + Extract selected {0, plural, one {archive} other {archives}} to any folder - Extrahera objekt från valda arkiv till aktuell mapp + Extract selected {0, plural, one {archive} other {archives}} to the current folder - Extrahera objekt från markerade arkiv till ny mapp + Extract selected {0, plural, one {archive} other {archives}} to new folder - Rotera valda bild(er) till vänster + Rotate selected {0, plural, one {image} other {images}} to the left - Rotera valda bild(er) till höger + Rotate selected {0, plural, one {image} other {images}} to the right Öppna Inställningarna @@ -2525,8 +2642,8 @@ Växla till detaljvy - - Växla till panelvy + + Växla till kortvy Växla till listvy @@ -2625,13 +2742,13 @@ Växla gruppsorteringsriktning - Öppna ny flik + Öppna en ny flik - Gå bakåt i navigeringshistoriken + Navigera bakåt - Gå framåt i navigeringshistoriken + Navigera framåt Gå upp en mapp @@ -2660,8 +2777,11 @@ Stäng alla andra flikar än den valda fliken + + Close all tabs including the current tab + - Öppna den senast stängda fliken + Reopen recently closed tab Flytta till föregående flik @@ -2673,13 +2793,13 @@ Stäng den nuvarande fliken - Close active pane + Close the active pane Focus other pane - Switch focus to the non active pane + Switch focus to the other pane Växla sidofältet @@ -2887,13 +3007,13 @@ Otaggad(e) - Flyttar till föregående flik + Föregående flik - Flyttar till nästa flik + Nästa flik - Stänger aktuell flik + Stäng flik Redigera sökväg @@ -2923,7 +3043,7 @@ Visa kryssrutor när du väljer objekt - Fokus sökfält + Edit path in the OmniBar Skapa nytt objekt @@ -2938,7 +3058,7 @@ Radera permanent - Ta bort objekt permanent + Delete selected {0, plural, one {item} other {items}} permanently Spela upp de valda mediefilerna @@ -3027,6 +3147,15 @@ Du har oengagerade ändringar på denna gren. Vad skulle du vilja göra med dem? + + You have an ongoing merge with unresolved conflicts. Please resolve the conflicts or abort the merge to switch branches. + + + Abort merge and switch to '{0}' + + + Stay on '{0}' and resolve conflicts + Växla gren @@ -3055,14 +3184,20 @@ Växla till ny affärsenhet - Skapa en mapp med de valda objekten + Create a folder with the currently selected {0, plural, one {item} other {items}} - Öppna egenskaper + Egenskaper + + + Open File Explorer properties Öppna egenskapsfönstret + + Open File Explorer properties window + Lokalbefolkningen @@ -3081,6 +3216,9 @@ Kör hämtning av git + + Clone a git repo + Run git pull @@ -3125,7 +3263,7 @@ Mica Alt - Bakgrundsmaterial + Bakgrund Solid @@ -3157,17 +3295,17 @@ Filer kan inte komma åt GitHub just nu. - - Öppna mapp i VS-kod + + Öppna mapp i {0} - - Öppna den aktuella katalogen i Visual Studio Code + + Open the current directory in {0} - - Öppna repo i VS-kod + + Open repo in {0} - - Öppna roten av Git repo i Visual Studio Code + + Open the root of the Git repo in {0} Kopiera kod @@ -3194,7 +3332,7 @@ Initiera repo - Initiera ett Git-arkiv + Initialize current folder as a git repository Namn på fjärrutvecklingskatalogen @@ -3209,13 +3347,13 @@ Sortera objekt efter sökväg - Öppna katalogen i ny ruta + Open selected directory in a new pane - Öppna katalogen i ny flik + Open selected directory in a new tab - Öppna katalogen i nytt fönster + Open selected directory in a new window Öppna alla @@ -3240,10 +3378,10 @@ Kommandot '{0}' är inte redo att köras. - Kommando palett + Command Palette - Öppna kommandopaletten + Open Command Palette in the OmniBar Börja om: @@ -3373,6 +3511,9 @@ Bearbetar objekt... + + Discovering items... + Hastighet: @@ -3385,61 +3526,129 @@ Shown in a StatusCenter card. - Komprimerat {0} objekt till "{1}" + Compressed {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Komprimerat {0} objekt från "{1}" till "{2}" + Compressed {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Fel vid komprimering av {0} objekt till "{1}" + Error compressing {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Det gick inte att komprimera {0} objekt från "{1}" till "{2}" + Failed to compress {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Komprimerar {0} objekt till "{1}" + Compressing {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Komprimerar {0} objekt från "{1}" till "{2}" + Compressing {0, plural, one {# item} other {# items}} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Avbröt kloning {0} till "{1}" + Shown in a StatusCenter card. + + + Canceled cloning {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Klonade "{0}" till "{1}" + Shown in a StatusCenter card. + + + Cloned {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Error cloning "{0}" to "{1}" + Shown in a StatusCenter card. + + + Failed to clone {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Klonar "{0}" till "{1}" + Shown in a StatusCenter card. + + + Cloning {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Canceled installing {0} fonts + Shown in a StatusCenter card. + + + Canceled installing {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Error installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Failed to install {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} from "{1}" Shown in a StatusCenter card. - Avbröt kopiering av {0} objekt till "{1}" + Canceled copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Avbröt kopiering av {0} objekt från "{1}" till "{2}" + Canceled copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Kopierade {0} objekt till "{1}" + Copied {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Kopierade {0} objekt från "{1}" till "{2}" + Copied {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Fel vid kopiering av {0} objekt till "{1}" + Error copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Det gick inte att kopiera {0} objekt från "{1}" till "{2}" + Failed to copy {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Kopierar {0} objekt till "{1}" + Copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Kopierar {0} objekt från "{1}" till "{2}" + Copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. + + Discovered {0, plural, one {# item} other {# items}} + Shown in a StatusCenter card during file discovery phase. + Avbröt extraheringen "{0}" till "{1}" Shown in a StatusCenter card. @@ -3473,55 +3682,55 @@ Shown in a StatusCenter card. - Avbröt borttagning av {0} objekt från "{1}" + Canceled deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Tog bort {0} objekt från "{1}" + Deleted {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Fel vid borttagning av {0} objekt från "{1}" + Error deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Det gick inte att ta bort {0} objekt från "{1}" + Failed to delete {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Tar bort {0} objekt från "{1}" + Deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Avbröt flyttningen av {0} objekt till "{1}" + Canceled moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Avbröt flyttningen av {0} objekt från "{1}" till "{2}" + Canceled moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Flyttade {0} objekt till "{1}" + Moved {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Flyttade {0} objekt från "{1}" till "{2}" + Moved {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Flyttar {0} objekt till "{1}" + Moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Flyttar {0} objekt från "{1}" till "{2}" + Moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Fel vid flytt av {0} objekt till "{1}" + Error moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Misslyckades att flytta {0} objekt från "{1}" till "{2}" + Failed to move {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. @@ -3549,7 +3758,7 @@ Shown in a StatusCenter card. - {0}/{1} objekt bearbetade + {0}/{1, plural, one {# item} other {# items}} processed Shown in a StatusCenter card. Used as "8/20 items processed" @@ -3567,6 +3776,9 @@ Det gick inte att ange bakgrundsbild + + Failed to open the settings file + Ta bort Git branch @@ -3592,7 +3804,7 @@ Det gick inte att tillämpa denna tagg - Extrahera objekt från valda arkiv till aktuell mapp för ett objekts arkiv, eller till ny mapp för flera objekts arkiv + Extract selected {0, plural, one {archive} other {archives}} here for single-item or to new folder for multi-item Extrahera här (Smart) @@ -3601,7 +3813,7 @@ Sortera filer och mappar tillsammans - Sortera filer och mappar tillsammans + Sort files and folders in the same list Sortera filer först @@ -3657,9 +3869,6 @@ Frågor & diskussioner - - Ytterligare storlekar är ännu inte tillgängliga för kakel vy. - Kompakt Used to describe layout sizes @@ -3821,6 +4030,10 @@ ICO Fil This is the friendly name for ICO files. + + ICL-fil + This is the friendly name for ICL files. + Zip Fil This is the friendly name for ZIP files. @@ -3829,6 +4042,10 @@ Bitmap filer This is the friendly name for bitmap files. + + Bildfiler + This is the friendly name for image files. + Num @@ -3848,44 +4065,44 @@ Redigera den valda filen i Anteckningar - Dimensions: + Dimensioner: Status: - Show Toolbar - Setting that controls if the Toolbar is shown in the main view + Visa verktygsfält + Setting that controls if the toolbar is shown in the main view Tab actions menu - - Add vertical pane + + Dela vertikalt - Add a vertical pane + Split pane vertically - - Add horizontal pane + + Dela horisontellt - - Add a horizontal pane + + Split pane horizontally - Arrange vertically + Ordna vertikalt Arrange panes vertically - Arrange horizontally + Ordna horisontellt Arrange panes horizontally - - Add pane + + Split pane Show tab actions button in the title bar @@ -3893,13 +4110,271 @@ Arrange panes - - Default pane arrangement + + Default dual pane split direction + + + Dual pane mode - Horizontal + Horisontell - Vertical + Vertikal + + + Show Files icon in the System Tray + + + CPU-trådar + + + Navigera till hemsidan + + + Verktygsfält + + + Användar-ID + + + Bulk rename + + + Komprimera innehåll + + + Show option to create alternate data stream + + + Create alternate data stream + + + Create alternate data stream for the selected {0, plural, one {item} other {items}} + + + Enter data stream name + + + There was an error creating the alternate data stream + + + Please note that alternate data streams only work on drives formatted as NTFS. + + + Alternate data streams are currently hidden + + + Would you like to display alternate data streams? You can modify this setting anytime from the files and folders settings page. + + + Hantera taggar + + + Tillgänglig + + + Total + + + Always switch focus to newly created tab + + + Toggle shelf pane + + + Toggle the visibility of the shelf pane + + + Show shelf pane toggle in address bar + + + Hylla + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + Clear items + + + Ta bort från hylla + + + Add to shelf + Tooltip that displays when dragging items to the Shelf Pane + + + Enter a hash to compare + Placeholder that appears in the compare hash text box + + + Matches {0} + Appears when two compared hashes match, e.g. "Matches SHA256" + + + No matches found + Appears when two compared hashes don't match + + + Sökväg eller alias + + + Ogiltig sökväg + + + Testa integration + + + {0} could not be located. Please check your settings and try again. + + + The configured IDE could not be located + + + Öppna inställningar + + + Visual Studio Code + + + Enter a path or launch alias + + + Please enter a name for the IDE + + + Clone repo + Clone repo dialog title + + + Klona + Primary action button in the clone repo dialog + + + Repository URL + URL textbox header in the clone repo dialog + + + Cannot clone repo + Cannot clone repo dialog title + + + Jämför en fil + Button that appears in file hash properties that allows the user to compare two files + + + Size format + + + Binär + + + Decimal + + + You can add sections to the sidebar by right-clicking and selecting the sections you want to add + + + Drag files or folders here to interact with them across different tabs + + + Enter a path to navigate to... + + + Hitta funktioner och kommandon... + + + Sök efter filer och mappar... + + + During file operations + + + Show status center button + + + Status center progress ring + Screen reader name for the status center progress ring + + + Ikonfiler + This is the friendly name for a variety of different icon files. + + + Show collapsed path breadcrumbs + + + Visa mappar i {0} + + + Visa mappar i Hem + + + There are no commands containing {0} + + + Se mer + + + Filtering for + + + Filnamn + + + Signaturer + + + Signature list + + + Issued by: + + + Issued to: + + + Giltig från: + + + Giltig till: + + + No signature was found. + + + Show option to open folders in Windows Terminal + + + Öppna loggfil + + + Open log file in your default editor + + + Open log file location in your default file manager + + + Kunde inte öppna loggfilen + + + Show status bar + + + Only in Columns View + + + New {0} + + + Enable smooth scrolling + + + Scrolling + + + Show option to Pin to Sidebar + + + Show option to Pin to the Start Menu \ No newline at end of file diff --git a/src/Files.App/Strings/ta/Resources.resw b/src/Files.App/Strings/ta/Resources.resw index eaf9e00a0f2a..bd6ee04cc60c 100644 --- a/src/Files.App/Strings/ta/Resources.resw +++ b/src/Files.App/Strings/ta/Resources.resw @@ -121,10 +121,16 @@ புது சாளரம் - பாதையை நகலெடு + பாதய நகலெடு + + + Copy item path - மேற்கோள்குறியுடன் பாதையை நகலெடு + Copy path with quotes + + + Copy selected item path with quotes உலாவு @@ -133,49 +139,49 @@ அளவு - உருவாக்கியது: + Created: - பாதை + பாத அளவு: - வட்டிலுள்ள அளவு: + Size on disk: Uncompressed size: - இந்த செயலை செய்ய முடியாது + This action cannot be done - இலக்கு கோப்புறை + The destination folder - மூல கோப்புறையின் துணைக்கோப்புறை + is a subfolder of the source folder - தவிர் + விட்டு விடு - அனைத்தையும் தேர்ந்தெடு + Select all Invert selection - தேர்வை அழி + Clear selection - திருத்தியது; + Modified: - அணுகியது: + Accessed: - எல்லா உருப்படிகளையும் அழி + எல்லா பொருளுங்களயும் அழி Enter a path to navigate to or type ">" to open the command palette @@ -184,10 +190,10 @@ தேடு - நீங்கள் முன்பு அணுகிய கோப்புகளும் கோப்புறைகளும் இங்கு காட்டப்படும் + Files you've previously accessed will show up here - இந்த உருப்படியை அகற்று + இந்த பொருள அகற்று GitHub களஞ்சியம் @@ -196,22 +202,22 @@ பற்றி - திறமூல + Open source - தேதி வடிவம் + Date format - கருப்பொருள் + தீம் - தெரிந்த கோப்பு வகைகளுக்கு நீடிப்புகளை காட்டு + Show extensions for known file types - மறைக்கப்பட்ட கோப்புகளையும் கோப்புறைகளையும் காட்டு + Show hidden files and folders - புள்ளியில் தொடங்கும் கோப்புகளை காட்டு + Show dot files தோற்றம் @@ -223,46 +229,49 @@ மேம்பட்ட - நீங்கள் விட்ட இடத்திலிருந்தே தொடரவும் + Continue where you left off - புது தாவலைத் திற + ஒரு புது ^டேபை திற - குறிப்பிட்ட பக்கத்தையோ பக்கங்களையோ திற + ஒரு குறிப்பிட்ட பக்கத்தயோ பக்கங்களயோ திற - திரைப்பலகம் + டெஸ்க்டாப் - ஆவணங்கள் + ஆவணங்க - பதிவிறக்கங்கள் + டவ்ன்லோடுங்க பேர் - ஏறு-வரிசை + ஏறுவரிசை ஒட்டு + + Paste shortcut + புது - பண்புகள் + பண்புங்க - திற + தெற - புது தாவலில் திற + புது ^டேபுல திற - புது சாளரத்தில் திற + புது ஃவிண்டோல திற பகிர் @@ -274,46 +283,49 @@ நீக்கு - Pin to Sidebar + பக்கப்பட்டய்'ல பொருத்து - Files'இற்கு வருக! + Files'கு வருக! - அனுமதியை வழங்கு + அனுமதி வழங்கு - தொடங்க, உங்கள் கோப்புகளை காண்பிக்க எங்களுக்கு அனுமதி வழங்க வேண்டும். இது ஓர் அமைப்புகள் பக்கத்தை திறக்கும். நிறைவடைந்தவுடன் நிரலை நீங்கள் மீண்டும் திறக்க வேண்டும். + தொடங்க, உங்க கோப்புகள காட்ட எங்களுக்கு நீங்க அனுமதி வழங்க'ணும்; இதுக்காக ஒரு ஸெட்டிங்ஸ் பக்கம் திறக்கும். முடிச்ச பிறகு, செயலிய மூடி, திரும்பவும் திறக்க'ணும். காட்சி - ஒரு உருப்படி பெயரை உள்ளிடுங்கள் + ஒரு பொருள் பேர உள்ளிடுங்க - - பேரை அமை + + Create new {0} வெளிச்சம் - இருள் + இருட்டு + + + Use system setting - நிரல் + செயலி - முறைமை + ஸிஸ்டம் - புது கோப்புறை + புது கோப்புற புது கோப்பு - இந்த கோப்புறை வெறுமையாய் கிடக்கு. + இந்த கோப்புற வெறுமயா இருக்கு. பின் @@ -328,61 +340,61 @@ புதுப்பி - இந்த பேருடன் ஏற்கனவே ஓர் உருப்படி இந்த கோப்புறையில் உள்ளது. + இந்த பேர்'ஓடு ஒரு பொருள் இந்த கோப்புறய்'ல ஏற்கனவே இருக்கு. - ஏற்கனவே உள்ள உருப்படியை மாற்று + Replace existing item - உருப்படி ஏற்கனவே உள்ளது + பொருள் ஏற்கனவே இருக்கு - பூட்டுத்திரை பின்புலமாக அமை + Set as lockscreen background Set as app background - இந்த உருப்படியை எங்களால் நீக்க முடியவில்லை + We weren't able to delete this item - இந்த உருப்படியை அணுகத் தேவையான இயக்கத்தைச் சொருகவும். + Please insert the necessary drive to access this item. Drive unplugged - நீங்கள் அணுக முயற்சிக்கும் கோப்பு நகர்த்தப்பட்டோ நீக்கப்பட்டோ இருக்கலாம். + The file you are attempting to access may have been moved or deleted. Cannot open properties for this file - நீங்கள் அணுக முயற்சிக்கும் கோப்பு நகர்த்தப்பட்டோ நீக்கப்பட்டோ இருக்கலாம். + The file you are attempting to access may have been moved or deleted. - கோப்பு கண்டுப்பிடிக்கப்படவில்லை + கோப்பு காணோம் - நீங்கள் அணுக முயற்சிக்கும் கோப்புறை நகர்த்தப்பட்டோ நீக்கப்பட்டோ இருக்கலாம். + The folder you are attempting to access may have been moved or deleted. - நீங்கள் இந்தக் கோப்புறையை நீக்குனீர்களா? + இந்த கோப்புறய நீங்க நீக்கி விட்டீங்களா? - நீங்கள் அணுக முயற்சிக்கும் கோப்பானது தற்போது {0}'ஆல் பயன்படுத்தப்படுகிறது + The file you are attempting to access is currently being used by {0} - நீங்கள் அணுக முயற்சிக்கும் கோப்பானது தற்போது மற்றொரு நிரலால் பயன்படுத்தப்படுகிறது + The file you are attempting to access is currently being used by another application - கோப்பு பயன்பாட்டில் உள்ளது + கோப்பு பயன்பாட்டுல இருக்கு - தளவமைப்பு + Layout - படிக்க-மட்டும் + படிக்க மட்டும் {0, plural, one {# day ago} other {# days ago}} @@ -394,13 +406,13 @@ {0} days ago - {0, plural, one {# hour ago} other {# hours ago}} + {0, plural, one {# மணிநேரம் முன்} other {# மணிநேரம் முன்}} - 1 hour ago + 1 மணிநேரம் முன் - {0} hours ago + {0} மணிநேரம் முன் {0, plural, one {# minute ago} other {# minutes ago}} @@ -421,22 +433,22 @@ {0} seconds ago - இப்போது + இப்போ The requested operation is not supported - {1}'இல் {0} வெறுமை + {0} free of {1} - இற்கு அனுப்பு + Send to - பின்வரும் எழுத்துகுறிகள் உருப்படி பேரில் இருக்கக் கூடாது: \ / : * ? " < > | + The item name must not contain the following characters: \ / : * ? " < > | - கொடுதுள்ள பேர் செல்லதது + The item name specified is invalid {0, plural, one {# item selected} other {# items selected}} @@ -447,19 +459,19 @@ ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural - ஆம் + ஆமா - நிச்சயமாக இந்த உருப்படிகள் அனைத்தையும் நிரந்தரமாக நீக்க விரும்புகிறீர்களா? + Are you sure you want to permanently delete all these items? - மறுசுழற்சி கூடையை வெறுமையாக்கு + மறுசுழற்சி கூடய வெறுமயா ஆக்கு - பைட்டுகள் + பைட்டுங்க - kB + KB MB @@ -483,70 +495,70 @@ {0, number} {0, plural, one {file} other {files}}, {1, number} {1, plural, one {folder} other {folders}} from {2, number} {2, plural, one {location} other {locations}} - வேற ஒரு பயனராய் இயக்கு + இன்னொறு பயனரா ஓட்டு - {0}-இன் எல்லா வகை + {0}'ஓட எல்லா வகய்ங்க - வேற-வேற வகைகள் + Different types - எல்லாம் {0}-இல் + {0}'ல எல்லாம் - பயன்படுத்தப்பட்ட இடம்: + Used space: - வெற்றிடம்: + Free space: - கொள்ளளவு: + Capacity: - கோப்பு முறைமை: + கோப்பு ஸிஸ்டம்: - சமீபத்திய + Recent - தாவலை இங்கு நகர்த்து + ^டேபை இங்கு நகர்த்து - நிலை - - - Windows இயல்புநிலை + நிலய் - முடிவுகள் இல்லை + முடிவுங்க இல்ல - காட்ட, எந்த உருப்படியையும் அணுக முடியவில்லை + காட்ட, எந்த பொருளயும் அணுக முடியவில்ல - {0}'இற்கு நகர்த்து + {0}'கு நகர்த்து - {0}'இற்கு நகலெடு + {0}'கு நகலெடு + + + Clone to {0} - குறுக்குவழியை உருவாக்கு + குறுக்குவழி உருவாக்கு - கோப்பு இருப்பிடத்தை திற + கோப்பு இருப்பிடத்த தெற - நியாயவாதம்: + Arguments: இலக்கு: - குறுக்குவழி வகை: + குறுக்குவழி வகய்: - வலை இணைப்பு + வலய் இணய்ப்பு பொது @@ -555,103 +567,103 @@ குறுக்குவழி - இணைய குறுக்குவழி + இணய குறுக்குவழி {0} - குறுக்குவழி - உருவாக்குனர் தயாராகத சிக்கல் ஒன்றில் Files சிக்கிக்கொண்டது. + Files ran into a problem that the developers didn't prepare for yet. - ஏதோ தவறாகிவிட்டது! + ஏதோ தப்பய்டுச்சு! - இந்த சிக்கலை தெரிவியுங்கள் + Report this issue The item referenced is either invalid or inaccessible.{0}Error message:{0}{1} - செல்லாத உருப்படி + Invalid item பதிப்பு: - இப்போது பகிர எதுவுமில்லை... + There's nothing to share right now... - நீங்கள் தேர்ந்தெடுத்துள்ள உருப்படிகள் பகிரப்படும் + The items you've selected will be shared - தேர்ந்தெடுத்துள்ள உருப்படி பகிரப்படும் + The selected item will be shared - {0}'ஐ பகிர்தல் + Sharing {0} - {0} {1} பகிர்தல் + Sharing {0} {1} The item you're attempting to rename no longer exists. Please verify the correct location of the item you need to rename. - உருப்படி இனி இல்லை + Item no longer exists - கொடுக்கப்பட்ட பேர் செல்லதது. தயவுசெய்து பேரைச் சரிப்பார்த்து மீண்டும் முயற்சிக்கவும். + The name specified was invalid. Please check the desired item name and try again. - செல்லாத உருப்படி பேர் + Invalid item name - குறிப்பிடப்பட்ட பொருளின் பெயரின் நீளம் அதிகபட்ச வரம்பை மீறுகிறது. விரும்பிய பெயரைச் சரிபார்த்து, மீண்டும் முயற்சிக்கவும். + The length of the item name specified exceeds the maximum limit. Please check the desired name and try again. - உருப்படி பேர் மிகவும் நீளமாக இருந்தது + Item name was too long - கூடுதல் விருப்பங்களை காட்டு + அதிக ஆப்ஷனுங்கள காட்டு - மொழி மாற்றத்தை செயற்படுத்த இந்த நிரலை மறுதொடங்க வேண்டும். இப்போது மறுதொடக்கம் செய்ய விரும்புகிறீர்களா? + The application needs to be restarted in order to apply these settings, would you like to restart the app? Sponsor us on GitHub - இந்த உருப்படியை எங்களால் உருவாக்க முடியவில்லை + We weren't able to create this item - அணுகல் மறுக்கப்பட்டது + Access Denied - இதுவாக அமை + Set as - இரத்துசெய் + ரத்து - ஒரு புது உருப்படியை உருவாக்கு + Create a new item - இந்த புது உருப்படியின் வகையை கீழே தேர்ந்தெடுங்கள் + Choose a type for this new item below கோப்பு - வெறுமை கோப்பு ஒன்றை உருவாக்கும் + Creates an empty file - கோப்புறை + கோப்புற - வெறுமை கோப்புறை ஒன்றை உருவாக்கும் + Creates an empty folder - கோப்புறையை புதுப்பி + Refresh the directory மொழி @@ -660,67 +672,67 @@ Move shell extensions into a sub menu - உருப்படிகளை நீக்கும்போது உறுதிப்படுத்தல் பெட்டியை காட்டு + Show confirmation dialog when deleting items - சில பண்புகளை சேமிப்பதில் ஒரு சிக்கல் இருந்தது. + There was an issue saving some properties. - பிழை + பிழய் - எப்படியிருந்தாலும் மூடு + Close anyway - தரமதிப்பு + Rating - உருப்படி பாதை + Item Path - உருப்படி வகை + Item Type - தலைப்பு + தலய்ப்பு பொருள் - கருத்துரை + கருத்து உரய் - பதிப்புரிமை + பதிப்புரிம - திருத்திய தேதி + Date Modified - பிட் செறிவு + Bit Depth - பரிமாணங்கள் + Dimensions - கிடைமட்ட அளவு + Horizontal Size - செங்குத்து அளவு + Vertical Size - கிடைமட்ட அளவு + Horizontal Resolution - செங்குத்து அளவு + Vertical Resolution - வண்ண இடம் + நிற எடம் sRGB - குறிப்பிடப்படாதது + Unspecified Longitude Decimal @@ -732,13 +744,13 @@ Altitude - எடுக்கப்பட்ட தேதி: + Date Taken - கேமரா உற்பத்தியாளர் + Camera Manufacturer - கேமரா மாடல் + Camera Model Exposure Time @@ -747,58 +759,58 @@ Focal Length - துளை + Aperture - ஆள்களின் பேர்கள் + People Names - அலைவரிசை எண்ணிக்கை + Channel Count - வடிவமைப்பு + Format - மாதிரி விகிதம் + Sample Rate - ஆல்பக் கலைஞர் + Album Artist - ஆல்பத் தலைப்பு + Album Title - கலைஞர் + Artist Beats Per Minute - இயற்றியவர் + Composer - நடத்துபவர் + Conductor - வட்டு எண் + Disc Number - நடை வகை + Genre - தடம் எண் + Track Number - கால அளவு + Duration - சட்டக எண்ணிக்கை + Frame Count - பாதுகாப்பு வகை + Protection Type - ஆசிரியர் உரலி + Author Url Content Distributor @@ -807,7 +819,7 @@ Date Released - தொடர் பேர் + Series Name Season Number @@ -816,16 +828,16 @@ Episode Number - தயாரித்தவர் + Producer Promotion Url - வழங்குனர் நடை + வழங்குநர் நடய் - வெளியீட்டாளர் + Publisher Thumbnail Large Path @@ -840,55 +852,55 @@ Thumbnail Small Uri - பயனர் வலை உரலி + User Web Url - எழுதியவர் + எழுத்தாளர் - ஆண்டு + வருஷம் - பங்களிப்பாளர் + Contributor - கடைசியாக எழுதியவர் + Last Author - திருத்த எண் + Revision Number - பதிப்பு + Version - உருவாக்கிய தேதி + Date Created - மொத்த திருத்தல் நேரம் + Total Editing Time - வார்ப்புரு + டெம்ப்லேட் - சொல் எண்ணிக்கை + Word Count - எழுத்து எண்ணிக்கை + Character Count - வரி எண்ணிக்கை + வரி எண்ணிக்க - பத்தி எண்ணிக்கை + Paragraph Count - பக்க எண்ணிக்கை + பக்க எண்ணிக்க - Slide Count + ஸ்லைடு எண்ணிக்க - சட்டக விகிதம் + Frame Rate Encoding Bitrate @@ -903,22 +915,22 @@ Compression - சட்டக அகலம் + Frame Width - சட்டக உயரம் + Frame Height - நோக்குதிசை + Orientation கோர் - படம் + Image - புகைப்படம் + Pho:to GPS @@ -930,7 +942,7 @@ கேட்பொலி - இசை + இசய் காணொளி @@ -939,73 +951,76 @@ ஆவணம் - முகவரி + Address - தேர்வு விருப்பங்கள் + தேர்வு ஆப்ஷனுங்க - தேர்ந்தெடு + Select'u - மறுசுழற்சி கூடை உருப்படி + Recycle bin item - குறுக்குவழி உருப்படி + குறுக்குவழி பொருள் - இயக்ககங்கள் + Drives இங்கு நகர்த்து - வன்பொருள் வெளியேற்றுவது பாதுகாப்பானது + Safe to remove hardware - சாதனம் இப்போது கணினியிலிருந்து பாதுகாப்பாக வெளியேற்றப்படலாம். + The device can now be safely removed from the computer. - சாதனத்தை வெளியேற்றுவதில் சிக்கல் + Problem Ejecting Device This device is currently in use. Close any programs, windows or tabs that might be using the device, and then try again. - வெளியேற்று + Eject Duplicate tab - தாவலைப் புது சாளரத்திற்கு நகர்த்து + புது ஃவிண்டோக்கு டæபை நகர்த்து - புது தாவல் + புது டæபு - சில பண்புகளில் தனிப்பட்ட தகவல்கள் இருக்கலாம். + Some properties may contain personal information. - எல்லா பண்புகளையும் அழி + Clear All Properties Check the status of file operations here - நிலை மையம் + Status center - {1}'இல் {0}'இற்கான தேடல் முடிவுகள் + Search results in {1} for {0} + + + Search results for `{0}` - விவரங்கள் + Details - இல்லை + Illy - பாதுகாக்கப்பட்ட முறைமை கோப்புகளை காட்டு + பாதுகாக்கப்பட்ட ஸிஸ்டம் கோப்புங்கள காட்டு Sync layout and sorting preferences across directories @@ -1014,16 +1029,16 @@ Customize the right click context menu - அசல் பாதை + Original path - சேர் + Ce:ru - திருத்து + Edit - அகற்று + Remove Open tabs in dual pane mode @@ -1032,13 +1047,13 @@ Show option to open folders in a new pane - Show option to copy path + பாதய நகலெடுக்க ஆப்ஷன் காட்டு Show option to create folder with selection - Show option to create shortcut + குறுக்குவழிய உருவாக்க ஆப்ஷன் காட்டு New pane @@ -1047,25 +1062,22 @@ Open in new pane - Files & folders + கோப்புங்க & கோப்புறய்ங்க - விவரங்கள் (Ctrl+Shift+1) - - - Tiles (Ctrl+Shift+2) + தகவலுங்க (Ctrl+Shift+1) - நீக்கப்பட்டத் தேதி + Date deleted Cloud Drives - உறுதிசெய் + Confirm - விரும்பும் பேர் + Virumbu'na pe:ru Toggle the preview pane @@ -1080,28 +1092,43 @@ Toggle the info pane - Toggle the info pane to view the detail/preview panes + Toggle visibility of the detail/preview panes - Toggle the Toolbar + Toggle toolbar + + + Toggle visibility of the toolbar + + + Toggle filter header + + + Toggle visibility of the filter header + + + Toggle dual pane + + + Toggle dual pane mode - எந்த முற்பார்வையும் இல்லை + No preview available - உருப்படி பேர் + பொருள் பேர் Unpin from Sidebar - பிணையம் + பிணயம் - கோப்பு விவரங்கள் + கோப்பு தகவலுங்க - கோப்பு முற்பார்வை + File preview Selected file preview pane @@ -1110,46 +1137,46 @@ பின்னூட்டம் - இணையத்திலுள்ளப்போதே கிடைக்கும் + Available when online Documentation - இணையமில்லாமலும் கிடைக்கும் + Offline'la kadýku - Partially available offline + ஆஃப்லய்ன்'ல கொஞ்சம் கிடய்க்கும் - ஒத்திசைகிறது + Syncing Excluded from sync - கணக்கிடப்படவில்லை + Not calculated - தெரியாதவை + Unknown If you change a file extension, the file might become unusable. Are you sure you want to change it? - CD ROM Drive + CD ROM ட்ரய்வு - Cloud Drive + க்லவ்டு ட்ரய்வு Fixed Disk Drive - நெகிழ் வட்டு இயக்ககம் + Floppy Disk Drive - பிணைய இயக்ககம் + பிணய ட்ரய்வு Unmounted Drive @@ -1161,40 +1188,40 @@ Removable Storage Device - தெரியாதவை + Unknown Virtual Drive - உருவாக்கிய தேதி + Date created - கூடுதல் விருப்பங்கள்... + அதிக ஆப்ஷனுங்க... - பிணைய இயக்ககத்தை இணை + Map network drive Pinned - கோப்பகங்கள் + Libraries No item selected - இலக்கு + Target - இணைப்பு மாறிகள் + Arguments - இயல்புநிலை + Default - உருப்படி எண்ணிக்கை + Item count This action requires administrator rights @@ -1206,22 +1233,22 @@ Columns (Ctrl+Shift+6) - தொடக்க மெனுவில் பொருத்து + Pin to the Start Menu - தொடக்க மெனுவிலிருந்து விடுவி + Unpin from the Start Menu - கோப்பகம் + Library - இருப்பிடங்கள்: + Locations: - இயல்புநிலை சேமிப்பு பாதையாக அமை + Set as default save path - இருப்பிடங்கள் இல்லை + No locations Input field cannot be empty! @@ -1230,16 +1257,22 @@ The name must not contain the following characters: \ / : * ? " < > | - இந்த பேரைப் பயன்படுத்துவது அனுமதிக்கப்படுவது இல்லை! + Using this name is not allowed! - அதே பேருடன் கோப்பகம் ஏற்கனவே உள்ளது! + Library with the same name already exists! Open Storage Sense + + Open the Storage Sense page in Windows Settings + + + Cleanup + - நகலெடு + Copy Copy {0, plural, one {item} other {items}} @@ -1248,7 +1281,7 @@ Delete {0, plural, one {item} other {items}} - நகர்த்து + Move {0, plural, one {One item will be moved} other {# items will be moved}} @@ -1260,7 +1293,7 @@ {0, plural, one {One item will be deleted} other {# items will be deleted}} - தொடர் + Continue {0, plural, one {There is one conflicting file name} other {There are # conflicting file names}} @@ -1275,31 +1308,31 @@ {0, plural, one {One item will be copied} other {# items will be copied}} - நிரந்தரமாக நீக்கு + Permanently delete - ISO வேகம் + ISO speed Replace existing - புது பேரை உருவாக்கு + Generate new name - தேர்வுடன் கோப்புறையை உருவாக்கு + Create folder with selection - வகை: + Type: - திருத்திய தேதி: + Date modified: - இதனுடன் திற + Open with - கோப்பு படவுரு + File icon Original path column @@ -1314,109 +1347,109 @@ Date deleted column - வரிசைப்படுத்து + Sort - மாற்றப்பட்ட தேதி + Date modified - அசல் கோப்புறை + Original folder - இறங்கு-வரிசை + Descending - மிகப்பெரிய + Huge Very large - பெரிய + Large - நடுநிலை + Medium - சிறிய + Small - மிகச்சிறிய + Tiny - எதிர்காலம் + Future - இன்று + Today - நேற்று + Yesterday - இந்த வாரம் முதலில் + Earlier this week - போன வாரம் + Last week - இந்த மாதம் முதலில் + Earlier this month - போன மாசம் + போன மாஸம் - இந்த ஆண்டு முதலில் + Earlier this year - போன வருஷம் + Last year - {0}-ஆம் வருஷம் + Year {0} - {0} உருப்படி + {0} item - {0} உருப்படிகள் + {0} items - மூடிய தாவலை மீண்டும் திற + Reopen tab - பேர் மாற்று + Rename - தனியுரிமை + Privacy - மறுசுழற்சி கூடை + the Recycle Bin - இரத்துசெய்கிறது + Canceling - அழி + Clear Widgets - ஒத்திசைவு நிலை + Sync status - பாதுகாப்பு + Security - மேம்பட்ட அனுமதிகள் + Advanced permissions - அனுமதி + Allow - மறு + Deny - முழு கட்டுப்பாடு + Full control List directory contents @@ -1425,34 +1458,34 @@ Modify - {0}'இற்கான அனுமதிகள் + Permissions for {0} Read and execute - படி + Read - குழு அல்லது பயனர் பேர்கள் + Group or user names - எழுது + Write - தெரியாத கணக்கு + Unknown account - இந்த பொருளின் பாதுகாப்பு பண்புகளை பார்க்க உங்களுக்கு அனுமதி இல்லை. தொடர, 'மேம்பட்ட அனுமதிகள்'-ஐ அழுத்தவும். + You do not have permissions to view the security properties of this object. Click "Advanced permissions" to proceed. - உரிமையாளர்: + Owner: - அறியாத உரிமையாளர் + Unknown owner - காப்பகத்தை பிரித்தெடு + Extract archive Open destination folder when complete @@ -1461,94 +1494,94 @@ Extract to {0}\ - புது கோப்பகத்தை உருவாக்கு + Create new library - இயல்புநிலை கோப்பகங்களை மீட்டெடு + Restore default libraries Are you sure you want to restore the default libraries? All your files will remain on your storage. - கோப்பகங்களை மீட்டெடு + Restore Libraries - உரிமையாளர் + Owner - சிறப்பு + Special - அணுகல் + Access - இற்கு பொருந்தும் + Applies to Principal - கோப்புகள் + files - இந்த கோப்புறை + this folder - துணைக்கோப்புறைகள் + subfolders Inherited - அனுமதிகள் + Permissions - இந்த பொருளின் பாதுகாப்பு பண்புகளைப் பார்க்க உங்களுக்கு அனுமதி இல்லை. பொருளின் உரிமையை எடுத்துகொள்ள நீங்கள் முயற்சிக்கலாம். + You do not have permissions to view the security properties of this object. You can try to take ownership of this object. - இந்த கோப்புறையும் அதன் கோப்புகளும் + This folder and files - இந்த கோப்புறை மற்றும் அதன் துணைக்கோப்புறைகளும் கோப்புகளும் + This folder, subfolders and files - கோப்புகள் மட்டும் + Only files - துணைக்கோப்புறைகள் மட்டும் + Only subfolders - துணைக்கோப்புறைகளும் கோப்புகளும் மட்டும் + Only subfolders and files - இந்த கோப்புறை மட்டும் + Only this folder - இந்த கோப்புறையும் அதன் துணைக்கோப்புறைகளும் + This folder and subfolders - தரவை சேர்த்தல் + Append data - அனுமதியை மாற்று + Change permission - கோப்புறைகளை உருவாக்கு + Create folders - கோப்புகளை உருவாக்கு + Create files - துணைக்கோப்புறைகளையும் கோப்புகளையும் நீக்கு + Delete subdirectories and files - கோப்புகளை இயக்குதல் + Execute files Read attributes - தரவை படித்தல் + Read data Read extended attributes @@ -1560,7 +1593,7 @@ Take ownership - கோப்புறையை காண்க + Visit folder Write attributes @@ -1587,7 +1620,7 @@ Replace all child object permissions entries with inheritable permission entries from this object - Close active pane + Close pane Enter compact overlay @@ -1605,13 +1638,13 @@ மொழி - வர்த்தக முத்திரைகள் + வர்த்தக முத்திரய்ங்க கோப்பு பதிப்பு - முழு முற்பார்வையை ஏற்று + Load full preview Downloads the item from the cloud and loads the preview @@ -1626,16 +1659,16 @@ WSL - {0} பிரிவை மறை + {0} பகுதிய மறய் - கோப்பை சேர் + கோப்ப சேர் - புதுப்பிப்புகள் கிடைக்கின்றன + புதுப்பிப்புங்க இருக்கு - புதுப்பிப்புகள் நிறுவ தயாராக உள்ளன + புதுப்பிப்புங்க நிறுவ தயாரா இருக்கு மூடு @@ -1646,23 +1679,14 @@ முகப்பு - - Ctrl+Shift+1 - - - Ctrl+Shift+2 - - - Ctrl+Shift+6 - - காப்பக பிரிப்பு வெற்றுகரமாக முடிந்தது. + The archive extraction completed successfully. - காப்பகத்தை பிரிக்கிறது + Extracting archive - பிரித்தெடுத்தல் முடிந்தது! + Extracting complete! Open parent folder @@ -1671,19 +1695,19 @@ Unknown - குறிக்குழுகளைத் திருத்து + குறிங்கள திருத்து Part of set - Open drive + ட்ரய்வ தெற Please insert a disc into drive {0} - வட்டை சொருகவும் + ஒரு வட்ட சொருங்க சரி @@ -1692,34 +1716,34 @@ Credential required - பெயரில்லா + Anonymous Provide your credential: - பயனர்பெயர் + பயனர்பேர் - உருப்படிகள் எதுவும் கண்டுப்பிடிக்கப்படவில்லை + No items found - பதிவுகோப்பு இருப்பிடத்தை திற + Open log location - {0}'இல் இணைப்பை உருவாக்கு + {0}'ல இணய்ப்ப உருவாக்கு Columns - - டைல்கள் + + Cards - கோப்புறைகளைப் புது தாவலில் திற + ஃபோல்டர்களை புது டæபுல திற - நிலை மையம் + நிலய் மய்யம் Date created column @@ -1731,13 +1755,13 @@ Experimental feature flags - உதவியும் ஆதரவும் + உதவியும் ஆதரவு - வசதி கோரிக்கை சமர்ப்பி + வசதி கோரிக்க சமர்ப்பி - பிழை அறிக்கையை சமர்ப்பி + பிழ அறிக்க சமர்ப்பி Set Files as the default file manager @@ -1746,40 +1770,40 @@ Use Files as Open File Dialog - குறிக்குழு + குறி - ஓர் அழுத்ததில் உருப்படிகளை திற + Open files with a single click - கோப்புறை பாதை + கோப்புற பாத - அமைப்புகளை ஏற்றுமதி செய் + ஏற்றுமதி அமய்ப்புங்க - அமைப்புகளை இறக்குமதி செய் + இறக்குமதி அமய்ப்புங்க - அமைப்புகளை இறக்குமதி செய்ய முடியவில்லை. அமைப்புகள் கோப்பானது பாழடைந்துவிட்டது. + Couldn't import settings. The settings file is corrupted. - அமைப்புகளை இறக்குமதி செய்வதில் பிழை + Error importing settings - மறுசுழற்சி கூடையை வெறுமையாக்கு + மறுசுழற்சி கூடய வெறுமயா ஆக்கு - பக்கப்பட்டி + பக்கப்பட்டய் Choose a custom folder icon - தனிப்பயனாக்கம் + Customization - இயல்புநிலையை மீட்டெடு + Restore default Open tab in existing instance when opening Files from another app @@ -1788,28 +1812,28 @@ Startup settings - இணக்கத்தன்மை + Compatibility - எதுவுமில்லை + None On Windows login - On this program start + இந்த செயலி தொடக்கத்துல - முறைமை + ஸிஸ்டம் - System (Enhanced) + ஸிஸ்டம் (மேம்பட்ட) - 16-bit (65536) color + 16-பிட் (65,536) நிறம் - 8-bit (256) color + 8-பிட்டு (256) நிறம் Disable full-screen optimizations @@ -1826,8 +1850,20 @@ Register this program for restart + + Start window + + + சாதா விண்டோ + + + Minimized + + + Maximized + - நிர்வாகியாக இயக்கு + Run as administrator Run compatibility troubleshooter @@ -1842,7 +1878,7 @@ Do not override DPI - இணக்கத்தன்மை பயன்முறை + Compatibility mode No reduced color @@ -1853,47 +1889,50 @@ This option modifies the system registry and can have unexpected side effects on your device. Continue at your own risk. + + The flatten operations are permanent and not recommended. Continue at your own risk. + - கோப்பகத்தை உருவாக்கு + Create Library - கோப்பகப் பேரை உள்ளிடுங்கள் + Enter Library name - கோப்புறை அளவுகளை கணக்கிடு + Calculate folder sizes - சமீபத்திய கோப்புகள் + Recent files - Windows தொடங்கும்போது Files'ஐ திற + Open Files on Windows startup - பண்புக்கூறுகள் + Attributes - மறைக்கப்பட்ட + Hidden - மேலும் விவரங்கள் + அதிக விவரங்க - படிக்க-மட்டும் + படிக்க மட்டும் Cleanup your drive contents - வகை + வகய் - பைட்கள் + பைட்டுங்க - பிரித்தெடு + Extract - கோப்புகளை பிரித்தெடு + கோப்புங்கள பிரித்தெடு இங்கு பிரித்தெடு @@ -1902,49 +1941,52 @@ Ctrl+E - {0}'இற்கு பிரித்தெடு + {0}'கு பிரித்தெடு - Run script + ஸ்க்ரிப்ட்ட ஓட்டு - PowerShell'உடன் இயக்கு + PowerShell மூலம் ஓட்டு Calculating folder sizes is resource intensive and may cause your CPU usage to increase. - இடப்புறம் சுழற்று + Rotate left - வலப்புறம் சுழற்று + Rotate right நிறுவு - Close tabs to the left + இடதுல இருக்குற டæபுகளை மூடு - வலது பக்கம் இருக்கும் தாவல்களை மூடு + வலதுல இருக்குற டæபுகளை மூடு - மற்ற தாவல்களை மூடு + மற்ற டæபை மூடு + + + டæபு எல்லாம் மூடு - இசை + இசய் - புகைப்படங்கள் + Pictures - Files-ஐ புதுப்பி + Files'அ புதுப்பி - குறிக்குழுகள் + குறிங்க - Universal + உலகளாவிய எ.கா: {0}, {1} @@ -1953,16 +1995,16 @@ Show thumbnails - மீண்டும் முயல்க + Retry Move operation is not supported in this context. Do you want to copy the items instead? - மறைக்கபட்டுள்ள உருப்படிகள் + மறைக்கப்பட்ட பொருளுங்க - தனிப்பயன் + Custom Set as desktop slideshow @@ -1985,20 +2027,32 @@ Behaviors - - Review Files + + Hello! + + + Enjoying Files? Please consider reviewing in the Microsoft Store. + + + Enjoying Files? Please consider supporting the project on GitHub. - - Would you like to review Files? + + Sponsor + + + Rate us + + + Dismiss - பின்புலமாக அமை + Set as background - திரைப்பலகப் பின்புலமாக அமை + Set as desktop background - இயல்புநிலையாக அமை + இயல்பாக்கு Date column @@ -2016,17 +2070,32 @@ Type column - பூட்டுத்திரை + Lockscreen - - Open folders with a single click in the Columns Layout + + Open folders with a single click - உருப்படிகளத் திறக்குது + பொருளுங்கள தெறப்பது Compress + + Move all contents from subfolders into the selected location + + + Flatten folder + + + Flatten + + + Flattening a folder will move all contents from its subfolders to the selected location. This operation is permanent and cannot be undone. By using this experimental feature, you acknowledge the risk and agree not to hold the Files team responsible for any data loss. + + + Show flatten options + Select files and folders when hovering over them @@ -2034,22 +2103,22 @@ Are you sure you want to restore all items in the recycle bin? - எல்லா உருப்படியையும் மீட்டெடு + எல்லா பொருளயும் மீட்டெடு - Do you want to restore the {0} selected item(s)? + Do you want to restore the {0, plural, one {selected item} other {{0} selected items}}? - Restore selection + தேர்வ மீட்டெடு - Restore All Items + எல்லா பொருளயும் மீட்டெடு மீட்டெடு - குறுக்குவழியை திறக்க முடியவில்லை + Shortcut cannot be opened The target destination cannot be found {0}. Do you want to delete this shortcut? @@ -2058,25 +2127,31 @@ You don't have permission to access this folder. - காப்பகக் கடவுச்சொல் + Archive password + + + Encoding + + + {0} (detected) - பாதை + பாத Sorting and grouping - காப்பகத்தை உருவாக்கு + Create archive உருவாக்கு - {0}-ஐ உருவாக்கு + {0}'அ உருவாக்கு - ஒரு பேரை உள்ளிடுங்கள் + ஒரு பேர உள்ளிடுங்க Compression level @@ -2097,16 +2172,31 @@ Fast - எதுவுமில்லை + None - Splitting size + பிரிப்பு அளவு - + Do not split + + Auto + + + Dictionary size + + + Word size + + + Estimated memory usage: {0} + + + Available memory: {0} + - குறுந்தட்டு + CD DVD @@ -2118,49 +2208,55 @@ Blu-ray - குறியாக்கம் + Encryption கடவுச்சொல் - வடிவமைப்பு + வடிவமய்ப்பு Sync status column - இதன்படி குழுப்படுத்து + Group by - இதன்படி வரிசைப்படுத்து + Sort by - Group in descending order + இறங்குவரிசை'ல குழுப்படுத்து - Sort in descending order + இறங்குவரிசை'ல வரிசப்படுத்து - ஒரு குறுக்குவழியை உருவாக்கு + ஒரு புது குறுக்குவழிய உருவாக்கு Create shortcuts to local or network programs, files, folders, computers or Internet addresses. - உருப்படியின் இருபிடத்தை உள்ளிடுங்கள்: + பொருள்'ஓட இருப்பிடத்த உள்ளிடுங்க: - ஒரு குறுக்குவழியை உருவாக்கும் + ஒரு குறுக்குவழிய உருவாக்கும் Recently used files is currently disabled in Windows File Explorer. - அமைப்புகள் கோப்பைத் திருத்து + அமய்ப்புங்க கோப்ப திருத்து + + + Open settings file in your default editor - - புதிது என்ன + + Release Notes + + + Open Release Notes Creating a shortcut in this location requires administrator privileges @@ -2169,16 +2265,16 @@ Would you like to create the shortcut on the desktop instead? - கொடுதுள்ள பேர் செல்லதது + கொடுக்கப்பட்ட பேர் செல்லாதது - அமைப்புகள் + அமய்ப்புங்க Double click on a blank space to go up one directory - மேலும் பார் + இன்னும் பார் Show edit tags flyout @@ -2187,16 +2283,16 @@ Show compression options - இற்கு அனுப்பு மெனுவை காட்டு + Show send to menu - Show option to open folders in a new tab + ஃபோல்டர்களை ஒரு புது டæபுல திறக்க ɔப்ஷன் காட்டு Show option to open folders in a new window - விரைவு அணுகல் + Quick access Enter your credentials to connect to: {0} @@ -2208,58 +2304,55 @@ Remember my credentials - பிணைய-கோப்புறை பிழை + பிணய-கோப்புற பிழய் - நிரல் பதிப்பு + செயலி பதிப்பு Windows பதிப்பு - எப்போதும் + எப்போவுமே - நிரந்திர நீக்கம் மட்டும் + நிரந்திர நீக்கல் மட்டுமே - எப்போதுமில்லை + Never - குறிக்குழு வண்ணம் + குறி நிறம் - புது குறிக்குழு + புது குறி - Create new tag + புது குறிய உருவாக்கு - ஏற்றுது... + ஏற்றுறோம்... Context menu options - கோப்பு நீட்டிப்புகளைக் காட்டு - - - மறைக்கப்பட்ட உருப்படிகளைக் காட்டு + File extensions - வடிவமை... + Format உதவி - Toggle full screen + Full screen - Are you sure you want to delete this tag? + இந்த குறிய நிச்சயமா நீக்க வேண்டுமா? - அனைத்தயும் இயக்கு + Play உயரம் @@ -2271,10 +2364,10 @@ Apply this action to all conflicting items - Windows முனையத்தில் திற + Windows Terminalல திற - Windows முனையத்தில் நிர்வாகியாகத் திற + Windows Terminalல நிர்வாகியா திற சேமி @@ -2286,79 +2379,82 @@ Reorder sidebar items - Hashes + ஹேஷுங்க - கணக்கிடும்போது ஒரு பிழை ஏற்பட்டது + An error occurred during the calculation Failed to calculate the hash, please close the file and try again. - Hashes aren't available for online files + ஆன்லய்ன் கோப்புங்களுக்கு ஹேஷுங்க இல்ல - வழிமுறை + வழிமுற - Hash value + ஹேஷ் மதிப்பு Select hashes to show - கணக்கிடுது... + கணக்கிடுறோம்... - பொருத்தப்பட்ட உருப்படிகள் + பொருத்தப்பட்ட பொருளுங்க - அளவை குறை + அளவ குற - அளவை அதிகரி + அளவ அதிகரி - Toggle sort direction + Sort direction - செல்லாத பேர் + செல்லாத பெயர் Name must not be empty or start or end with a period. - காணொளிகள் + காணொளிங்க - Launch preview popup + Preview popup Toggle compact overlay - இணைய-உதவி பக்கத்தை உலாவியில் திற + Open the online help page in your browser - Toggle full screen + Toggle full screen mode - Enter compact overlay + Enter compact overlay mode - Exit compact overlay + Exit compact overlay mode - Toggle compact overlay + Toggle compact overlay mode - தேடல் பெட்டிக்குப் போ + Start search in the OmniBar - Toggle whether to show hidden items + Toggle visibility of hidden items + + + Toggle visibility of dot files - Toggle whether to show file extensions + Toggle visibility of file extensions Toggle the preview pane to view file previews @@ -2367,154 +2463,175 @@ Toggle whether to show sidebar - நகலகத்திற்கு உருப்படி(கள்)-ஐ நகலெடு + Copy selected {0, plural, one {item} other {items}} - தேர்ந்தெடுத்துள்ள உருப்படிகளின் பாதையை நகலகத்தில் நகலெடு + Copy path of the current directory + + + Copy path of selected items + + + Copy path of selected items with quotes - தேர்ந்தெடுத்துள்ள உருப்படிகளின் பாதையை மேற்கோள்குறியுடன் நகலகத்தில் நகலெடு + Copy path of the current directory with quotes - நகலகத்திற்கு உருப்படி(கள்)-ஐ வெட்டு + Cut selected {0, plural, one {item} other {items}} - நகலகத்திலுள்ள உருப்படி(கள்)-ஐ தற்போதய கோப்புறையில் ஒட்டு + Paste items to the current folder + + + Paste items to the current folder as shortcuts - நகலகத்திலுள்ள உருப்படி(கள்)-ஐ தேர்ந்தெடுத்துள்ள கோப்புறையில் ஒட்டு + Paste items to the selected folder + + + Paste to selected folder - உருப்படி(கள்)-ஐ நீக்கு + Delete selected {0, plural, one {item} other {items}} - புது கோப்புறையை உருவாக்கு + புது கோப்புறய உருவாக்கு - Create new shortcut(s) to selected item(s) + Create new {0, plural, one {shortcut} other {shortcuts}} to selected {0, plural, one {item} other {items}} Create new shortcut to any item - மறுசுழற்சி கூடையை வெறுமையாக்கு + Empty the contents of Recycle Bin Open "Format Drive" menu for selected item - Restore selected item(s) from recycle bin + Restore selected {0, plural, one {item} other {items}} from recycle bin Restore all items from recycle bin - உருப்படி(கள்)-ஐ திற + Open {0, plural, one {item} other {items}} - Open item(s) with selected application + Open {0, plural, one {item} other {items}} with selected application Open parent folder of searched item - பக்க உள்ளடங்களைப் புதுப்பி + Refresh page contents - தேர்ந்தெடுத்துள்ள உருப்படியை மறுபேரிடு + Rename selected item - எல்லா உருப்படிகளையும் தேர்ந்தெடு + எல்லா பொருளுங்களயும் தேர்ந்தெடு - Invert item selection + Invert selected items - உருப்படி தேர்வை அழி + Clear selected items Toggle item selection - தேர்ந்தெடுதுள்ள உருப்படி(கள்)-ஐ மற்றவருடன் பகிர் + Share selected {0, plural, one {file} other {files}} with others - தொடக்க மெனுவில் உருப்படி(கள்)-ஐ பொருத்து + Pin {0, plural, one {item} other {items}} to the Start Menu - தொடக்க மெனுவிலிருந்து உருப்படி(கள்)-ஐ விடுவு + Unpin {0, plural, one {item} other {items}} from the Start Menu - Pin folder(s) to Sidebar + Pin {0, plural, one {folder} other {folders}} to Sidebar - Unpin folder(s) from Sidebar + Unpin {0, plural, one {folder} other {folders}} from Sidebar - தேர்ந்தெடுத்துள்ள புகைப்படத்தைத் திரைப்பலகப் பின்புலமாக அமை + Set selected picture as desktop background Set selected pictures as desktop slideshow - தேர்ந்தெடுத்துள்ள புகைப்படத்தைப் பூட்டுத்திரை பின்புலமாக அமை + Set selected picture as lockscreen background Set selected picture as the app background + + Install font + + + Install driver + + + Install certificate + - தேர்ந்தெடுத்துள்ள எழுத்துரு(கள்)-ஐ நிறுவு + Install selected {0, plural, one {font} other {fonts}} - தேர்ந்தெடுத்துள்ள inf கோப்பு(கள்) மூலம் இயக்குநிரல்(கள்)-ஐ நிறுவு + Install {0, plural, one {driver} other {drivers}} using selected inf {0, plural, one {file} other {files}} - தேர்ந்தெடுத்துள்ள சான்றிதழ்(கள்)-ஐ நிறுவு + Install selected {0, plural, one {certificate} other {certificates}} - தேர்ந்தெடுத்துள்ள நிரலை நிர்வாகியாக இயக்கு + Run selected application as administrator - தேர்ந்தெடுத்துள்ள நிரலை வேறோரு பயனராக இயக்கு + Run selected application as another user - தேர்ந்தெடுத்துள்ள PowerShell சிறுநிரலை இயக்கு + Run selected PowerShell script Launch preview in popup window - தேர்ந்தெடுத்துள்ள உருப்படி(கள்)-உடன் காப்பகத்தை உருவாக்கு + Create archive with selected {0, plural, one {item} other {items}} - Create 7z archive instantly with selected item(s) + Create 7z archive with selected {0, plural, one {item} other {items}} - Create zip archive instantly with selected item(s) + Create zip archive with selected {0, plural, one {item} other {items}} - Extract items from selected archive(s) to any folder + Extract selected {0, plural, one {archive} other {archives}} to any folder - Extract items from selected archive(s) to current folder + Extract selected {0, plural, one {archive} other {archives}} to the current folder - Extract items from selected archive(s) to new folder + Extract selected {0, plural, one {archive} other {archives}} to new folder - தேர்ந்தெடுத்துள்ள படம்(கள்)-ஐ இடப்புறமாகச் சுழற்று + Rotate selected {0, plural, one {image} other {images}} to the left - தேர்ந்தெடுத்துள்ள படம்(கள்)-ஐ வலப்புறமாகச் சுழற்று + Rotate selected {0, plural, one {image} other {images}} to the right - அமைப்புகள் பக்கத்தைத் திற + ஸெட்டிங்ஸ் பக்கத்த திற - முனையத்தில் கோப்புறையைத் திற + கோப்புறய முனையத்துல திற - நிர்வாகியாக முனையத்தில் கோப்புறையைத் திற + முனையத்துல ஃபோல்டரை நிர்வாகியா திற Decrease item size in the current view @@ -2523,10 +2640,10 @@ Increase item size in the current view - விவரக் காட்டிக்கு மாறு + தகவல் காட்சிக்கு மாறு - - டைல் காட்சிக்கு மாறு + + Switch to cards view பட்டியல் காட்சிக்கு மாறு @@ -2547,73 +2664,73 @@ Switch views adaptively - பேரால் உருப்படிகளை வரிசைப்படுத்து + பேரால பொருள்களை வரிசைப்படுத்து - திருத்தப்பட்ட தேதியால் உருப்படிகளை வரிசைப்படுத்து + Sort items by date modified - உருவாக்கப்பட்ட தேதியால் உருப்படிகளை வரிசைப்படுத்து + Sort items by date created - அளவால் உருப்படிகளை வரிசைப்படுத்து + அளவால பொருள்களை வரிசைப்படுத்து - வகையால் உருப்படிகளை வரிசைப்படுத்து + வகயால பொருள்களை வரிசைப்படுத்து - ஒத்திசைவு நிலையால் உருப்படிகளை வரிசைப்படுத்து + Sort items by sync status - குறிக்குழுகளால் உருப்படிகளை வரிசைப்படுத்து + ^டேகுகளால பொருள்களை வரிசைப்படுத்து - அசல் கோப்புறையால் உருப்படிகளை வரிசைப்படுத்து + அசல் ஃபோல்டரால பொருள்களை வரிசைப்படுத்து - நீக்கப்பட்ட தேதியால் உருப்படிகளை வரிசைப்படுத்து + Sort items by date deleted - Sort items in ascending order + ஏறுவரிசைல பொருள்களை வரிசைப்படுத்து - Sort items in descending order + இறங்குவரிசைல பொருள்களை வரிசைப்படுத்து Toggle item sort direction - குழுப்படுத்தாமல் உருப்படிகளைப் பட்டியலிடு + List items without grouping - பேரால் உருப்படிகளைக் குழுப்படுத்து + பேரால பொருள்களை குழுப்படுத்து - திருத்தப்பட்ட தேதியால் உருப்படிகளை குழுப்படுத்து + Group items by date modified - உருவாக்கப்பட்ட தேதியால் உருப்படிகளை குழுப்படுத்து + Group items by date created - அளவால் உருப்படிகளைக் குழுப்படுத்து + அளவால பொருள்களை குழுப்படுத்து - வகையால் உருப்படிகளைக் குழுப்படுத்து + வகையால பொருள்களை குழுப்படுத்து - ஒத்திசைவு நிலையால் உருப்படிகளைக் குழுப்படுத்து + Group items by sync status - குறிக்குழுகளால் உருப்படிகளைக் குழுப்படுத்து + ^டேகுகளால பொருள்களை குழுப்படுத்து - அசல் கோப்புறையால் உருப்படிகளைக் குழுப்படுத்து + Group items by original folder - நீக்கப்பட்ட தேதியால் உருப்படிகளைக் குழுப்படுத்து + Group items by date deleted - கோப்புறை பாதையால் உருப்படிகளை குழுப்படுத்து + Group items by folder path Sort groups in ascending order @@ -2625,16 +2742,16 @@ Toggle group sort direction - புது தாவலைத் திற + ஒரு புது ^டேபை திற - Navigate backward in navigation history + Navigate backward - Navigate forward in navigation history + Navigate forward - Navigate up one directory + ஒரு ஃபோல்டர் மேல போ Duplicate current tab @@ -2658,28 +2775,31 @@ Close tabs other than current tab - தேர்ந்தெடுத்துள்ள தாவலை தவிர மற்றதை மூடு + Close tabs other than selected tab + + + Close all tabs including the current tab - கடைசியாய் மூடிய தாவலை மீண்டும் திற + Reopen recently closed tab Move to the previous tab - Move to the next tab + அடுத்த டæபுக்கு நகரு - தற்போதைய தாவலை மூடு + தற்போதைய டæபை மூடு - Close active pane + Close the active pane Focus other pane - Switch focus to the non active pane + Switch focus to the other pane Toggle the sidebar @@ -2717,7 +2837,7 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Tab + டæபு Key name for hotkeys in menus. Use abbreviation if possible. @@ -2729,15 +2849,15 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Left + இடது Key name for hotkeys in menus. Use abbreviation if possible. - Right + வலது Key name for hotkeys in menus. Use abbreviation if possible. - Down + கீழ் Key name for hotkeys in menus. Use abbreviation if possible. @@ -2745,7 +2865,7 @@ Key name for hotkeys in menus. Use abbreviation if possible. - முகப்பு + Home Key name for hotkeys in menus. Use abbreviation if possible. @@ -2773,7 +2893,7 @@ Key name for hotkeys in menus. Use abbreviation if possible. - அழி + Clear Key name for hotkeys in menus. Use abbreviation if possible. @@ -2793,15 +2913,15 @@ Key name for hotkeys in menus. Use abbreviation if possible. - நிரல் + App Key name for hotkeys in menus. Use abbreviation if possible. - நிரல்2 + App1 Key name for hotkeys in menus. Use abbreviation if possible. - நிரல்2 + App2 Key name for hotkeys in menus. Use abbreviation if possible. @@ -2833,7 +2953,7 @@ Key name for hotkeys in menus. Use abbreviation if possible. - பிடித்தவை + பிடிச்சது Key name for hotkeys in menus. Use abbreviation if possible. @@ -2875,7 +2995,7 @@ Toggle selection - கோப்பு நீட்டிப்புகளை மாற்றும்போது எச்சரிக்கை காட்டு + Show warning when changing file extensions இந்த PC @@ -2887,13 +3007,13 @@ Untagged - Moves to the previous tab + Previous tab - Moves to the next tab + Next tab - தற்போதைய தாவலை மூடும் + Close tab பாதையை திருத்து @@ -2902,7 +3022,7 @@ Redo - செயல்தவிர் + Undo இல்லாத இருப்பிடம் @@ -2923,25 +3043,25 @@ Show checkboxes when selecting items - Focus path bar + Edit path in the OmniBar - புது உருப்படியை உருவாக்கு + புது பொருள் உருவாக்கு No groups or users have permission to access this object. However, the owner of this object can assign permissions. - உருப்படியுடைய இருப்பிடத்தை திற + பொருள் இடத்தை திற - நிரந்தரமாய் நீக்கு + நிரந்தரமா நீக்கு - உருப்படி(கள்)-ஐ நிரந்தரமாய் நீக்கு + Delete selected {0, plural, one {item} other {items}} permanently - தேர்ந்தெடுத்து இருக்கின்ற ஊடகக் கோப்புகளை இயக்கு + Play the selected media files Undo the last file operation @@ -2950,10 +3070,10 @@ Redo the last file operation - இருப்பிடம்: + இடம்: - மாசத்தால் உருப்படிகளை குழுப்படுத்து + Group items by month Group items by year @@ -3013,22 +3133,31 @@ To try taking ownership of the item, which includes permission to view its properties, click Change above. - அனுமதிகளை காட்ட முடியவில்லை. + Unable to display permissions. - '{0}'-இல் என் மாற்றங்களை விடு + என் மாற்றங்களை '{0}'ல விடு Discard my changes - '{0}'-கு என் மாற்றங்களை கொண்டு வா + என் மாற்றங்களை '{0}'கு கொண்டு வா - You have uncommitted changes on this branch. What would you like to do with them? + இந்த கிளைல கமிட்டு-பண்ணாத மாற்றங்கள் வச்சிருக்கீங்க. அதை எல்லாம் என்ன செய்ய விரும்புறீங்கள்? + + + You have an ongoing merge with unresolved conflicts. Please resolve the conflicts or abort the merge to switch branches. + + + Abort merge and switch to '{0}' + + + Stay on '{0}' and resolve conflicts - கிளையை மாற்று + Switch branch கிளைகள் @@ -3040,7 +3169,7 @@ புது கிளை - செல்லாத கிளை பேர் + Invalid branch name கிளையை உருவாக்கு @@ -3055,22 +3184,28 @@ புது கிளைக்கு மாறு - தற்போது தேர்ந்தெடுத்துள்ள உருப்படி(கள்)-உடன் ஒரு கோப்புறையை உருவாக்கு + Create a folder with the currently selected {0, plural, one {item} other {items}} - பண்புகளை திற + பண்புங்க + + + கோப்பு-உலவி பண்புங்கள திற - பண்புகள் சாளரத்தை திற + பண்புகள் ஃவிண்டோவை திற + + + கோப்பு-உலவி பண்புகள் ஃவிண்டோ திற - உள்ளகங்கள் + Locals - தொலைவுகள் + Remotes - இங்லிஷில் இருந்து உங்கள் மொழிக்கு Crowdin-இல் மொழிபெயருங்கள் + Translate on Crowdin Fetch @@ -3079,38 +3214,41 @@ இழு - git fetch-ஐ இயக்கு + git fetch ஓட்டு + + + Clone a git repo - git pull-ஐ இயக்கு + git pull ஓட்டு - முற்பார்வை + Preview Git நிலை - Git பிழை + Git பிழ git pull failed due to a timeout error - (பல மதிப்புகள்) + (multiple values) Text indicating that multiple selected files have different metadata values. - ஆசிரியர் + Author - Date committed + Comít'na the:dhi - Commit message + கமிட் உர - Commit SHA + கமிட் SHA Git @@ -3122,55 +3260,55 @@ மைக்கா - மாற்று மைக்கா + Mica Alt - Backdrop Material + Backdrop Solid - கிளைகளை நிர்வகி + Manage branches தள்ளு - git push-ஐ இயக்கு + git push ஓட்டு ஒத்திசை - git pull-ஐ இயக்கி, அப்புறம் git push-ஐ இயக்கு + git pull ஓட்டு, பிறகு git push {0} outgoing / {1} incoming commits - Connect to GitHub + GitHub-உடன் இணை Enter the following code at the link below to start working with GitHub repositories. - Files-ஆல் தற்போது GitHub-ஐ அணுக முடியவில்லை. + Files cannot access GitHub right now. - - கோப்புறையை VS Code-இல் திற + + Open folder in {0} - - Open the current directory in Visual Studio Code + + Open the current directory in {0} - - களஞ்சியத்தை VS Code-இல் திற + + Open repo in {0} - - Open the root of the Git repo in Visual Studio Code + + Open the root of the Git repo in {0} - Copy code + கோட நகலு Added @@ -3185,37 +3323,37 @@ Untracked - Unable to display current owner. + இப்பொ-இருக்க உரிமயாளர காட்ட முடியல. Great! You are now authorized. - களஞ்சியத்தை துவங்கி வை + களஞ்சியத்தை துவங்கு - Initialize a Git repository + Initialize current folder as a git repository Remote Repository Name - Current Branch + தற்போதைய கிளை Path column - Sort items by path + பாதயால பொருள்களை வரிசப்படுத்து - Open directory in new pane + Open selected directory in a new pane - Open directory in new tab + Open selected directory in a new tab - Open directory in new window + Open selected directory in a new window எல்லாம் திற @@ -3224,14 +3362,14 @@ Open all tagged items - Drive ({0}) + ({0}) 'ட்ரைவு {0} is the drive letter. - Invalid command + செல்லாத கட்டள - '{0}' is not recognized as a command. + '{0}' ஒரு கட்டளையா அறியப்படலை. Command not executable @@ -3240,44 +3378,44 @@ The '{0}' command is not ready to be executed. - Command palette + Command Palette - Open command palette + Open Command Palette in the OmniBar - இங்கு தொடங்கு: + Start in: - BitLocker-ஐ துவங்கு + BitLocker 'ஆன் பண்ணு - BitLocker-ஐ நிர்வகி + Manage BitLocker The following items are too large to be copied to this drive - Format drive + ட்ரய்வ வடிவமய் - இனிமேல் காட்டாதே + இனிமேல் காட்டாத - Files நிர்வாகியாய் இயங்குகிறது + Files நிர்வாகியா ஓடுது Due to a limitation with Windows and WinAppSdk, drag and drop isn't available when running Files as admin. If you wish to use drag and drop, you can work around this limitation by opening UAC (User Account Control) from the Start Menu and selecting the third level, and restarting Windows. Otherwise, you can keep using Files without drag & drop. - சாளரத்தை மூடியதற்கு அப்புறமும் நிரலை பின்புலத்தில் இயங்க விடு + Leave app running in the background when the window is closed - நீலம் + Ni:lam One of the custom color themes - நீலச் சாம்பல் + Blue Gray One of the custom color themes @@ -3293,7 +3431,7 @@ One of the custom color themes - சாம்பல் + Sa:mbal One of the custom color themes @@ -3301,7 +3439,7 @@ One of the custom color themes - பச்சை + Paccy One of the custom color themes @@ -3341,25 +3479,25 @@ One of the custom color themes - ஊதா-சிவப்பு ஒளி + Violet Red Light One of the custom color themes - மஞ்சள் தங்கம் + Yellow Gold One of the custom color themes - அழிப்பு முடிந்தது + அழிப்பு முடிஞ்சிது - Name: + பேரு: {0} of {1} processed Shown in a StatusCenter card. Used as "64 MB of 1 GB processed" - உருப்படிகள் + பொருள்கள் Files can't initialize this directory as a Git repository. @@ -3368,11 +3506,14 @@ Contributing Artist - Add page + பக்கம் சேர் Processing items... + + Discovering items... + வேகம்: @@ -3385,61 +3526,129 @@ Shown in a StatusCenter card. - Compressed {0} item(s) to "{1}" + Compressed {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Compressed {0} item(s) from "{1}" to "{2}" + Compressed {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error compressing {0} item(s) to "{1}" + Error compressing {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to compress {0} item(s) from "{1}" to "{2}" + Failed to compress {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Compressing {0} item(s) to "{1}" + Compressing {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Compressing {0} item(s) from "{1}" to "{2}" + Compressing {0, plural, one {# item} other {# items}} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Canceled cloning {0} to "{1}" + Shown in a StatusCenter card. + + + Canceled cloning {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Cloned "{0}" to "{1}" + Shown in a StatusCenter card. + + + Cloned {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Error cloning "{0}" to "{1}" + Shown in a StatusCenter card. + + + Failed to clone {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Cloning "{0}" to "{1}" + Shown in a StatusCenter card. + + + Cloning {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Canceled installing {0} fonts + Shown in a StatusCenter card. + + + Canceled installing {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Error installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Failed to install {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} from "{1}" Shown in a StatusCenter card. - Canceled copying {0} item(s) to "{1}" + Canceled copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Canceled copying {0} item(s) from "{1}" to "{2}" + Canceled copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Copied {0} item(s) to "{1}" + Copied {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Copied {0} item(s) from "{1}" to "{2}" + Copied {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error copying {0} item(s) to "{1}" + Error copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to copy {0} item(s) from "{1}" to "{2}" + Failed to copy {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Copying {0} item(s) to "{1}" + Copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Copying {0} item(s) from "{1}" to "{2}" + Copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. + + Discovered {0, plural, one {# item} other {# items}} + Shown in a StatusCenter card during file discovery phase. + Canceled extracting "{0}" to "{1}" Shown in a StatusCenter card. @@ -3473,55 +3682,55 @@ Shown in a StatusCenter card. - Canceled deleting {0} item(s) from "{1}" + Canceled deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Deleted {0} item(s) from "{1}" + Deleted {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Error deleting {0} item(s) from "{1}" + Error deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Failed to delete {0} item(s) from "{1}" + Failed to delete {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Deleting {0} item(s) from "{1}" + Deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Canceled moving {0} item(s) to "{1}" + Canceled moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Canceled moving {0} item(s) from "{1}" to "{2}" + Canceled moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Moved {0} item(s) to "{1}" + Moved {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Moved {0} item(s) from "{1}" to "{2}" + Moved {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Moving {0} item(s) to "{1}" + Moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Moving {0} item(s) from "{1}" to "{2}" + Moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error moving {0} item(s) to "{1}" + Error moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to move {0} item(s) from "{1}" to "{2}" + Failed to move {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. @@ -3529,11 +3738,11 @@ Shown in a StatusCenter card. - Emptied Recycle Bin + மறுசுழற்சி கூடையை வெறுமை ஆக்கிட்டோம் Shown in a StatusCenter card. - Emptying Recycle Bin + மறுசுழற்சி கூடையை வெறுமை ஆக்கிறோம் Shown in a StatusCenter card. @@ -3549,7 +3758,7 @@ Shown in a StatusCenter card. - {0}/{1} item(s) processed + {0}/{1, plural, one {# item} other {# items}} processed Shown in a StatusCenter card. Used as "8/20 items processed" @@ -3567,20 +3776,23 @@ Failed to set the background wallpaper + + Failed to open the settings file + - Delete Git branch + Git கிளயை நீக்கு Are you sure you want to permanently delete "{0}" branch? - Connected to GitHub + GitHub-உடன் இணைக்கப்பட்டு Logout - Connect to GitHub + GitHub-உடன் இணை Login @@ -3592,7 +3804,7 @@ There was an error applying this tag - Extract items from selected archive(s) to current folder for single-item archive, or to new folder for multi-item archive + Extract selected {0, plural, one {archive} other {archives}} here for single-item or to new folder for multi-item Extract here (Smart) @@ -3601,7 +3813,7 @@ Sort files and folders together - Sort files and folders together + Sort files and folders in the same list Sort files first @@ -3634,10 +3846,10 @@ Scroll to previous folder when navigating up - Open new window + புது ஃவிண்டோ திற - Change album cover + ஐல்பக் கவர மாற்று Failed to rename item @@ -3646,30 +3858,27 @@ Editing "{0}" requires additional permissions - Edit permissions + அனுமதிகள திருத்து Files is still running in the background to improve startup performance. - Where did Files go? + Files எங்க போச்சு? - Questions & discussions - - - Additional sizes are not yet available for the Tiles View. + கேள்விகள் & உரையாடல்கள் Compact Used to describe layout sizes - சிறிய + Small Used to describe layout sizes - நடுநிலை + Medium Used to describe layout sizes @@ -3693,7 +3902,7 @@ Used to describe layout sizes - பெரிய + Large Used to describe layout sizes @@ -3722,16 +3931,16 @@ செயல்கள் - Commands + கட்டளய்ங்க் - Add command + கட்டளய சேரு Restore defaults - Choose an action + ஒரு செயல தேர்வு-செய்ங்க Are you sure you want to restore the default key bindings? This action cannot be undone. @@ -3756,7 +3965,7 @@ Image alignment type - மையம் + Center Image alignment type @@ -3770,14 +3979,14 @@ Image fit - Left + இடது Image alignment type Opacity - Right + வலது Image alignment type @@ -3800,17 +4009,17 @@ This is a type of backdrop for the application background - Show for all locations + எல்லா இடத்துக்கும் காட்டு Setting where users can choose to display "Open IDE" button for Git Repos - Developer tools + டிவெலப்பர் கருவிகள் Configure the "Open IDE" button on the status bar - Show for Git repos + Git repos'கு காட்டு Setting where users can choose to display "Open IDE" button for all locations. @@ -3818,22 +4027,30 @@ This is the friendly name for DLL files. - ICO File + ICO ஃபய்ல் This is the friendly name for ICO files. + + ICL File + This is the friendly name for ICL files. + Zip File This is the friendly name for ZIP files. - Bitmap Files + 'பிட்மைப் ஃபய்ல்ஸ் This is the friendly name for bitmap files. + + Image Files + This is the friendly name for image files. + Num - Network locations + நெட்ஃவஃர்க் இடங்கள் There are no network locations. If you don't use network locations, you can disable the widget. @@ -3842,35 +4059,35 @@ Disable - Edit in Notepad + நோட்பைடுல திருத்து - Edit the selected file in Notepad + Select'na file'y Notepad'la edit'u Dimensions: - Status: + நிலை: - Show Toolbar - Setting that controls if the Toolbar is shown in the main view + Show toolbar + Setting that controls if the toolbar is shown in the main view - Tab actions menu + டைபு செயல்கள் மெஞு - - Add vertical pane + + Split vertically - Add a vertical pane + Split pane vertically - - Add horizontal pane + + Split horizontally - - Add a horizontal pane + + Split pane horizontally Arrange vertically @@ -3884,8 +4101,8 @@ Arrange panes horizontally - - Add pane + + Split pane Show tab actions button in the title bar @@ -3893,8 +4110,11 @@ Arrange panes - - Default pane arrangement + + Default dual pane split direction + + + Dual pane mode Horizontal @@ -3902,4 +4122,259 @@ Vertical + + Show Files icon in the System Tray + + + CPU threads + + + ஹோம் பேஜ்கு போ + + + Toolbars + + + User ID + + + Bulk rename + + + Compress contents + + + Show option to create alternate data stream + + + Create alternate data stream + + + Create alternate data stream for the selected {0, plural, one {item} other {items}} + + + Enter data stream name + + + There was an error creating the alternate data stream + + + Please note that alternate data streams only work on drives formatted as NTFS. + + + Alternate data streams are currently hidden + + + Would you like to display alternate data streams? You can modify this setting anytime from the files and folders settings page. + + + Manage tags + + + Available + + + Total + + + Always switch focus to newly created tab + + + Toggle shelf pane + + + Toggle the visibility of the shelf pane + + + Show shelf pane toggle in address bar + + + Shelf + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + Clear items + + + Remove from shelf + + + Add to shelf + Tooltip that displays when dragging items to the Shelf Pane + + + Enter a hash to compare + Placeholder that appears in the compare hash text box + + + Matches {0} + Appears when two compared hashes match, e.g. "Matches SHA256" + + + No matches found + Appears when two compared hashes don't match + + + Path or alias + + + Invalid path + + + Test integration + + + {0} could not be located. Please check your settings and try again. + + + The configured IDE could not be located + + + Open settings + + + Visual Studio Code + + + Enter a path or launch alias + + + Please enter a name for the IDE + + + Clone repo + Clone repo dialog title + + + Clone + Primary action button in the clone repo dialog + + + Repository URL + URL textbox header in the clone repo dialog + + + Cannot clone repo + Cannot clone repo dialog title + + + Compare a file + Button that appears in file hash properties that allows the user to compare two files + + + Size format + + + Binary + + + Decimal + + + You can add sections to the sidebar by right-clicking and selecting the sections you want to add + + + Drag files or folders here to interact with them across different tabs + + + Enter a path to navigate to... + + + Find features and commands... + + + Search for files and folders... + + + During file operations + + + Show status center button + + + Status center progress ring + Screen reader name for the status center progress ring + + + Icon files + This is the friendly name for a variety of different icon files. + + + Show collapsed path breadcrumbs + + + Show folders in {0} + + + Show folders in Home + + + There are no commands containing {0} + + + See more + + + Filtering for + + + Filename + + + Signatures + + + Signature list + + + Issued by: + + + Issued to: + + + Valid from: + + + Valid to: + + + No signature was found. + + + Show option to open folders in Windows Terminal + + + Open log file + + + Open log file in your default editor + + + Open log file location in your default file manager + + + Unable to open the log file + + + Show status bar + + + Only in Columns View + + + New {0} + + + Enable smooth scrolling + + + Scrolling + + + Show option to Pin to Sidebar + + + Show option to Pin to the Start Menu + \ No newline at end of file diff --git a/src/Files.App/Strings/th-TH/Resources.resw b/src/Files.App/Strings/th-TH/Resources.resw index 37002165ef37..580317b53e3a 100644 --- a/src/Files.App/Strings/th-TH/Resources.resw +++ b/src/Files.App/Strings/th-TH/Resources.resw @@ -123,9 +123,15 @@ คัดลอกเส้นทาง + + Copy item path + คัดลอกเส้นทางพร้อมกับอัญประกาศ + + Copy selected item path with quotes + เรียกดู @@ -160,7 +166,7 @@ ข้าม - เลือกทั้งหมด + Select all สลับการเลือก @@ -178,7 +184,7 @@ ล้างประวัติรายการ - ป้อนเส้นทางเพื่อนำทาง หรือพิพม์ > เพื่อเปิดแผงคำสั่ง + ป้อนเส้นทางเพื่อนำทาง หรือพิมพ์ "ฬ" เพื่อเปิดแผงคำสั่ง ค้นหา @@ -211,7 +217,7 @@ แสดงไฟล์และโฟลเดอร์ที่ซ่อนไว้ - แสดงไฟล์ที่ใช้ชื่อจุด + Show dot files ตกแต่ง @@ -249,6 +255,9 @@ วาง + + Paste shortcut + สร้าง @@ -291,8 +300,8 @@ ระบุชื่อใหม่ของรายการ - - ตั้งชื่อ + + Create new {0} สว่าง @@ -300,6 +309,9 @@ มืด + + Use system setting + โปรแกรมประยุกต์ @@ -418,7 +430,7 @@ 1 second ago - {0} seconds ago + {0} วินาทีที่แล้ว ขณะนี้ @@ -515,9 +527,6 @@ Status - - ตามระบบ - ไม่มีรายการนี้ @@ -530,6 +539,9 @@ คัดลอกไปที่ {0} + + Clone to {0} + สร้างทางลัด @@ -998,6 +1010,9 @@ ผลการค้นหา {0} ใน {1} + + Search results for `{0}` + รายละเอียด @@ -1052,9 +1067,6 @@ รายละเอียด (Ctrl+Shift+1) - - เรียงต่อกัน (Ctrl+Shift+2) - วันที่ลบ @@ -1080,10 +1092,25 @@ Toggle the info pane - Toggle the info pane to view the detail/preview panes + Toggle visibility of the detail/preview panes - Toggle the Toolbar + Toggle toolbar + + + Toggle visibility of the toolbar + + + Toggle filter header + + + Toggle visibility of the filter header + + + Toggle dual pane + + + Toggle dual pane mode ไม่มีตัวอย่างให้ดู @@ -1238,6 +1265,12 @@ เปิดที่เก็บข้อมูลอัจฉรียะ + + Open the Storage Sense page in Windows Settings + + + Cleanup + คัดลอก @@ -1380,7 +1413,7 @@ {0} รายการ - เปิดแท็บที่ปิดขึ้นใหม่ + Reopen tab เปลี่ยนชื่อ @@ -1587,7 +1620,7 @@ แทนที่สิทธิผู้สืบทอดทั้งหมดด้วยการตั้งสิทธิ์สืบทอดจากสิ่งนี้ - Close active pane + Close pane เปิดใช้งานหน้าต่างย่อ @@ -1646,15 +1679,6 @@ หน้าแรก - - Ctrl+Shift+1 - - - Ctrl+Shift+2 - - - Ctrl+Shift+6 - แยกไฟล์เสร็จสมบูรณ์ @@ -1712,8 +1736,8 @@ คอลัมน์ - - ไทล์ + + Cards เปิดโฟลเดอร์ในแท็บใหม่ @@ -1749,7 +1773,7 @@ แท็ก - คลิกครั้งเดียวเพื่อเปิดรายการ + Open files with a single click เส้นทางโฟลเดอร์ @@ -1826,6 +1850,18 @@ ลงทะเบียนโปรแกรมนี้สำหรับการเริ่มต้นใหม่ + + Start window + + + Normal window + + + Minimized + + + Maximized + เรียกใช้ในฐานะผู้ดูแล @@ -1853,6 +1889,9 @@ ตัวเลือกนี้แก้ไขรีจิสทรีของระบบซึ่งอาจทำให้เกิดผลข้างเคียงขึ้นได้ ดำเนินการต่อด้วยความเสี่ยงของคุณเอง + + The flatten operations are permanent and not recommended. Continue at your own risk. + สร้างคลัง @@ -1931,6 +1970,9 @@ ปิดแท็บอื่น ๆ ทั้งหมด + + Close all tabs + เพลง @@ -1985,11 +2027,23 @@ ลักษณะการทำงาน - - แสดงคำวิจารณ์แอป + + Hello! + + + Enjoying Files? Please consider reviewing in the Microsoft Store. + + + Enjoying Files? Please consider supporting the project on GitHub. - - คุณต้องการให้คำวิจารณ์แอปนี้ไหม?? + + Sponsor + + + Rate us + + + Dismiss ตั้งเป็นพื้นหลัง @@ -2018,8 +2072,8 @@ หน้าจอล็อก - - Open folders with a single click in the Columns Layout + + Open folders with a single click การเปิดรายการ @@ -2027,6 +2081,21 @@ บีดอัด + + Move all contents from subfolders into the selected location + + + Flatten folder + + + Flatten + + + Flattening a folder will move all contents from its subfolders to the selected location. This operation is permanent and cannot be undone. By using this experimental feature, you acknowledge the risk and agree not to hold the Files team responsible for any data loss. + + + Show flatten options + Select files and folders when hovering over them @@ -2037,7 +2106,7 @@ คืนค่ารายการทั้งหมด - Do you want to restore the {0} selected item(s)? + Do you want to restore the {0, plural, one {selected item} other {{0} selected items}}? คืนค่าส่วนที่เลือก @@ -2060,6 +2129,12 @@ รหัสผ่านไฟล์จัดเก็บ + + Encoding + + + {0} (detected) + แยกไปที่ @@ -2102,8 +2177,23 @@ ขนาดแบ่งส่วน - - ไม่แยก + + Do not split + + + Auto + + + Dictionary size + + + Word size + + + Estimated memory usage: {0} + + + Available memory: {0} CD @@ -2159,8 +2249,14 @@ แก้ไขไฟล์ตั้งค่า - - มีอะไรใหม่ + + Open settings file in your default editor + + + Release Notes + + + Open Release Notes การสร้างทางลัดไปยังที่ตั้งนี้จำเป็นต้องใช้สิทธิ์ผู้ดูแลระบบ @@ -2241,25 +2337,22 @@ Context menu options - แสดงนามสกุลไฟล์ - - - แสดงรายการที่ซ่อน + File extensions - จัดรูปแบบ... + รูปแบบ ช่วยเหลือ - สลับแบบเต็มหน้าจอ + Full screen คุณแน่ใจแล้วใช่ไหมที่จะลบแท็กนี้? - เล่นทั้งหมด + Play ความสูง @@ -2319,7 +2412,7 @@ เพิ่มขนาด - Toggle sort direction + Sort direction ชื่อไม่ถูกต้อง @@ -2331,34 +2424,37 @@ วีดีโอ - Launch preview popup + Preview popup Toggle compact overlay - เปิดหน้าต่างช่วยเหลือในเบราว์เซอร์ + Open the online help page in your browser - สลับแบบเต็มหน้าจอ + Toggle full screen mode - เปิดใช้งานหน้าต่างย่อ + Enter compact overlay mode - ออกจากหน้าต่างย่อ + Exit compact overlay mode - Toggle compact overlay + Toggle compact overlay mode - ไปที่ช่องค้นหา + Start search in the OmniBar - Toggle whether to show hidden items + Toggle visibility of hidden items + + + Toggle visibility of dot files - Toggle whether to show file extensions + Toggle visibility of file extensions Toggle the preview pane to view file previews @@ -2367,52 +2463,64 @@ Toggle whether to show sidebar - คัดลอกรายการไปยังคลิปบอร์ด + Copy selected {0, plural, one {item} other {items}} - Copy path of selected items to the clipboard + Copy path of the current directory + + + Copy path of selected items + + + Copy path of selected items with quotes - Copy path of selected items with quotes to the clipboard + Copy path of the current directory with quotes - ตัดรายการไปยังคลิปบอร์ด + Cut selected {0, plural, one {item} other {items}} - วางรายการจากคลิปบอร์ดไปยังโฟลเดอร์ปัจจุบัน + Paste items to the current folder + + + Paste items to the current folder as shortcuts - วางรายการจากคลิปบอร์ดไปยังโฟลเดอร์ที่เลือก + Paste items to the selected folder + + + Paste to selected folder - กำลังลบรายการ + Delete selected {0, plural, one {item} other {items}} สร้างโฟลเดอร์ใหม่ - Create new shortcut(s) to selected item(s) + Create new {0, plural, one {shortcut} other {shortcuts}} to selected {0, plural, one {item} other {items}} Create new shortcut to any item - ล้างข้อมูลในถังรีไซเคิล + Empty the contents of Recycle Bin Open "Format Drive" menu for selected item - Restore selected item(s) from recycle bin + Restore selected {0, plural, one {item} other {items}} from recycle bin Restore all items from recycle bin - เปิดรายการ + Open {0, plural, one {item} other {items}} - เปิดรายการด้วยแอปที่เลือก + Open {0, plural, one {item} other {items}} with selected application Open parent folder of searched item @@ -2427,28 +2535,28 @@ เลือกรายการทั้งหมด - สลับการเลือกรายการ + Invert selected items - ล้างส่วนรายการที่เลือก + Clear selected items Toggle item selection - Share selected file(s) with others + Share selected {0, plural, one {file} other {files}} with others - Pin item(s) to the Start Menu + Pin {0, plural, one {item} other {items}} to the Start Menu - Unpin item(s) from the Start Menu + Unpin {0, plural, one {item} other {items}} from the Start Menu - ปักหมุดโฟลเดอร์ยังที่แถบด้านข้าง + Pin {0, plural, one {folder} other {folders}} to Sidebar - นำโฟลเดอร์ออกจากแถบด้านข้าง + Unpin {0, plural, one {folder} other {folders}} from Sidebar ตั้งค่าพื้นหลังเดสก์ท็อปจากรูปที่เลือก @@ -2462,14 +2570,23 @@ Set selected picture as the app background + + Install font + + + Install driver + + + Install certificate + - ติดตั้งแบบอักษรที่เลือก + Install selected {0, plural, one {font} other {fonts}} - ติดตั้งไดรเวอร์โดยใช้ไฟล์ INF + Install {0, plural, one {driver} other {drivers}} using selected inf {0, plural, one {file} other {files}} - ติดตั้งใบรับรองที่เลือก + Install selected {0, plural, one {certificate} other {certificates}} Run selected application as administrator @@ -2484,28 +2601,28 @@ Launch preview in popup window - Create archive with selected item(s) + Create archive with selected {0, plural, one {item} other {items}} - Create 7z archive instantly with selected item(s) + Create 7z archive with selected {0, plural, one {item} other {items}} - Create zip archive instantly with selected item(s) + Create zip archive with selected {0, plural, one {item} other {items}} - Extract items from selected archive(s) to any folder + Extract selected {0, plural, one {archive} other {archives}} to any folder - Extract items from selected archive(s) to current folder + Extract selected {0, plural, one {archive} other {archives}} to the current folder - Extract items from selected archive(s) to new folder + Extract selected {0, plural, one {archive} other {archives}} to new folder - หมุนรูปที่เลือกไว้ไปทางซ้าย + Rotate selected {0, plural, one {image} other {images}} to the left - หมุนรูปที่เลือกไว้ไปทางขวา + Rotate selected {0, plural, one {image} other {images}} to the right เปิดแผงตั้งค่า @@ -2525,8 +2642,8 @@ สลับเป็นเค้าโครงละเอียด - - สลับเป็นเค้าโครงไทล์ + + Switch to cards view สลับเป็นเค้าโครงรายการ @@ -2628,10 +2745,10 @@ เปิดแท็บใหม่ - Navigate backward in navigation history + Navigate backward - Navigate forward in navigation history + Navigate forward Navigate up one directory @@ -2660,8 +2777,11 @@ Close tabs other than selected tab + + Close all tabs including the current tab + - Reopen last closed tab + Reopen recently closed tab Move to the previous tab @@ -2673,13 +2793,13 @@ ปิดแท็บปัจจุบัน - Close active pane + Close the active pane Focus other pane - Switch focus to the non active pane + Switch focus to the other pane Toggle the sidebar @@ -2887,13 +3007,13 @@ ไม่​ได้​แท็ก - Moves to the previous tab + Previous tab - Moves to the next tab + Next tab - Closes current tab + Close tab Edit path @@ -2923,7 +3043,7 @@ แสดงช่องเครื่องหมายเมื่อเลือกรายการ - Focus path bar + Edit path in the OmniBar สร้างรายการใหม่ @@ -2938,7 +3058,7 @@ ลบอย่างถาวร - ลบรายการอย่างถาวร + Delete selected {0, plural, one {item} other {items}} permanently เล่นไฟล์สื่อที่เลือก @@ -3027,6 +3147,15 @@ You have uncommitted changes on this branch. What would you like to do with them? + + You have an ongoing merge with unresolved conflicts. Please resolve the conflicts or abort the merge to switch branches. + + + Abort merge and switch to '{0}' + + + Stay on '{0}' and resolve conflicts + Switch branch @@ -3055,14 +3184,20 @@ Switch to new branch - Create a folder with the currently selected item(s) + Create a folder with the currently selected {0, plural, one {item} other {items}} คุณสมบัติ + + Open File Explorer properties + เปิดหน้าต่างคุณสมบัติ + + Open File Explorer properties window + Locals @@ -3081,6 +3216,9 @@ Run git fetch + + Clone a git repo + Run git pull @@ -3125,7 +3263,7 @@ Mica Alt - รูปแบบพื้นหลัง + Backdrop Solid @@ -3157,17 +3295,17 @@ Files cannot access GitHub right now. - - เปิดโฟลเดอร์ใน VS Code + + Open folder in {0} - - เปิดไดเร็กทอรีปัจจุบันใน Visual Studio Code + + Open the current directory in {0} - - เปิดคลังจัดเก็บใน VS Code + + Open repo in {0} - - Open the root of the Git repo in Visual Studio Code + + Open the root of the Git repo in {0} คัดลอกรหัส @@ -3194,7 +3332,7 @@ Initialize repo - Initialize a Git repository + Initialize current folder as a git repository Remote Repository Name @@ -3209,13 +3347,13 @@ Sort items by path - เปิดโฟลเดอร์ในบานหน้าต่างใหม่ + Open selected directory in a new pane - เปิดไดเรกทอรีในแท็บใหม่ + Open selected directory in a new tab - เปิดไดเรกทอรีในหน้าต่างใหม่ + Open selected directory in a new window Open all @@ -3240,10 +3378,10 @@ The '{0}' command is not ready to be executed. - Command palette + Command Palette - Open command palette + Open Command Palette in the OmniBar Start in: @@ -3373,6 +3511,9 @@ กำลังประมวลผลรายการ + + Discovering items... + ความเร็ว: @@ -3385,61 +3526,129 @@ Shown in a StatusCenter card. - Compressed {0} item(s) to "{1}" + Compressed {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Compressed {0} item(s) from "{1}" to "{2}" + Compressed {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error compressing {0} item(s) to "{1}" + Error compressing {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to compress {0} item(s) from "{1}" to "{2}" + Failed to compress {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Compressing {0} item(s) to "{1}" + Compressing {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Compressing {0} item(s) from "{1}" to "{2}" + Compressing {0, plural, one {# item} other {# items}} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Canceled cloning {0} to "{1}" + Shown in a StatusCenter card. + + + Canceled cloning {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Cloned "{0}" to "{1}" + Shown in a StatusCenter card. + + + Cloned {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Error cloning "{0}" to "{1}" + Shown in a StatusCenter card. + + + Failed to clone {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Cloning "{0}" to "{1}" + Shown in a StatusCenter card. + + + Cloning {0} from "{1}" to "{2}" + Shown in a StatusCenter card. + + + Canceled installing {0} fonts + Shown in a StatusCenter card. + + + Canceled installing {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installed {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Error installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Failed to install {0, plural, one {# font} other {# fonts}} from "{1}" + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} + Shown in a StatusCenter card. + + + Installing {0, plural, one {# font} other {# fonts}} from "{1}" Shown in a StatusCenter card. - Canceled copying {0} item(s) to "{1}" + Canceled copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Canceled copying {0} item(s) from "{1}" to "{2}" + Canceled copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Copied {0} item(s) to "{1}" + Copied {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Copied {0} item(s) from "{1}" to "{2}" + Copied {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error copying {0} item(s) to "{1}" + Error copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to copy {0} item(s) from "{1}" to "{2}" + Failed to copy {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Copying {0} item(s) to "{1}" + Copying {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Copying {0} item(s) from "{1}" to "{2}" + Copying {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. + + Discovered {0, plural, one {# item} other {# items}} + Shown in a StatusCenter card during file discovery phase. + Canceled extracting "{0}" to "{1}" Shown in a StatusCenter card. @@ -3473,55 +3682,55 @@ Shown in a StatusCenter card. - Canceled deleting {0} item(s) from "{1}" + Canceled deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Deleted {0} item(s) from "{1}" + Deleted {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Error deleting {0} item(s) from "{1}" + Error deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Failed to delete {0} item(s) from "{1}" + Failed to delete {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Deleting {0} item(s) from "{1}" + Deleting {0, plural, one {# item} other {# items}} from "{1}" Shown in a StatusCenter card. - Canceled moving {0} item(s) to "{1}" + Canceled moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Canceled moving {0} item(s) from "{1}" to "{2}" + Canceled moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Moved {0} item(s) to "{1}" + Moved {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Moved {0} item(s) from "{1}" to "{2}" + Moved {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Moving {0} item(s) to "{1}" + Moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Moving {0} item(s) from "{1}" to "{2}" + Moving {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. - Error moving {0} item(s) to "{1}" + Error moving {0, plural, one {# item} other {# items}} to "{1}" Shown in a StatusCenter card. - Failed to move {0} item(s) from "{1}" to "{2}" + Failed to move {0, plural, one {# item} other {# items}} from "{1}" to "{2}" Shown in a StatusCenter card. @@ -3549,7 +3758,7 @@ Shown in a StatusCenter card. - {0}/{1} รายการได้ถูกประมวลผลแล้ว + {0}/{1, plural, one {# item} other {# items}} processed Shown in a StatusCenter card. Used as "8/20 items processed" @@ -3567,6 +3776,9 @@ Failed to set the background wallpaper + + Failed to open the settings file + Delete Git branch @@ -3592,7 +3804,7 @@ There was an error applying this tag - Extract items from selected archive(s) to current folder for single-item archive, or to new folder for multi-item archive + Extract selected {0, plural, one {archive} other {archives}} here for single-item or to new folder for multi-item แยกไฟล์ที่นี้ (อัจฉริยะ) @@ -3601,7 +3813,7 @@ เรียงไฟล์และโฟลเดอร์เข้าด้วยกัน - เรียงไฟล์และโฟลเดอร์เข้าด้วยกัน + Sort files and folders in the same list เรียงไฟล์ก่อน @@ -3657,9 +3869,6 @@ ตั้งคำถามสนทนา - - Additional sizes are not yet available for the Tiles View. - กะทัดรัด Used to describe layout sizes @@ -3821,6 +4030,10 @@ ICO File This is the friendly name for ICO files. + + ICL File + This is the friendly name for ICL files. + Zip File This is the friendly name for ZIP files. @@ -3829,6 +4042,10 @@ Bitmap Files This is the friendly name for bitmap files. + + Image Files + This is the friendly name for image files. + Num @@ -3854,23 +4071,23 @@ Status: - Show Toolbar - Setting that controls if the Toolbar is shown in the main view + Show toolbar + Setting that controls if the toolbar is shown in the main view Tab actions menu - - Add vertical pane + + Split vertically - Add a vertical pane + Split pane vertically - - Add horizontal pane + + Split horizontally - - Add a horizontal pane + + Split pane horizontally Arrange vertically @@ -3884,8 +4101,8 @@ Arrange panes horizontally - - Add pane + + แบ่งหน้าต่าง Show tab actions button in the title bar @@ -3893,8 +4110,11 @@ Arrange panes - - Default pane arrangement + + Default dual pane split direction + + + Dual pane mode Horizontal @@ -3902,4 +4122,259 @@ Vertical + + Show Files icon in the System Tray + + + CPU threads + + + Navigate to the home page + + + Toolbars + + + User ID + + + Bulk rename + + + Compress contents + + + Show option to create alternate data stream + + + Create alternate data stream + + + Create alternate data stream for the selected {0, plural, one {item} other {items}} + + + Enter data stream name + + + There was an error creating the alternate data stream + + + Please note that alternate data streams only work on drives formatted as NTFS. + + + Alternate data streams are currently hidden + + + Would you like to display alternate data streams? You can modify this setting anytime from the files and folders settings page. + + + Manage tags + + + Available + + + Total + + + Always switch focus to newly created tab + + + Toggle shelf pane + + + Toggle the visibility of the shelf pane + + + Show shelf pane toggle in address bar + + + Shelf + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + Clear items + + + Remove from shelf + + + Add to shelf + Tooltip that displays when dragging items to the Shelf Pane + + + Enter a hash to compare + Placeholder that appears in the compare hash text box + + + Matches {0} + Appears when two compared hashes match, e.g. "Matches SHA256" + + + No matches found + Appears when two compared hashes don't match + + + Path or alias + + + Invalid path + + + Test integration + + + {0} could not be located. Please check your settings and try again. + + + The configured IDE could not be located + + + Open settings + + + Visual Studio Code + + + Enter a path or launch alias + + + Please enter a name for the IDE + + + Clone repo + Clone repo dialog title + + + Clone + Primary action button in the clone repo dialog + + + Repository URL + URL textbox header in the clone repo dialog + + + Cannot clone repo + Cannot clone repo dialog title + + + Compare a file + Button that appears in file hash properties that allows the user to compare two files + + + Size format + + + Binary + + + Decimal + + + You can add sections to the sidebar by right-clicking and selecting the sections you want to add + + + Drag files or folders here to interact with them across different tabs + + + Enter a path to navigate to... + + + Find features and commands... + + + Search for files and folders... + + + During file operations + + + Show status center button + + + Status center progress ring + Screen reader name for the status center progress ring + + + Icon files + This is the friendly name for a variety of different icon files. + + + Show collapsed path breadcrumbs + + + Show folders in {0} + + + Show folders in Home + + + There are no commands containing {0} + + + See more + + + Filtering for + + + Filename + + + Signatures + + + Signature list + + + Issued by: + + + Issued to: + + + Valid from: + + + Valid to: + + + No signature was found. + + + Show option to open folders in Windows Terminal + + + Open log file + + + Open log file in your default editor + + + Open log file location in your default file manager + + + Unable to open the log file + + + Show status bar + + + Only in Columns View + + + New {0} + + + Enable smooth scrolling + + + Scrolling + + + Show option to Pin to Sidebar + + + Show option to Pin to the Start Menu + \ No newline at end of file diff --git a/src/Files.App/Strings/tr-TR/Resources.resw b/src/Files.App/Strings/tr-TR/Resources.resw index 8f366e1c41a8..23c6861414ef 100644 --- a/src/Files.App/Strings/tr-TR/Resources.resw +++ b/src/Files.App/Strings/tr-TR/Resources.resw @@ -123,11 +123,17 @@ Yolu kopyala + + Öğe yolunu kopyala + Tırnak işaretleriyle yolu kopyala + + Seçilen konum tırnak işaretleri ile kopyala + - Göz at + Gözat Boyut @@ -145,7 +151,7 @@ Diskteki boyutu: - Sıkıştırılmamış Boyut: + Sıkıştırılmamış boyut: Bu işlem yapılamaz @@ -160,7 +166,7 @@ Atla - Tümünü Seç + Tümünü seç Seçimi tersine çevir @@ -190,7 +196,7 @@ Bu ögeyi kaldır - GitHub Reposu + GitHub deposu Hakkında @@ -217,7 +223,7 @@ Görünüm - Arka plan rengi + Arkaplan rengi Gelişmiş @@ -249,6 +255,9 @@ Yapıştır + + Kısayol yapıştır + Yeni @@ -274,7 +283,7 @@ Sil - Hızlı Erişime Sabitle + Hızlı erişime sabitle Files'a hoş geldiniz! @@ -291,8 +300,8 @@ Bir öge adı yazın - - İsim ayarla + + Yeni {0} oluştur Açık @@ -300,6 +309,9 @@ Koyu + + Sistem ayarını kullan + Uygulama @@ -328,7 +340,7 @@ Yenile - Bu dizinde aynı adlı bir öge zaten var. + Bu klasörde aynı adlı bir öge zaten var. Var olan ögeyi değiştir @@ -379,7 +391,7 @@ Dosya kullanımda - Sayfa Düzeni + Sayfa düzeni Salt okunur @@ -406,7 +418,7 @@ {0, plural, one {# dakika önce} other {# dakika önce}} - 1 dakika önce güncellendi + 1 dakika önce {0} dakika önce @@ -515,9 +527,6 @@ Durum - - Windows varsayılanı - Sonuç yok @@ -530,6 +539,9 @@ {0} içine kopyala + + {0} içine kopyala + Kısayol oluştur @@ -618,7 +630,7 @@ Bu ayarların uygulanabilmesi için uygulamanın yeniden başlatılması gerekiyor, uygulamayı yeniden başlatmak ister misiniz? - GitHub üzerinden sponsor olun + Bizi GitHub'ta destekleyin Bu öge oluşturulamadı @@ -651,7 +663,7 @@ Boş bir klasör oluşturur - Dizini yenile + Klasörü yenile Dil @@ -998,6 +1010,9 @@ {0} için {1} içindeki arama sonuçları + + '{0}' için arama sonuçları + Ayrıntılar @@ -1008,7 +1023,7 @@ Korunan sistem dosyalarını göster - Dizinler arası düzen ve sıralama tercihlerini senkronla + Dizinler arası düzen ve sıralama tercihlerini eşitle Sağ tık menüsünü özelleştir @@ -1047,14 +1062,11 @@ Yeni bölmede aç - Dosyalar ve klasörler + Dosyalar & klasörler Ayrıntılar Görünümü (Ctrl+Shift+1) - - Döşeme Görünümü (Ctrl+Shift+2) - Silinme tarihi @@ -1068,22 +1080,37 @@ İstenilen ad - Önizleme bölmesini aç/kapat + Önizleme bölmesini değiştir - Önizleme bölmesini aç/kapat + Ayrıntılar bölmesini değiştir Temel dosya özelliklerini görüntülemek için ayrıntılar bölmesini açın - Bilgi bölmesini aç/kapat + Bilgi bölmesini değiştir - Ayrıntı/önizleme bölmelerini görüntülemek için bilgi bölmesini açın + Ayrıntı/önizleme panellerinin görünürlüğünü değiştir - Kenar çubuğunu ayarla + Araç çubuğunu değiştir + + + Araç çubuğu görünürlüğünü değiştir + + + Filtre başlığını değiştir + + + Filtre başlığının görünürlüğünü değiştir + + + Çift bölmeyi değiştir + + + Çift bölme modunu değiştir Ön izleme yok @@ -1092,7 +1119,7 @@ Öge Adı - Kenar Çubuğundan Sabitlemeyi Kaldır + Kenar çubuğundan kaldır @@ -1104,7 +1131,7 @@ Dosya ön izlemesi - Seçilmiş dosya ön izleme panosu + Seçili dosya önizleme bölmesi Geri bildirim @@ -1113,7 +1140,7 @@ Çevrimiçiyken Kullanılabilir - Dökümantasyon + Belgeler Çevrimdışı kullanılabilir @@ -1206,10 +1233,10 @@ Sütun Görünümü (Ctrl+Shift+6) - Başlat Menüsüne Sabitle + Başlat menüsüne sabitle - Başlat Menüsünden Sabitlemeyi Kaldır + Başlat menüsünden sabitlemeyi kaldır Kütüphane @@ -1238,6 +1265,12 @@ Akıllı Depolama'yı aç + + Windows Ayarlarından Akıllı Depolama sayfasını açın + + + Temizle + Kopyala @@ -1380,7 +1413,7 @@ {0} öğe - Kapatılan sekmeyi yeniden aç + Sekmeyi yeniden aç Yeniden adlandır @@ -1587,7 +1620,7 @@ Tüm alt nesne izin girişlerini bu nesneden devralınabilir izin girişleriyle değiştir - Geçerli sekmeyi kapat + Bölmeyi kapat Kompakt yer paylaşımına gir @@ -1644,16 +1677,7 @@ Güncelle - Ev - - - Ctrl+Shift+1 - - - Ctrl+Shift+2 - - - Ctrl+Shift+6 + Ana sayfa Arşiv çıkarma işlemi başarıyla tamamlandı. @@ -1712,8 +1736,8 @@ Sütunlar - - Sıralar + + Kartlar Klasörleri yeni sekmede aç @@ -1722,10 +1746,10 @@ Durum Merkezi - Sütun oluşturulma tarihi + Oluşturulma tarihi sütunu - Öğe Sütun Boyutu + Öğe boyutu sütunu Deneysel özellik işaretleri @@ -1749,19 +1773,19 @@ Dosya etiketi - Öğeleri tek bir tıklamayla açın + Dosyaları tek tıklamayla açın Klasör yolu - Ayarları Dışa Aktar + Ayarları dışa aktar Ayarları içe aktar - Ayarları içe aktarılamadı. Ayarlar dosyası bozuk. + Ayarları içeri aktarılamadı. Ayarlar dosyası bozuk. Ayarları içe aktarırken hata oluştu @@ -1785,7 +1809,7 @@ Dosyaları başka bir uygulamadan açarken sekmeyi mevcut örnekte aç - Başlatma seçenekleri + Başlangıç ​​ayarları Uyumluluk @@ -1826,6 +1850,18 @@ Yeniden başlatmak için bu programı kaydet + + Başlangıç Penceresi + + + Normal pencere + + + Küçültülmüş + + + Tam ekran + Yönetici olarak çalıştır @@ -1853,6 +1889,9 @@ Bu seçenek sistem kayıt defterini değiştirir ve aygıtınızda beklenmeyen yan etkilere neden olabilir. Kendi sorumluluğunuzda devam edin. + + Düzleştirme işlemleri kalıcıdır ve önerilmez. Kendi sorumluluğunuzda devam edin. + Kütüphane oluştur @@ -1863,7 +1902,7 @@ Klasör boyutlarını hesapla - Son Dosyalar + Son dosyalar Windows başlatıldığında Files'ı aç @@ -1931,6 +1970,9 @@ Diğer Sekmeleri Kapat + + Tüm sekmeleri kapat + Müzik @@ -1971,7 +2013,7 @@ Tüm Sütunları Sığacak Şekilde Boyutlandır - Uyarlanır düzen + Uyarlanabilir düzen Uyarlanabilir (Ctrl+Shift+7) @@ -1985,11 +2027,23 @@ Davranışlar - - Files'ı Değerlendir + + Merhaba! + + + Files hoşunuza mı gitti? Lütfen Microsoft Store'da inceleme yazmayı düşünün. + + + Files'ı beğendiniz mi? Lütfen GitHub'da projeyi desteklemeyi düşünün. - - Files'ı değerlendirmek ister misiniz? + + Destekle + + + Bizi değerlendirin + + + Reddet Arka plan olarak ayarla @@ -2004,7 +2058,7 @@ Tarih sütunu - Sütun oluşturulma tarihi + Oluşturulma tarihi sütunu Boyut sütunu @@ -2013,13 +2067,13 @@ Etiket sütunu - Tip sütunu + Tür sütunu Kilit Ekranı - - Sütunlar Düzeninde tek bir tıklama ile klasörleri aç + + Klasörleri tek tıklamayla açın Açılış öğeleri @@ -2027,23 +2081,38 @@ Sıkıştır + + Alt klasörlerdeki içerikleri seçilen konuma taşı + + + Klasörü düzleştir + + + Düzleştir + + + Bir klasörü düzleştirmek içerikleri alt klasörlerinden seçilen konuma taşıyacaktır. Bu işlem kalıcıdır ve geri alınamaz. Bu deneysel özelliği kullanarak, veri kaybı riskini ve herhangi bir veri kaybında Files ekibini sorumlu tutmamayı kabul etmelisiniz. + + + Düzleştirme seçeneklerini göster + - Üzerine getirildiğinde dosyaları ve dizinleri seç + Üzerine getirildiğinde dosyaları ve klasörleri seç Geri dönüşüm kutusundaki tüm öğeleri geri yüklemek istediğinizden emin misiniz? - Bütün öğeleri geri yükle + Tüm ögeleri geri yükle - Seçilen {0} adet ögeyi geri yüklemek istiyor musunuz? + {0, plural,one {Seçili öğeyi}other {Seçili {0} öğeyi}} geri yüklemek istiyor musunuz? Seçimi geri yükle - Bütün öğeleri geri yükle + Tüm Öğeleri Geri Yükle Geri yükle @@ -2060,6 +2129,12 @@ Arşiv şifresi + + Kodlama + + + {0} (algılandı) + Yol @@ -2102,9 +2177,24 @@ Bölme boyutu - + Bölme yapma + + Auto + + + Dictionary size + + + Word size + + + Estimated memory usage: {0} + + + Available memory: {0} + CD @@ -2157,10 +2247,16 @@ Son kullanılan dosyalar şu an için Windows Dosya Gezgininde devre dışıdır. - Ayar Dosyasını Düzenle + Ayar dosyasını düzenle - - Yenilikler + + Ayarlar dosyasını varsayılan düzenleyici içinde aç + + + Sürüm Notları + + + Sürüm Notlarını aç Bu konumda bir kısayol oluşturmak, yönetici ayrıcalıkları gerektirir @@ -2241,25 +2337,22 @@ Bağlam menüsü seçenekleri - Dosya eklentilerini göster - - - Gizli dosyaları göster + Dosya uzantıları - Biçimlendir... + Biçim Yardım - Tam ekranı aç/kapat + Tam ekran Bu etiketi silmek istediğinize emin misiniz? - Hepsini oynat + Oynat Boy @@ -2319,7 +2412,7 @@ Boyutu büyüt - Sıralama yönünü değiştir + Sıralama yönü Geçersiz isim @@ -2331,34 +2424,37 @@ Videolar - Önizleme açılır penceresini başlat + Önizleme açılır penceresi Kompakt üstyazımı aç / kapa - Çevrimiçi yardım sayfasını tarayıcıda aç + Tarayıcınızda çevrimiçi yardım sayfasını açın - Tam ekranı aç/kapat + Tam ekran modunu değiştir - Kompakt yer paylaşımına gir + Kompakt yer paylaşımı moduna gir - Kompakt yer paylaşımından çık + Kompakt yer paylaşımı modundan çık - Kompakt yer paylaşımını değiştir + Kompakt katman modunu değiştir - Arama kutusuna git + OmniBar üstünde arama başlat - Gizli öğelerin gösterilip gösterilmeyeceğini değiştirin + Gizli öğelerin görünürlüğünü değiştir + + + Nokta ile başlayan dosyaların görünürlüğünü değiştir - Dosya uzantılarının gösterilip gösterilmeyeceğini değiştirin + Dosya uzantılarının görünürlüğünü değiştir Dosya önizlemelerini görüntülemek için önizleme bölmesini açın @@ -2367,52 +2463,64 @@ Yan bölme görünürlüğünü aç / kapa - Öge(ler)i panoya kopyala + Seçili {0, plural, one {öğeyi} other {öğeleri}} kopyala - Seçilen öğelerin yolunu panoya kopyala + Geçerli dizin yolunu kopyala + + + Seçili öğelerin yolunu kopyala + + + Seçili öğelerin yolunu tırnak işaretleriyle kopyala - Seçilen öğelerin yolunu tırnak işaretleri ile panoya kopyala + Geçerli dizinin yolunu tırnak işaretleriyle kopyala - Öge(ler)i panoya kes + Seçili {0, plural, one {öğeyi} other {öğeleri}} kes - Panodaki öge(ler)i mevcut klasöre yapıştır + Öğeleri mevcut klasöre yapıştır + + + Öğeleri mevcut klasöre kısayol olarak yapıştır - Panodaki öge(ler)i seçilen klasöre yapıştır + Öğeleri seçilen klasöre yapıştır + + + Seçili klasöre yapıştır - Ögeleri sil + Seçili {0, plural, one {öğeyi} other {öğeleri}} sil Yeni klasör oluştur - Seçili öge(ler)e yeni kısayol(lar) oluştur + Seçili {0, plural, one {öğe} other {öğeler}} için yeni {0, plural, one {kısayol} other {kısayollar}} oluştur Herhangi bir ögeye yeni kısayol oluştur - Geri dönüşüm kutusunu boşalt + Çöp kutusu içeriğini boşalt Seçili öge için "Sürücüyü Biçimlendir" menüsünü aç - Seçili öge(ler)i Geri Dönüşüm Kutusu'ndan geri yükle + Seçili {0, plural, one {öğeyi} other {öğeleri}} geri dönüşüm kutusundan geri yükle Tüm öğeleri geri dönüşüm kutusundan geri yükle - Öge(ler)i aç + {0, plural, one {Öğeyi} other {Öğeleri}} aç - Öge(ler)i seçilen uygulamayla aç + Seçilen uygulama ile {0, plural,one {öğeyi}other {öğeleri}} aç Aranan ögenin ait olduğu klasörü aç @@ -2427,28 +2535,28 @@ Tüm ögeleri seç - Öge seçimini ters çevir + Seçili öğeleri ters çevir - Öge seçimini temizle + Seçilen öğeleri temizle Öge seçimini aç / kapa - Seçili dosya(lar)ı başkalarıyla paylaş + Seçilen {0, plural,one {dosyayı}other {dosyaları}} başkalarıyla paylaş - Öge(ler)i Başlat Menüsü'ne sabitle + {0, plural,one {Öğeyi}other {Öğeleri}} Başlat Menüsüne sabitle - Öge(ler)i Başlat Menüsü'nden kaldır + {0, plural,one {Öğenin}other {Öğelerin}} Başlat Menüsündeki sabitlemesini kaldır - Klasör(ler)i Kenar Çubuğuna Sabitle + {0, plural,one {Klasörü}other {Klasörleri}} kenar çubuğuna sabitle - Klasör(ler)i Kenar Çubuğundan Kaldır + {0, plural,one {Klasörün}other {Klasörlerin}} kenar çubuğundan sabitlemesini kaldır Seçilen resmi masaüstü arka planı olarak ayarla @@ -2462,14 +2570,23 @@ Seçili resmi uygulama arka planı olarak ayarla + + Yazı tipi kur + + + Sürücü kur + + + Sertifika kur + - Seçili yazı tip(ler)ini yükle + Seçilen {0, plural, one {yazı tipini} other {yazı tiplerini}} yükle - Seçilen inf dosya(ları) kullanarak sürücü(ler) kurun + Seçilen inf {0, plural,one {dosyasıyla}other {dosyalarıyla}} {0, plural,one {sürücü}other {sürücüleri}} yükle - Seçilen sertifika(ları) yükleyin + Seçilen {0, plural, one {sertifikayı} other {sertifikaları}} yükle Seçili uygulamayı yönetici olarak çalıştır @@ -2484,28 +2601,28 @@ Önizlemeyi açılır pencerede göster - Seçilen öge(ler)le arşiv oluştur + Seçilen {0, plural,one {öğeyle}other {öğelerle}} arşiv oluştur - Seçilen öge(ler)le anında 7z arşivi oluştur + Seçilen {0, plural,one {öğeyle}other {öğelerle}} 7z arşivi oluştur - Seçilen öge(ler)le anında zip arşivi oluştur + Seçilen {0, plural,one {öğeyle}other {öğelerle}} zip arşivi oluştur - Seçili arşiv(ler)deki öğeleri herhangi bir klasöre çıkart + Herhangi bir klasöre seçilen {0, plural,one {arşivi}other {arşivleri}} çıkar - Seçili arşiv(ler)deki öğeleri geçerli klasöre çıkart + Mevcut klasöre seçilen {0, plural,one {arşivi}other {arşivleri}} çıkar - Seçili arşiv(ler)deki öğeleri yeni klasöre çıkart + Yeni klasöre seçilen {0, plural,one {arşivi}other {arşivleri}} çıkar - Seçili resim(leri) sola döndür + Seçilen {0, plural,one {resmi}other {resimleri}} sola döndür - Seçili resim(leri) sağa döndür + Seçilen {0, plural,one {resmi}other {resimleri}} sağa döndür Ayarlar sayfasını aç @@ -2525,8 +2642,8 @@ Detaylar görünümüne geç - - Döşeme görünümüne geç + + Kart görünümüne geç Liste görünümüne geç @@ -2625,13 +2742,13 @@ Grup sıralama yönünü değiştir - Yeni sekme aç + Yeni sekmede aç - Navigasyon geçmişinde geri git + Geriye git - Navigasyon geçmişinde ileri git + İleriye git Bir dizinde yukarı git @@ -2660,6 +2777,9 @@ Seçilen sekme dışındaki sekmeleri kapat + + Şu anki sekme dahil olmak üzere tüm sekmeleri kapat + Son kapatılan sekmeyi yeniden aç @@ -2673,13 +2793,13 @@ Geçerli sekmeyi kapat - Geçerli sekmeyi kapat + Etkin bölmeyi kapat - Focus other pane + Diğer bölmeye odaklan - Switch focus to the non active pane + Odaklanmayı diğer bölmeye geçir Kenar çubuğunu değiştir @@ -2887,13 +3007,13 @@ Etiketlenmemiş - Önceki sekmeye gider + Önceki sekme - Sonraki sekmeye gider + Sonraki sekme - Geçerli sekmeyi kapatır + Sekmeyi kapat Yolu düzenle @@ -2902,7 +3022,7 @@ Tekrarla - Geri Al + Geri al Geçersiz konum @@ -2923,7 +3043,7 @@ Öğeleri seçerken onay kutularını göster - Yol çubuğuna odaklan + OmniBar üstünde yolu düzenle Yeni öğe oluştur @@ -2938,7 +3058,7 @@ Kalıcı olarak sil - Öğeleri kalıcı olarak sil + Seçili {0, plural, one {öğeyi} other {öğeleri}} kalıcı olarak sil Seçilen medya dosyalarını oynatın @@ -3027,6 +3147,15 @@ Bu dalda kaydedilmemiş değişiklikleriniz var. Onlarla ne yapmak istersin? + + Devam eden ve çözülmemiş çakışmaları olan bir birleştirme işleminiz var. Lütfen çakışmaları çözün veya dallar arasında geçiş yapmak için birleştirme işlemini iptal edin. + + + Birleştirmeyi iptal et ve '{0}' ye geçiş yap + + + '{0}' de devam et ve çatışmaları çöz + Branch değiştir @@ -3055,14 +3184,20 @@ Yeni branch'a geç - Seçili öğe(ler) ile bir klasör oluşturun + Seçilen {0, plural,one {öğeyle}other {öğelerle}} bir klasör oluştur - Özellikleri aç + Özellikler + + + Dosya Gezgini özelliklerini aç Özellikler penceresini aç + + Dosya Gezgini özellikleri penceresi + Yerel @@ -3081,6 +3216,9 @@ Git fetch'i çalıştır + + Git deposu klonla + Git pull'u çalıştır @@ -3125,7 +3263,7 @@ Mika Alt - Zemin Malzemesi + Arka plan materyali Katı @@ -3149,7 +3287,7 @@ {0} giden / {1} gelen commit - GitHub'a Bağlan + GitHub'a bağlan GitHub repolarıyla çalışmaya başlamak için aşağıdaki bağlantıya aşağıdaki kodu girin. @@ -3157,17 +3295,17 @@ Files, GitHub'a şu anda erişemiyor. - - Klasörü VS Code da aç + + Klasörü {0} içinde aç - - Mevcut dizini Visual Studio Code'da aç + + Geçerli dizini {0} konumunda aç - - Repoyu VS Code da aç + + Depoyu {0} içinde aç - - Git reposunun ana dizinini Visual Studio Code da aç + + Git deposunun kökünü {0} içinde açın Kodu kopyala @@ -3194,7 +3332,7 @@ Repo başlat - Bir Git deposu başlat + Mevcut klasörü bir git deposu olarak başlat Uzak Depo Adı @@ -3209,13 +3347,13 @@ Öğeleri yola göre sırala - Dizini yeni bölmede aç + Seçili dizini yeni bir bölme üstünde aç - Dizini yeni sekmede aç + Seçili dizini yeni bir sekmede aç - Dizini yeni pencerede aç + Seçili dizini yeni bir pencerede aç Tümünü aç @@ -3240,10 +3378,10 @@ '{0}' komutu, çalıştırılmak için hazır değil. - Komut paleti + Komut Paleti - Komut paletini aç + Komut Paletini OmniBar’da Aç Başlama yeri: @@ -3270,7 +3408,7 @@ Windows ile WinAppSdk arasındaki bir kısıtlamadan ötürü sürükle ve bırak, Files yönetici olarak çalışırken kullanılamaz. Sürükle ve bırakı kullanmak istiyorsanız, geçici bir çözüm olarak Başlat Menüsünden UAC'ı (Kullanıcı Hesap Denetimi) başlatın ve üçüncü düzeyi seçin, ardından Windows'u yeniden başlatın. Aksi hâlde Files'ı sürükle ve bırak olmadan kullanmaya devam edebilirsiniz. - Penceresi kapatıldığında uygulamayı arka planda çalışır durumda bırak + Pencere kapatıldığında uygulamayı arka planda çalışır durumda bırak Mavi @@ -3359,7 +3497,7 @@ Shown in a StatusCenter card. Used as "64 MB of 1 GB processed" - ögeler + öğe Dosyalar bu dizini Git deposu olarak başlatamaz. @@ -3373,6 +3511,9 @@ Öğeler işleniyor... + + Ögeler bulunuyor... + Hız: @@ -3385,61 +3526,129 @@ Shown in a StatusCenter card. - {0} öğe "{1}" olarak sıkıştırıldı + {0, plural,one {# öğe}other {# öğe}} "{1}" konumuna sıkıştırıldı Shown in a StatusCenter card. - {0} öğe "{1}"den "{2}"ye sıkıştırıldı + {0, plural,one {# öğe}other {# öğe}} "{1}" konumundan "{2}" konumuna sıkıştırıldı Shown in a StatusCenter card. - {0} öğe "{1}" olarak sıkıştırılırken hata oluştu + {0, plural,one {# öğenin}other {# öğenin}} "{1}" konumuna sıkıştırılma hatası Shown in a StatusCenter card. - {0} öğe "{1}"den "{2}"ye sıkıştırılamadı + {0, plural,one {# öğe}other {# öğe}} "{1}" konumundan "{2}" konumuna sıkıştırılamadı Shown in a StatusCenter card. - {0} öğe/öğe "{1}" olarak sıkıştırılıyor + {0, plural,one {# öğe}other {# öğe}} "{1}" konumuna sıkıştırılıyor Shown in a StatusCenter card. - {0} öğe "{1}"den "{2}"ye sıkıştırılıyor + {0, plural,one {# öğe}other {# öğe}} "{1}" konumundan "{2}" konumuna sıkıştırılıyor + Shown in a StatusCenter card. + + + {0} öğesinin "{1}" öğesine klonlanması iptal edildi + Shown in a StatusCenter card. + + + {0} öğesinin "{1}" öğesinden "{2}" öğesine klonlanması iptal edildi + Shown in a StatusCenter card. + + + "{0}" öğesi "{1}" öğesine klonlandı + Shown in a StatusCenter card. + + + {0} öğesi "{1}" öğesinden "{2}" öğesine klonlandı + Shown in a StatusCenter card. + + + "{0}", "{1}" öğesine klonlanırken hata oluştu + Shown in a StatusCenter card. + + + {0} öğesi "{1}" öğesinden "{2}" öğesine klonlanamadı + Shown in a StatusCenter card. + + + "{0}", "{1}" öğesine klonlanıyor + Shown in a StatusCenter card. + + + {0}, "{1}" öğesinden "{2}" öğesine klonlanıyor + Shown in a StatusCenter card. + + + {0} yazı tipi yüklemesi iptal edildi + Shown in a StatusCenter card. + + + "{1}" konumundan {0, plural, one {# yazı tipinin} other {# yazı tipinin}} yüklenmesi iptal edildi + Shown in a StatusCenter card. + + + {0, plural, one {# yazı tipi}other {# yazı tipi}} yüklendi + Shown in a StatusCenter card. + + + {0, plural, one {# yazı tipi}other {# yazı tipi}} "{1}" konumundan yüklendi + Shown in a StatusCenter card. + + + {0, plural, one {# yazı tipi}other {# yazı tipi}} yükleme hatası + Shown in a StatusCenter card. + + + {0, plural, one {# yazı tipi}other {# yazı tipi}} "{1}" konumundan yüklenemedi + Shown in a StatusCenter card. + + + {0, plural, one {# yazı tipi}other {# yazı tipi}} yükleniyor + Shown in a StatusCenter card. + + + {0, plural, one {# yazı tipi}other {# yazı tipi}} "{1}" konumundan yükleniyor Shown in a StatusCenter card. - {0} öğenin "{1}" öğesine kopyalanması iptal edildi + {0, plural,one {# öğenin}other {# öğenin}} "{1}" konumuna kopyalanması iptal edildi Shown in a StatusCenter card. - {0} öğenin "{1}" öğesinden "{2}" öğesine kopyalanması iptal edildi + {0, plural,one {# öğenin}other {# öğenin}} "{1}" konumundan "{2}" konumuna kopyalanması iptal edildi Shown in a StatusCenter card. - {0} öğe "{1}" öğesine kopyalandı + {0, plural,one {# öğe}other {# öğe}} "{1}" konumuna kopyalandı Shown in a StatusCenter card. - {0} öğe "{1}" öğesinden "{2}" öğesine kopyalandı + {0, plural,one {# öğe}other {# öğe}} "{1}" konumundan "{2}" konumuna kopyalandı Shown in a StatusCenter card. - {0} öğe "{1}" öğesine kopyalanırken hata oluştu + {0, plural,one {# öğenin}other {# öğenin}} "{1}" konumuna kopyalanma hatası Shown in a StatusCenter card. - {0} öğe "{1}" öğesinden "{2}" öğesine kopyalanamadı + {0, plural,one {# öğe}other {# öğe}} "{1}" konumundan "{2}" konumuna kopyalanamadı Shown in a StatusCenter card. - {0} öğe "{1}" öğesine kopyalanıyor + {0, plural,one {# öğe}other {# öğe}} "{1}" konumuna kopyalanıyor Shown in a StatusCenter card. - {0} öğe "{1}" öğesinden "{2}" öğesine kopyalanıyor + {0, plural,one {# öğe}other {# öğe}} "{1}" konumundan "{2}" konumuna kopyalanıyor Shown in a StatusCenter card. + + {0, plural,one {# öge}other {# öge}} bulundu + Shown in a StatusCenter card during file discovery phase. + "{0}" öğesinin "{1}" öğesine çıkarılması iptal edildi Shown in a StatusCenter card. @@ -3473,55 +3682,55 @@ Shown in a StatusCenter card. - "{1}" kaynağından {0} öğenin silinmesi iptal edildi + "{1}" konumundaki {0, plural,one {# öğenin}other {# öğenin}} silinmesi iptal edildi Shown in a StatusCenter card. - "{1}" kaynağından {0} öğe silindi + "{1}" konumundaki {0, plural,one {# öğe}other {# öğe}} silindi Shown in a StatusCenter card. - "{1}" kaynağından {0} öğe silinirken hata oluştu + "{1}" konumundaki {0, plural,one {# öğenin}other {# öğenin}} silinme hatası Shown in a StatusCenter card. - "{1}" kaynağından {0} öğe silinemedi + "{1}" konumundaki {0, plural,one {# öğe}other {# öğe}} silinemedi Shown in a StatusCenter card. - "{1}" kaynağından {0} öğe siliniyor + "{1}" konumundaki {0, plural,one {# öğe}other {# öğe}} siliniyor Shown in a StatusCenter card. - {0} öğenin "{1}" öğesine taşınması iptal edildi + {0, plural, one {# öğenin} other {# öğenin}} "{1}" konumuna taşınması iptal edildi Shown in a StatusCenter card. - {0} öğenin "{1}" konumundan "{2}" konumuna taşınması iptal edildi + {0, plural, one {# öğenin} other {# öğenin}} "{1}" konumundan "{2}" konumuna taşınması iptal edildi Shown in a StatusCenter card. - {0} öğe "{1}" konumuna taşındı + {0, plural,one {# öğe}other {# öğe}} "{1}" konumuna taşındı Shown in a StatusCenter card. - {0} öğe "{1}" konumundan "{2}" konumuna taşındı + {0, plural,one {# öğe}other {# öğe}} "{1}" konumundan "{2}" konumuna taşındı Shown in a StatusCenter card. - {0} öğe "{1}" öğesine taşınıyor + {0, plural,one {# öğe}other {# öğe}} "{1}" konumuna taşınıyor Shown in a StatusCenter card. - {0} öğe "{1}" konumundan "{2}" konumuna taşınıyor + {0, plural,one {# öğe}other {# öğe}} "{1}" konumundan "{2}" konumuna taşınıyor Shown in a StatusCenter card. - {0} öğe "{1}" öğesine taşınırken hata oluştu + {0, plural,one {# öğenin}other {# öğenin}} "{1}" konumuna taşınma hatası Shown in a StatusCenter card. - {0} öğe "{1}" konumundan "{2}" konumuna taşınamadı + {0, plural,one {# öğe}other {# öğe}} "{1}" konumundan "{2}" konumuna taşınamadı Shown in a StatusCenter card. @@ -3549,7 +3758,7 @@ Shown in a StatusCenter card. - {0}/{1} öğe işlendi + {0}/{1, plural, one {# öğe} other {# öğeler}} işlendi Shown in a StatusCenter card. Used as "8/20 items processed" @@ -3567,6 +3776,9 @@ Arka plan duvar kağıdı ayarlanamadı + + Ayarlar dosyası açılamadı + Git kancasını sil @@ -3592,7 +3804,7 @@ Bu etiketi uygularken bir hata oluştu - Seçili arşivlerdeki öğeleri tek öğeli arşiv için geçerli klasöre veya çok öğeli arşiv için yeni klasöre çıkar + Seçilen {0, plural, one {arşiv} other {arşivler}}i tek öğe için buraya veya çoklu öğe için yeni klasöre çıkarın Buraya çıkart (Akıllı) @@ -3601,7 +3813,7 @@ Dosyaları ve klasörleri birlikte sırala - Dosyaları ve klasörleri birlikte sırala + Dosyaları ve klasörleri aynı listede sıralayın Önce dosyaları sırala @@ -3657,9 +3869,6 @@ Sorular ve tartışmalar - - Döşeme Görünümü için henüz ek boyutlar mevcut değildir. - Ölçek Used to describe layout sizes @@ -3746,7 +3955,7 @@ Özelleştirilmiş - Tercihler senkronize edildiğinde uyarlanabilir düzen kullanılamaz, varsayılan düzen Ayrıntılar olarak değiştirilmiştir. + Tercihler eşitlendiğinde uyarlanabilir düzen kullanılamaz, varsayılan düzen ayrıntılar olarak değiştirildi. Arka plan resmi @@ -3821,6 +4030,10 @@ ICO Dosyası This is the friendly name for ICO files. + + ICL Dosyası + This is the friendly name for ICL files. + Zip Dosyası This is the friendly name for ZIP files. @@ -3829,6 +4042,10 @@ Bit Eşlem Resmi Dosyaları This is the friendly name for bitmap files. + + Resim Dosyaları + This is the friendly name for image files. + Sayı @@ -3854,52 +4071,310 @@ Durum: - Araç Çubuğunu Göster - Setting that controls if the Toolbar is shown in the main view + Araç çubuğunu göster + Setting that controls if the toolbar is shown in the main view Sekme aksiyonları menüsü - - Add vertical pane + + Dikey olarak böl - Add a vertical pane + Bölmeyi dikey olarak böl - - Add horizontal pane + + Yatay olarak böl - - Add a horizontal pane + + Bölmeyi yatay olarak böl - Arrange vertically + Dikey düzenle - Arrange panes vertically + Sekmeleri dikey düzenle - Arrange horizontally + Yatay düzenle - Arrange panes horizontally + Sekmeleri yatay düzenle - - Add pane + + Bölünmüş panel - Show tab actions button in the title bar + Başlık çubuğunda sekme eylemleri düğmesini göster - Arrange panes + Bölmeleri düzenle + + + Varsayılan çift bölmeli bölme yönü - - Default pane arrangement + + Çift bölme modu - Horizontal + Yatay - Vertical + Dikey + + + Sistem tepsisinde Files simgesini göster + + + CPU iş parçacıkları + + + Ev sayfasına gidin + + + Araç çubukları + + + Kullanıcı kimliği + + + Toplu yeniden adlandırma + + + İçerikleri sıkıştır + + + Alternatif veri akışı oluşturma seçeneğini göster + + + Alternatif veri akışı oluştur + + + Seçilen {0, plural, one {öğe} other {öğeler}} için alternatif veri akışı oluştur + + + Veri akışı adını girin + + + Alternatif veri akışını oluştururken bir sorun oluştu + + + Lütfen şunu not edin ki alternatif veri akışları yalnızca NTFS formatına sahip olan sürücülerde çalışacaktır. + + + Alternatif veri akışları şu an gizli + + + Başka veri akışlarını görüntülemek ister misiniz? Bu ayarı her zaman dosyalar ve klasörler ayarlar sayfasından değiştirebilirsiniz. + + + Etiketleri yönet + + + Mevcut + + + Toplam + + + Her zaman yeni oluşturulan sekmeye odaklan + + + Raf bölmesini değiştir + + + Raf bölmesinin görünürlüğünü değiştir + + + Adres çubuğunda raf bölmesi ayarını göster + + + Raf + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + Öğeleri Sil + + + Raftan kaldır + + + Rafa ekle + Tooltip that displays when dragging items to the Shelf Pane + + + Karşılaştırmak için bir karma değeri girin + Placeholder that appears in the compare hash text box + + + {0} eşleşme + Appears when two compared hashes match, e.g. "Matches SHA256" + + + Eşleşme bulunamadı + Appears when two compared hashes don't match + + + Yol ya da yürütme takma adı + + + Geçersiz dosya yolu + + + Test entegrasyonu + + + {0} bulunamadı. Lütfen ayarlarınızı kontrol edip tekrar deneyin. + + + Yapılandırılan IDE bulunamadı + + + Ayarları aç + + + Visual Studio Code + + + Bir konum veya uygulama yürütme takma adı girin + + + Lütfen IDE için bir ad girin + + + Depoyu klonla + Clone repo dialog title + + + Klonla + Primary action button in the clone repo dialog + + + Depo URL'si + URL textbox header in the clone repo dialog + + + Repo klonlanamıyor + Cannot clone repo dialog title + + + Dosyayı karşılaştır + Button that appears in file hash properties that allows the user to compare two files + + + Boyut biçimi + + + İkili + + + Ondalık + + + Sağ tıklayıp eklemek istediğiniz bölümleri seçerek kenar çubuğuna bölümler ekleyebilirsiniz + + + Dosyaları veya klasörleri buraya sürükleyerek farklı sekmelerde etkileşim kurabilirsiniz + + + Gezinmek için bir yol girin... + + + Özellikleri ve komutları bulun... + + + Dosya ve klasörleri arayın... + + + Dosya işlemler sırasında + + + Durum merkezi düğmesini göster + + + Durum merkezi ilerleme halkası + Screen reader name for the status center progress ring + + + Simge dosyaları + This is the friendly name for a variety of different icon files. + + + Daraltılmış yol izini göster + + + {0} içindeki klasörleri göster + + + Ana Sayfa'daki klasörleri göster + + + {0} içeren hiçbir komut yoktur + + + Daha fazla gör + + + Şunlara göre filtrele + + + Dosya adı + + + İmzalar + + + İmza listesi + + + Veren: + + + Verilen: + + + Geçerlilik tarihi: + + + Geçerli: + + + İmza bulunamadı. + + + Klasörleri Windows Terminal'de açma seçeneğini göster + + + Günlük dosyasını aç + + + Günlük dosyasını varsayılan düzenleyici içinde aç + + + Günlük dosyası konumunu varsayılan dosya yöneticide aç + + + Günlük dosyası açılamıyor + + + Durum çubuğunu göster + + + Yalnızca Sütunlar Görünümünde + + + Yeni '{0}' + + + Akıcı kaydırmayı etkinleştirin + + + Kaydırma + + + Show option to Pin to Sidebar + + + Show option to Pin to the Start Menu \ No newline at end of file diff --git a/src/Files.App/Strings/uk-UA/Resources.resw b/src/Files.App/Strings/uk-UA/Resources.resw index 2791d9f37957..476933651ccb 100644 --- a/src/Files.App/Strings/uk-UA/Resources.resw +++ b/src/Files.App/Strings/uk-UA/Resources.resw @@ -123,9 +123,15 @@ Копіювати шлях + + Копіювати шлях до елемента + Копіювати шлях у лапках + + Скопіювати шлях до виділеного елементу з лапками + Вибрати @@ -160,7 +166,7 @@ Пропустити - Виділити все + Вибрати все Інвертувати виділення @@ -249,6 +255,9 @@ Вставити + + Вставити ярлик + Створити @@ -291,8 +300,8 @@ Введіть назву елемента - - Встановити ім'я + + Створити новий {0} Світла @@ -300,6 +309,9 @@ Темна + + Використовувати системні налаштування + За замовчуванням @@ -340,7 +352,7 @@ Установити як фонове зображення екрана блокування - Встановити як фон програми + Встановити як фон застосунку Не вдалося видалити цей елемент @@ -349,7 +361,7 @@ Підключить необхідний диск для доступу до цього елемента. - Диск відключено + Диск від’єднано Файл, до якого ви намагаєтеся отримати доступ, можливо було переміщено або видалено. @@ -385,40 +397,40 @@ Лише для читання - {0, plural, one {# day ago} other {# days ago}} + {0, plural, one {# день тому} few {# дні тому} many {# днів тому} other {# днів тому}} - 1 day ago + 1 день тому - {0} days ago + {0} днів тому - {0, plural, one {# hour ago} other {# hours ago}} + {0, plural, one {# годину тому} few {# години тому} many {# годин тому} other {# годин тому}} - 1 hour ago + 1 годину тому - {0} hours ago + {0} годин тому - {0, plural, one {# minute ago} other {# minutes ago}} + {0, plural, one {# хвилину тому} few {# хвилини тому} many {# хвилин тому} other {# хвилин тому}} - 1 minute ago + 1 хвилину тому - {0} minutes ago + {0} хвилин тому - {0, plural, one {# second ago} other {# seconds ago}} + {0, plural, one {# секунду тому} few {# секунди тому} many {# секунд тому} other {# секунд тому}} - 1 second ago + 1 секунду тому - {0} seconds ago + {0} секунд тому Щойно @@ -443,7 +455,7 @@ ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural - {0, plural, one {елемент} few {об'єкти} many {об'єкти} other {об'єкти}} + {0, plural, one {елемент} few {елементи} many {елементів} other {елементів}} ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural @@ -477,10 +489,10 @@ Б - {0, number} {0, plural, one {file} other {files}}, {1, number} {1, plural, one {folder} other {folders}} + {0, number} {0, plural, one {файл} few {файли} many {файлів} other {файли}}, {1, number} {1, plural, one {тека} few {теки} many {тек} other {тек}} - {0, number} {0, plural, one {file} other {files}}, {1, number} {1, plural, one {folder} other {folders}} from {2, number} {2, plural, one {location} other {locations}} + {0, number} {0, plural, one {файл} few {файли} many {файлів} other {файлів}}, {1, number} {1, plural, one {тека} few {теки} many {тек} other {тек}} від {2, number} {2, plural, one {розміщення} few {розміщень} many {розміщень} other {розміщення}} Запуск від імені іншого користувача @@ -513,10 +525,7 @@ Перемістити вкладку - Status - - - Стандартна + Стан Немає результатів @@ -530,6 +539,9 @@ Копіювати до {0} + + Клонувати до {0} + Створити ярлик @@ -894,10 +906,10 @@ Швидкість потоку - Аудіо бітрейт + Бітрейт звуку - Бітрейт відео для кодування + Бітрейт відео Стискання @@ -998,6 +1010,9 @@ Результати пошуку в + + Результати пошуку по «{0}» + Подробиці @@ -1032,13 +1047,13 @@ Відображати опцію відкриття папок у новій панелі - Відображати опцію копіювання шляху + Показувати параметр копіювання шляху - Відображати опцію створення папки з обраними елементами + Показувати параметр створення теки з обраними елементами - Відображати опцію створення ярлика + Показувати параметр створення ярлика Нова панель @@ -1052,9 +1067,6 @@ Подробиці (Ctrl+Shift+1) - - Плитка - Дата видалення @@ -1071,19 +1083,34 @@ Відобразити панель попереднього перегляду - Зобразити панель подробиць + Показати / Приховати панель подробиць - Перемкнути панель деталей для перегляду основних властивостей файлу + Перемкнути панель подробиць для перегляду основних властивостей файлу Перемкнути інформаційну панель - Перемкнути інформаційну панель для перегляду деталей/прев'ю панелей + Увімк. / Вимк. видимість деталей / передперегляду панелей - Toggle the Toolbar + Сховати / Показати панель інструментів + + + Сховати / Приховати видимість панелі інструментів + + + Увімк. / Вимк. заголовок фільтра + + + Увімк. / Вимк. видимість заголовка фільтра + + + Увімк. / Вимк. подвійні панелі + + + Увімк. / Вимк. режим подвійних панелей Попередній перегляд недоступний @@ -1238,41 +1265,47 @@ Відкрити Сховище Даних + + Відкрити сторінку Сховища Даних у Налаштуваннях Windows + + + Очистити + Копіювати - Copy {0, plural, one {item} other {items}} + Копіювати {0, plural, one {елемент} few {елементи} many {елементів} other {елемент}} - Delete {0, plural, one {item} other {items}} + Видалити {0, plural, one {елемент} few {елементи} many {елементів} other {елемент}} Перемістити - {0, plural, one {One item will be moved} other {# items will be moved}} + {0, plural, one {Один елемент буде переміщено} few {# елементи буде переміщено} many {# елементів буде переміщено} other {# елементів буде переміщено}} - Move {0, plural, one {item} other {items}} + Перемістити {0, plural, one {елемент} few {елементи} many {елементів} other {елемент}} - {0, plural, one {One item will be deleted} other {# items will be deleted}} + {0, plural, one {Один елемент буде видалено} few {# елементи буде видалено} many {# елементів буде видалено} other {# елементів буде видалено}} Продовжити - {0, plural, one {There is one conflicting file name} other {There are # conflicting file names}} + {0, plural, one {Є одне конфліктне ім'я файлу} few {Є # конфліктні імена файлів} many {Є # конфліктних імен файлів} other {Є # конфліктні імена файлів}} - {0, plural, one {There is one conflicting file name} other {There are # conflicting file names}}, and {1, plural, one {one outgoing item} other {# outgoing items}} + {0, plural, one {Є одне конфліктне ім'я файлу} few {Є # конфліктні імена файлів} many {Є # конфліктних імен файлів} other {Є # конфліктні імена файлів}}, і {1, plural, one {один вихідний елемент} few {# вихідні елементи} many {# вихідних елементів} other {# вихідні елементи}} - Conflicting {0, plural, one {file name} other {file names}} + Конфлікт {0, plural, one {ім'я файлу} few {імен файлів} many {імен файлів} other {ім'я файлу}} - {0, plural, one {One item will be copied} other {# items will be copied}} + {0, plural, one {Один елемент буде скопійовано} few {# елементи буде скопійовано} many {# елементів буде скопійовано} other {# елементів буде скопійовано}} Остаточно видалити @@ -1380,7 +1413,7 @@ Елементів: {0} - Відкрити закриту вкладку + Знову відкрити вкладку Перейменувати @@ -1455,7 +1488,7 @@ Розпакувати архів - Після завершення відкрийте папку призначення + Після завершення відкрийте теку призначення Видобути до {0}\ @@ -1470,7 +1503,7 @@ Ви впевнені, що бажаєте відновити стандартні бібліотеки? Усі ваші файли залишаться у вашому сховищі. - Відновлення Бібліотек + Відновлення бібліотек Власник @@ -1491,7 +1524,7 @@ файли - цю папку + цю теку вкладені папки @@ -1542,7 +1575,7 @@ Видалення підкаталогів та файлів - Виконати файли + Виконання файлів Читання атрибутів @@ -1560,7 +1593,7 @@ Стати власником - Перейти у папку + Перейти в теку Записування атрибутів @@ -1587,7 +1620,7 @@ Замінити всіх записів дозволів дочірніх об'єктів на успадковувані записи дозволів з цього об'єкта - Close active pane + Закрити панель Компактний режим @@ -1611,13 +1644,13 @@ Версія файлу: - Завантажити прев'ю повністю + Завантажити передперегляд повністю Завантажує елемент з хмари і завантажує попередній перегляд - {0, plural, one {# item} other {# items}} ({1, plural, one {# file} other {# files}}, {2, plural, one {# folder} other {# folders}}) + {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елемент}} ({1, plural, one {# файл} few {# файли} many {# файлів} other {# файли}}, {2, plural, one {# тека} few {# теки} many {# тек} other {# тек}}) Нестиснутий розмір @@ -1646,15 +1679,6 @@ Основне - - Ctrl+Shift+1 - - - Ctrl+Shift+2 - - - Ctrl+Shift+6 - Видобування архіву завершено успішно. @@ -1712,8 +1736,8 @@ Стовпці - - Плитки + + Картки Відкрити папки у новій вкладці @@ -1749,7 +1773,7 @@ Мітка - Відкривати елементи одним кліком + Відкривати файли одним клацанням Шлях до папки @@ -1826,6 +1850,18 @@ Зареєструйте цю програму для перезавантаження + + Початкове вікно + + + Звичайне вікно + + + Згорнуто + + + Розгорнуто + Запуск від імені адміністратора @@ -1848,11 +1884,14 @@ Немає зменшеного кольору - Third party libraries + Сторонні бібліотеки Ця опція змінює системний реєстр і може мати несподівані побічні ефекти на вашому пристрої. Продовжуйте на свій страх і ризик. + + Операції зведення є постійними та не рекомендуються. Продовжуйте на свій страх і ризик. + Створити бібліотеку @@ -1931,6 +1970,9 @@ Закрити інші вкладки + + Закрити всі вкладки + Музика @@ -1985,11 +2027,23 @@ Поведінки - - Оцінити Files + + Привіт! + + + Подобається Files? Залиште відгук у Microsoft Store. + + + Подобається Files? Підтримайте проєкт на GitHub. + + + Спонсор + + + Оцінити нас - - Бажаєте залишити відгук про Files? + + Відхилити Встановити як тло @@ -1998,7 +2052,7 @@ Установити як тло робочого стола - Встановити за замовчуванням + Зробити типовим Стовпчик дати @@ -2018,8 +2072,8 @@ Екран блокування - - Відкрити папки одним натисканням по Макету Стовпчиків + + Відкривати теки одним клацанням Відкриття елементів @@ -2027,6 +2081,21 @@ Стиснути + + Перемістити весь вміст підтек у вибране розташування + + + Вирівняти теку + + + Вирівняти + + + Вирівнювання теки перемістить увесь вміст її вкладених тек у вибране місце. Ця операція є постійною і не може бути скасована. Використовуючи цю експериментальну функцію, ви визнаєте ризик і погоджуєтеся не притягувати команду Files до відповідальності за будь-яку втрату даних. + + + Показати параметри вирівнювання + Вибрати файли та папки при наведенні на них @@ -2037,7 +2106,7 @@ Відновити всі елементи - Ви хочете відновити {0} виділених елементів? + Ви хочете відновити {0, plural, one {вибраний елемент} few {{0} вибраних елементи} many {{0} вибраних елементів} other {{0} вибраних елементів}}? Відновити вибране @@ -2060,6 +2129,12 @@ Пароль архіву + + Кодування + + + {0} (виявлено) + Шлях @@ -2102,9 +2177,24 @@ Розмір розділення - + Не розділяти + + Auto + + + Dictionary size + + + Word size + + + Estimated memory usage: {0} + + + Available memory: {0} + CD @@ -2159,8 +2249,14 @@ Редагувати файл налаштувань - - Що нового + + Відкрити файл налаштувань у типовому редакторі + + + Список змін + + + Відкрити Список змін Створення ярлика у цьому місці потребує прав адміністратора @@ -2241,25 +2337,22 @@ Опції контекстного меню - Відображати розширення файлів - - - Показати приховані елементи + Розширення файлів - Форматувати... + Формат Довідка - Відкрити на повний екран + На весь екран Ви впевнені, що хочете видалити цю мітку? - Відтворити усі + Відтворити Висота @@ -2319,7 +2412,7 @@ Збільшити розмір - Перемкнути напрямок сортування + Порядок сортування Недопустима назва @@ -2331,34 +2424,37 @@ Відео - Запустити спливне вікно попереднього перегляду + Спливне вікно передперегляду Увімкнути компактний оверлей - Відкрити сторінку довідки в браузері онлайн + Відкрити онлайн сторінку довідки у вашому браузері - Відкрити на повний екран + Перемкнути режим «На весь екран» - Компактний режим + Увімкнути компактне накладання - Вийти з компактного режиму + Вимкнути компактне накладання - Увімкнути компактний оверлей + Увімк. / Вимк. компактне накладання - Перейти до поля пошуку + Почати пошук в OmniBar - Перемкнути для показу прихованих елементів + Сховати / Показати приховані елементи + + + Сховати / Показати файли, що починаються з крапки - Перемкнути для показу розширень файлів + Сховати / Показати розширення файлів Перемкнути панель попереднього перегляду файлів @@ -2367,52 +2463,64 @@ Перемкнути для показу бічної панелі - Копіювати елемент(и) до буфера обміну + Скопіювати {0, plural, one {вибраний елемент} few {вибрані елементи} many {вибраних елементів} other {вибраних елементів}} - Копіювати шлях до вибраних елементів в буфер обміну + Копіювати шлях поточної теки + + + Копіювати шлях вибраних елементів + + + Копіювати шлях вибраних елементів у лапках - Копіювати шлях до вибраних елементів з лапок в буфер обміну + Копіювати шлях поточної теки в лапках - Вирізати елемент(и) до буфера обміну + Вирізати {0, plural, one {вибраний елемент} few {вибрані елементи} many {вибраних елементів} other {вибраних елементів}} - Вставити елемент(и) з буфера обміну до поточної папки + Вставити елементи до поточної теки + + + Вставити елементи до поточної теки як ярлики - Вставити елемент(и) з буфера обміну до вибраної папки + Вставити елементи до вибраної теки + + + Вставити до вибраної теки - Видалити елемент(и) + Видалити {0, plural, one {вибраний елемент} few {вибрані елементи} many {вибраних елементів} other {вибраних елементів}} Створити нову теку - Створити нові ярлик(и) для виділених елемента(ів) + Створити {0, plural, one {новий ярлик} few {нові ярлики} many {нових ярликів} other {нових ярликів}} {0, plural, one {вибраного елементу} few {вибраних елементів} many {вибраних елементів} other {вибраних елементів}} Створити новий ярлик будь-якого елемента - Очистити кошик + Очистити вміст кошика Відкрити меню "Відформатувати диск" для вибраного елемента - Відновити вибрані елемент(и) з кошика + Відновити {0, plural, one {вибраний елемент} few {вибраних елементи} many {вибраних елементів} other {вибраних елементів}} із корзини Відновити всі елементи з кошика - Відкрити елемент(и) + Відкрити {0, plural, one {елемент} few {елементи} many {елементів} other {елементів}} - Відкрити елемент(и) в обраному застосунку + Відкрити {0, plural, one {елемент} few {елементи} many {елементи} other {елементи}} вибраним застосунком Відкрити батьківську папку шуканого елемента @@ -2427,28 +2535,28 @@ Вибрати все - Інвертувати виділення елементу + Інвертувати вибрані елементи - Очистити виділення елементу + Очистити вибрані елементи Перемкнути виділення елементу - Поділитися вибраним файлом(ми) з іншими + Поділитися {0, plural, one {вибраним файлом} few {вибраними файлами} many {вибраними файлами} other {вибраними фацлами}} з іншими - Закріпити елемент(и) в меню Пуск + Закріпити {0, plural, one {елемент} few {елементи} many {елементи} other {елементи}} у меню «Пуск» - Відкріпити елемент(и) з меню Пуск + Відкріпити {0, plural, one {елемент} few {елементи} many {елементи} other {елементи}} з меню «Пуск» - Закріпити папку(и) в бічній панелі + Закріпити {0, plural, one {теку} few {теки} many {теки} other {теки}} на бічній панелі - Відкріпити папку(и) з бічної панелі + Відкріпити {0, plural, one {теку} few {теки} many {теки} other {теки}} з бокової панелі Встановити вибране зображення тлом робочого столу @@ -2462,14 +2570,23 @@ Встановити зображення тлом додатку + + Встановити шрифт + + + Встановити драйвер + + + Встановити сертифікат + - Встановити вибраний шрифт(и) + Встановити {0, plural, one {вибраний шрифт} few {вибрані шрифти} many {вибраних шрифтів} other {вибраних шрифтів}} - Встановити драйвер(и) за допомогою вибраного inf файлу(ів) + Встановити {0, plural, one {драйвер} few {драйвери} many {драйверів} other {драйверів}}, використовуючи {0, plural, one {вибраний файл} few {вибрані файли} many {вибраних файлів} other {вибраних файлів}} inf - Встановити вибрані сертифікат(и) + Встановити {0, plural, one {вибраний сертифікат} few {вибрані сертифікати} many {вибраних сертифікатів} other {вибраних сертифікатів}} Запустити вибраний застосунок як адміністратор @@ -2484,28 +2601,28 @@ Запустити попередній перегляд у спливному вікні - Створити архів з вибраним елементом(ами) + Створити архів з {0, plural, one {вибраним елементом} few {вибраними елементами} many {вибраними елементами} other {вибраними елементами}} - Миттєво створити архів 7z з вибраним елементом(ами) + Створити 7z-архів з {0, plural, one {вибраним елементом} few {вибраними елементами} many {вибраними елементами} other {вибраними елементами}} - Миттєво створити архів zip з вибраним елементом(ами) + Створити zip-архів з {0, plural, one {вибраним елементом} few {вибраними елементами} many {вибраними елементами} other {вибраними елементами}} - Розпакувати елементи з вибраного архіву(ів) в будь-яку папку + Витягнути {0, plural, one {вибраний архів} few {вибрані архіви} many {вибраних архівів} other {вибраних архівів}} до будь-якої теки - Розпакувати елементи з вибраного архіву(ів) в поточну папку + Витягнути {0, plural, one {вибраний архів} few {вибрані архіви} many {вибраних архівів} other {вибраних архівів}} до поточної теки - Розпакувати елементи з вибраного архіву(ів) в нову папку + Витягнути {0, plural, one {вибраний архів} few {вибрані архіви} many {вибраних архівів} other {вибраних архівів}} до нової теки - Повернути вибране(і) зображення ліворуч + Обернути {0, plural, one {вибране зображення} few {вибрані зображення} many {вибраних зображень} other {вибраних зображень}} ліворуч - Повернути вибране(і) зображення праворуч + Обернути {0, plural, one {вибране зображення} few {вибрані зображення} many {вибраних зображень} other {вибраних зображень}} праворуч Відкрити сторінку налаштувань @@ -2525,8 +2642,8 @@ Перемкнути на вигляд деталей - - Перемкнути на вигляд плиток + + Перемкнутись на перегляд картками Переключити на вигляд списку @@ -2628,10 +2745,10 @@ Відкрити нову вкладку - Перейти назад в історії навігації + Перейти назад - Перейти вперед в історії навігації + Перейти вперед Перейти на один каталог вище @@ -2660,8 +2777,11 @@ Закрити вкладки, окрім вибраної вкладки + + Закрити всі вкладки, включаючи поточну вкладку + - Повторно відкрити закриту вкладку + Повторно відкрити нещодавно закриту вкладку Перейти до попередньої вкладки @@ -2673,13 +2793,13 @@ Закрити поточну вкладку - Close active pane + Закрити активну панель - Focus other pane + Сфокусуватися на іншій панелі - Switch focus to the non active pane + Перемкнути фокус на іншу панель Перемкнути бічну панель @@ -2887,13 +3007,13 @@ Без міток - Переходить до попередньої вкладки + Попередня вкладка - Переходить до наступної вкладки + Наступна вкладка - Закриває поточну вкладку + Закрити вкладку Редагувати шлях @@ -2923,7 +3043,7 @@ Показувати прапорці під час вибору елементів - Фокус на панелі шляху + Змінити шлях до OmniBar Створити новий елемент @@ -2938,7 +3058,7 @@ Видалити безповоротно - Видалити елемент(и) безповоротно + Остаточно видалити {0, plural, one {вибраний елемент} few {вибраних елементи} many {вибраних елементів} other {вибраних елементів}} Відтворити вибрані медіафайли @@ -3027,6 +3147,15 @@ Ви маєте не закомічені зміни на даній гілці. Що б ви хотіли зробити з ними? + + У вас є поточне злиття з невирішеними конфліктами. Вирішіть конфлікти або скасуйте злиття, щоб перейти до іншої гілки. + + + Скасувати злиття і перейти до «{0}» + + + Залишитися в «{0}» і вирішити конфлікти + Змінити гілку @@ -3055,14 +3184,20 @@ Перейти на нову гілку - Створити теку з поточним вибраним елементом(ами) + Створити теку з {0, plural, one {поточним вибраним елементом} few {поточними вибраними елементами} many {поточними вибраними елементами} other {поточними вибраними елементами}} - Відкрити властивості + Властивості + + + Відкрити властивості провідника файлів Відкрити вікно властивостей + + Відкрити вікно властивостей провідника файлів + Локальні @@ -3081,6 +3216,9 @@ Запустити git fetch + + Клонувати git-репозиторій + Запустити git pull @@ -3125,7 +3263,7 @@ Mica Alt - Матеріал фону + Фон Цільний @@ -3157,17 +3295,17 @@ Наразі Files не може отримати доступ до GitHub. - - Відкрити папку у VS Code + + Відкрити теку в {0} - - Відкрити каталог у Visual Studio Code + + Відкрити поточний каталог у {0} - - Відкрити репозиторій у VS Code + + Відкрити репозиторій у {0} - - Відкрийте корінь Git репозиторію за допомогою Visual Studio Code + + Відкрийте корінь Git-репозиторію в {0} Скопіювати код @@ -3194,7 +3332,7 @@ Ініціалізувати репозиторій - Ініціалізувати репозиторій Git + Встановити поточну теку як git-репозиторій Назва віддаленого репозиторію @@ -3209,13 +3347,13 @@ Впорядкувати елементи за шляхом - Відкрити каталог у новій панелі + Відкрити вибраний каталог у новій панелі - Відкрити каталог у новій вкладці + Відкрити вибраний каталог у новій вкладці - Відкрити каталог у новому вікні + Відкрити вибраний каталог у новому вікні Відкрити все @@ -3240,10 +3378,10 @@ Команда '{0}' не готова до виконання. - Панель команд + Командна палітра - Відкрити панель команд + Відкрити командну палітру в OmniBar Запустити в: @@ -3373,6 +3511,9 @@ Опрацювання елементів... + + Виявлення елементів... + Швидкість: @@ -3385,61 +3526,129 @@ Shown in a StatusCenter card. - Стиснення {0} елемент(ів) до "{1}" + Стиснуто {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} до «{1}» Shown in a StatusCenter card. - Стиснення {0} елемент(ів) з "{1}" до "{2}" + Стиснуто {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} з «{1}» до «{2}» Shown in a StatusCenter card. - Помилка стиснення {0} елемент(ів) до "{1}" + Помилка стиснення {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} до «{1}» Shown in a StatusCenter card. - Не вдалось стиснути {0} елемент(ів) з "{1}" до "{2}" + Не вдалося стиснути {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} з «{1}» до «{2}» Shown in a StatusCenter card. - Стиснення {0} елемент(ів) до "{1}" + Стиснення {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} до «{1}» Shown in a StatusCenter card. - Стиснення {0} елемент(ів) до "{1}" до "{2}" + Стиснення {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} з «{1}» до «{2}» + Shown in a StatusCenter card. + + + Скасовано клонування {0} до «{1}» + Shown in a StatusCenter card. + + + Скасовано клонування {0} з «{1}» до «{2}» + Shown in a StatusCenter card. + + + Клоновано «{0}» до «{1}» + Shown in a StatusCenter card. + + + Клоновано {0} з «{1}» до «{2}» + Shown in a StatusCenter card. + + + Помилка клонування «{0}» до «{1}» + Shown in a StatusCenter card. + + + Не вдалося клонувати {0} з «{1}» до «{2}» + Shown in a StatusCenter card. + + + Клонування «{0}» до «{1}» + Shown in a StatusCenter card. + + + Клонування {0} з «{1}» до «{2}» + Shown in a StatusCenter card. + + + Скасовано встановлення {0} шрифтів + Shown in a StatusCenter card. + + + Скасовано встановлення {0, plural, one {# шрифту} few {# шрифтів} many {# шрифтів} other {# шрифтів}} з «{1}» + Shown in a StatusCenter card. + + + Встановлено {0, plural, one {# шрифт} few {# шрифти} many {# шрифтів} other {# шрифтів}} + Shown in a StatusCenter card. + + + Встановлено {0, plural, one {# шрифт} few {# шрифти} many {# шрифтів} other {# шрифтів}} з «{1}» + Shown in a StatusCenter card. + + + Помилка встановлення {0, plural, one {# шрифт} few {# шрифти} many {# шрифтів} other {# шрифтів}} + Shown in a StatusCenter card. + + + Не вдалося встановити {0, plural, one {# шрифту} few {# шрифтів} many {# шрифтів} other {# шрифтів}} з «{1}» + Shown in a StatusCenter card. + + + Встановлення {0, plural, one {# шрифт} few {# шрифти} many {# шрифтів} other {# шрифтів}} + Shown in a StatusCenter card. + + + Встановлення {0, plural, one {# шрифт} few {# шрифти} many {# шрифтів} other {# шрифтів}} з «{1}» Shown in a StatusCenter card. - Скасовано копіювання {0} елемент(ів) до "{1}" + Скасовано копіювання {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} до «{1}» Shown in a StatusCenter card. - Скасовано копіювання {0} елемент(ів) з "{1}" до "{2}" + Скасовано встановлення {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} з «{1}» до «{2}» Shown in a StatusCenter card. - Скопійовано {0} елемент(ів) до "{1}" + Скопійовано {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} до «{1}» Shown in a StatusCenter card. - Скопійовано {0} елемент(ів) з "{1}" до "{2}" + Скопійовано {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} з «{1}» до «{2}» Shown in a StatusCenter card. - Помилка копіювання {0} елемент(ів) до "{1}" + Помилка копіювання {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} до «{1}» Shown in a StatusCenter card. - Не вдалось скопіювати {0} елемент(ів) з "{1}" до "{2}" + Не вдалося скопіювати {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} з «{1}» до «{2}» Shown in a StatusCenter card. - Копіювання {0} елемент(ів) до "{1}" + Копіювання {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} до «{1}» Shown in a StatusCenter card. - Копіювання {0} елемент(ів) з "{1}" до "{2}" + Копіювання {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} з «{1}» до «{2}» Shown in a StatusCenter card. + + Виявлено {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} + Shown in a StatusCenter card during file discovery phase. + Скасовано видобування "{0}" до "{1}" Shown in a StatusCenter card. @@ -3473,55 +3682,55 @@ Shown in a StatusCenter card. - Скасовано видалення {0} елементів з "{1}" + Скасовано видалення {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} з «{1}» Shown in a StatusCenter card. - Видалено {0} item(s) з "{1} " " + Видалено {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} з «{1}» Shown in a StatusCenter card. - Error deleting {0} item(s) from "{1}" + Помилка видалення {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} з «{1}» Shown in a StatusCenter card. - Не вдалося видалити {0} item(s) з "{1}" + Не вдалося видалити {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} з «{1}» Shown in a StatusCenter card. - Видалення {0} елемент(ів) з "{1} " " + Видалення {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} з «{1}» Shown in a StatusCenter card. - Скасовано переміщення {0} елемент(ів) до "{1}" + Скасовано переміщення {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} до «{1}» Shown in a StatusCenter card. - Скасовано переміщення {0} елемент(ів) з "{1}" до "{2}" + Скасовано переміщення {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} з «{1}» до «{2}» Shown in a StatusCenter card. - Переміщено {0} елемент(ів) до "{1}" + Переміщено {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} до «{1}» Shown in a StatusCenter card. - Переміщено {0} елемент(ів) з "{1}" до "{2}" + Переміщено {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} з «{1}» до «{2}» Shown in a StatusCenter card. - Переміщення {0} елемент(ів) з "{1}" + Перміщення {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} до «{1}» Shown in a StatusCenter card. - Переміщення {0} елемент(ів) з "{1}" до "{2}" + Переміщення {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} з «{1}» до «{2}» Shown in a StatusCenter card. - Помлка переміщення {0} елемент(ів) до "{1}" + Помилка переміщення {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} до «{1}» Shown in a StatusCenter card. - Не вдалось перемістити {0} елемент(ів) з "{1}" до "{2}" + Не вдалося перемістити {0, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} з «{1}» до «{2}» Shown in a StatusCenter card. @@ -3549,7 +3758,7 @@ Shown in a StatusCenter card. - {0}/{1} елемент(ів) опрацьовано + {0}/{1, plural, one {# елемент} few {# елементи} many {# елементів} other {# елементів}} оброблено Shown in a StatusCenter card. Used as "8/20 items processed" @@ -3567,6 +3776,9 @@ Не вдалося встановити шпалери + + Не вдалося відкрити файл налаштувань + Видалити гілку Git @@ -3592,7 +3804,7 @@ При застосуванні цього тега сталася помилка - Видобути елементи з вибраного архіву до поточної теки для однопредметного архіву або до нової теки для декількох елементів + Витягнути {0, plural, one {вибраний архів} few {вибрані архіви} many {вибраних архівів} other {вибраних архівів}} сюди для одного елемента або в нову теку для декількох Видобути тут (Smart) @@ -3601,7 +3813,7 @@ Сортувати папки та файли разом - Сортувати папки та файли разом + Сортувати файли та теки з одного списку Спершу сортувати файли @@ -3657,9 +3869,6 @@ Питання та обговорення - - Додаткові розміри поки що недоступні для перегляду плиток. - Компактний Used to describe layout sizes @@ -3728,7 +3937,7 @@ Додати команду - Відновити значення за замовчуванням + Відновити усталені значення Виберіть дію @@ -3740,13 +3949,13 @@ Цей зв'язування ключів вже використовується, будь ласка, оберіть інший ключ, щоб продовжити. - The key binding you choose cannot be used, please try again using a different key binding. + Прив'язка клавіш, яку ви обираєте, не можна використати, будь ласка, спробуйте ще раз, використовуючи інше прив’язування клавіш. Модифікований - Адаптивна розкладка неможлива при синхронізації налаштувань, макет за замовчуванням змінено на деталі. + Адаптивна розкладка неможлива при синхронізації налаштувань, типовий макет змінено на деталі. Фонове зображення @@ -3821,6 +4030,10 @@ Файл ICO This is the friendly name for ICO files. + + ICL файл + This is the friendly name for ICL files. + ZIP-файл This is the friendly name for ZIP files. @@ -3829,8 +4042,12 @@ Файли растру This is the friendly name for bitmap files. + + Файли зображень + This is the friendly name for image files. + - Num + Номер Мережеві розташування @@ -3848,58 +4065,316 @@ Редагування виділеного файлу в Notepad - Dimensions: + Розміри: - Status: + Стан: - Show Toolbar - Setting that controls if the Toolbar is shown in the main view + Показати панель інструментів + Setting that controls if the toolbar is shown in the main view - Tab actions menu + Меню дій вкладки - - Add vertical pane + + Розділити вертикально - Add a vertical pane + Розділити панелі вертикально - - Add horizontal pane + + Розділити горизонтально - - Add a horizontal pane + + Розділити панелі горизонтально - Arrange vertically + Розташувати вертикально - Arrange panes vertically + Розташувати панелі вертикально - Arrange horizontally + Розташувати горизонтально - Arrange panes horizontally + Розташувати панелі горизонтально - - Add pane + + Розділена панель - Show tab actions button in the title bar + Показати кнопку дій вкладки в рядку заголовка - Arrange panes + Розташувати панелі - - Default pane arrangement + + Типовий напрямок розділення на дві панелі + + + Режим подвійних панелей - Horizontal + Горизонтально - Vertical + Вертикально + + + Показати піктограму Files в System Tray + + + Потоки CPU + + + Перейти до головної сторінки + + + Панелі інструментів + + + Ідентифікатор користувача + + + Масове перейменування + + + Стиснути вміст + + + Показати опцію створення альтернативного потоку даних + + + Створити альтернативний потік даних + + + Створити альтернативний потік даних для {0, plural, one {вибраного елементу} few {вибраних елементів} many {вибраних елементів} other {вибраних елементів}} + + + Введіть назву потоку даних + + + Виникла помилка при створенні альтернативного потоку даних + + + Зверніть увагу, що альтернативні потоки даних працюють тільки на дисках, форматованих як NTFS. + + + Альтернативні потоки даних наразі приховані + + + Чи бажаєте Ви показувати альтернативні потоки даних? Ви можете змінити це налаштування в будь-який час зі сторінки налаштувань файлів і каталогів. + + + Керувати тегами + + + Доступно + + + Загалом + + + Завжди перемикати фокус на новостворену вкладку + + + Перемкнути панель полиць + + + Сховати / Показати панель полиць + + + Показувати перемикач панелі полиць в адресному рядку + + + Полиця + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + Очистити елементи + + + Вилучити з полиці + + + Додати на полицю + Tooltip that displays when dragging items to the Shelf Pane + + + Введіть хеш для порівняння + Placeholder that appears in the compare hash text box + + + Збіг з {0} + Appears when two compared hashes match, e.g. "Matches SHA256" + + + Збігів не знайдено + Appears when two compared hashes don't match + + + Шлях або псевдонім + + + Недійсний шлях + + + Перевірка інтеграції + + + {0} не може бути розміщено. Перевірте ваші налаштування і повторіть спробу. + + + Не вдалося знайти налаштований IDE + + + Відкрити налаштування + + + Visual Studio Code + + + Введіть шлях або запустіть псевдонім + + + Введіть назву для IDE + + + Клонувати репозиторій + Clone repo dialog title + + + Клонувати + Primary action button in the clone repo dialog + + + URL репозиторію + URL textbox header in the clone repo dialog + + + Неможливо клонувати репозиторій + Cannot clone repo dialog title + + + Порівняти файл + Button that appears in file hash properties that allows the user to compare two files + + + Формат розміру + + + Двійковий + + + Десятковий + + + Ви можете додати розділи на бічну панель, клацнувши ПКМ та вибравши розділи, які ви хочете додати. + + + Перетягніть сюди файли або теки, щоби працювати з ними на різних вкладках. + + + Введіть шлях, щоби перейти до... + + + Знайти функції та команди... + + + Знайти файли й теки... + + + Під час роботи з файлами + + + Показати кнопку «Центр стану» + + + Кільце прогресу центру стану + Screen reader name for the status center progress ring + + + Файли значків + This is the friendly name for a variety of different icon files. + + + Показувати згорнутий шлях + + + Показати теку в {0} + + + Показати теки в Головній + + + Немає команд, що містять {0} + + + Показати більше + + + Фільтрувати за + + + Назва файлу + + + Підписи + + + Список підписів + + + Ким видано: + + + Кому видано: + + + Дійсний з: + + + Дійсний до: + + + Підпис не знайдено. + + + Показати параметр відкриття тек у терміналі Windows + + + Відкрити файл журналу + + + Відкрити файл журналу в типовому редакторі + + + Відкрити файл журналу в типовому файловому менеджері + + + Не вдається відкрити файл журналу + + + Показувати рядок стану + + + Лише в режимі стовпців + + + Новий {0} + + + Увімкнути плавне гортання + + + Гортання + + + Показувати параметр закріплення в бічній панелі + + + Показувати параметр закріплення у меню «Пуск» \ No newline at end of file diff --git a/src/Files.App/Strings/vi/Resources.resw b/src/Files.App/Strings/vi/Resources.resw index bd55768cd69d..411b9ef75e74 100644 --- a/src/Files.App/Strings/vi/Resources.resw +++ b/src/Files.App/Strings/vi/Resources.resw @@ -123,9 +123,15 @@ Sao chép đường dẫn + + Sao chép đường dẫn khoản mục + Sao chép đường dẫn kèm dấu ngoặc kép + + Sao chép đường dẫn khoản mục đã chọn kèm dấu ngoặc kép + Duyệt tìm @@ -142,7 +148,7 @@ Kích cỡ: - Kích cỡ trên ổ đĩa: + Kích cỡ thực tế: Kích cỡ chưa nén: @@ -160,7 +166,7 @@ Bỏ qua - Chọn tất cả + Chọn tất cả Đảo lựa chọn @@ -196,7 +202,7 @@ Giới thiệu - Mã nguồn mở + Nguồn mở Định dạng thời gian @@ -249,6 +255,9 @@ Dán + + Dán dưới dạng lối tắt + Mới @@ -283,7 +292,7 @@ Cấp quyền - Để bắt đầu, bạn sẽ cần cho phép chúng tôi hiển thị các tệp của bạn. Hành động này sẽ mở trang Cài đặt để bạn có thể cấp quyền truy cập cho chúng tôi. Bạn sẽ cần khởi động lại ứng dụng sau khi hoàn thành bước này. + Trước khi bắt đầu, bạn cần phải cấp quyền để hiển thị các tệp. Ứng dụng sẽ mở cửa sổ Cài đặt, hãy thực hiện cấp quyền trong cửa sổ này. Sau khi hoàn tất, bạn sẽ cần khởi động lại ứng dụng. Hiển thị @@ -291,8 +300,8 @@ Nhập tên khoản mục - - Đặt tên + + Tạo {0} mới Sáng @@ -300,6 +309,9 @@ Tối + + Dùng cài đặt hệ thống + Ứng dụng @@ -515,9 +527,6 @@ Trạng thái - - Mặc định của Windows - Không có kết quả @@ -530,6 +539,9 @@ Sao chép đến {0} + + Clone vào {0} + Tạo lối tắt @@ -606,7 +618,7 @@ Tên khoản mục không hợp lệ - Độ dài của tên khoản mục đã nhập vượt quá giới hạn tối đa. Vui lòng kiểm tra và thử lại. + Độ dài tên khoản mục đã nhập vượt quá giới hạn tối đa. Vui lòng kiểm tra và thử lại. Tên khoản mục quá dài @@ -738,7 +750,7 @@ Nhà sản xuất máy ảnh - Hiệu máy ảnh + Số hiệu máy ảnh Thời gian phơi sáng @@ -843,13 +855,13 @@ Url Web người dùng - Nhà văn + Người viết Năm - Người đóng góp + Cộng tác viên Tác giả cuối @@ -948,7 +960,7 @@ Chọn - Mục thùng rác + Khoản mục thùng rác Mục lối tắt @@ -978,7 +990,7 @@ Nhân đôi thẻ - Di chuyển thẻ đến cửa sổ mới + Di chuyển thẻ sang cửa sổ mới Thẻ mới @@ -998,6 +1010,9 @@ Kết quả tìm kiếm {0} trong {1} + + Kết quả tìm kiếm `{0}` + Chi tiết @@ -1035,7 +1050,7 @@ Hiện tùy chọn sao chép đường dẫn - Hiện tùy chọn tạo thư mục với lựa chọn + Hiện tùy chọn tạo thư mục từ lựa chọn Hiện tùy chọn tạo lối tắt @@ -1052,9 +1067,6 @@ Chi tiết (Ctrl+Shift+1) - - Lát (Ctrl+Shift+2) - Ngày xóa @@ -1068,22 +1080,37 @@ Tên mong muốn - Bật/Tắt ngăn xem trước + Bật tắt ngăn xem trước - Bật/Tắt ngăn chi tiết + Bật tắt ngăn chi tiết - Chuyển đổi ẩn/hiện ngăn chi tiết để xem các thuộc tính cơ bản của tệp + Ẩn hoặc hiện ngăn chi tiết để xem các thuộc tính cơ bản của tệp - Bật/Tắt ngăn thông tin + Bật tắt ngăn thông tin - Chuyển đổi ẩn/hiện ngăn thông tin để xem chi tiết hoặc xem trước tệp + Ẩn hoặc hiện thanh chi tiết và thanh xem trước - Bật/Tắt thanh công cụ + Bật tắt thanh công cụ + + + Ẩn hoặc hiện thanh công cụ + + + Bật tắt phần lọc + + + Ẩn hoặc hiện phần lọc + + + Bật tắt hai ngăn + + + Bật hoặc tắt chế độ hai ngăn Không có bản xem trước @@ -1134,7 +1161,7 @@ Không xác định - Nếu bạn thay đổi phần mở rộng, tệp có thể không sử dụng được nữa. Bạn có chắc chắn muốn thay đổi phần mở rộng không? + Nếu bạn thay đổi phần mở rộng, tệp có thể không sử dụng được nữa. Bạn có chắc chắn muốn tiếp tục không? Ổ đĩa CD ROM @@ -1238,6 +1265,12 @@ Mở Storage Sense + + Mở trang Storage Sense trong Cài đặt Windows + + + Dọn dẹp + Sao chép @@ -1287,7 +1320,7 @@ Tạo tên mới - Tạo thư mục với lựa chọn + Tạo thư mục từ lựa chọn Loại: @@ -1380,7 +1413,7 @@ {0} mục - Mở lại thẻ đã đóng + Mở lại thẻ đã đóng Đổi tên @@ -1425,7 +1458,7 @@ Sửa đổi - Quyền truy cập cho {0} + Quyền truy cập của {0} Đọc và thực thi @@ -1572,7 +1605,7 @@ Ghi thuộc tính mở rộng - Chuyển đổi quyền được kế thừa thành quyền tách biệt + Chuyển quyền được kế thừa thành quyền tách biệt Bật kế thừa quyền @@ -1587,13 +1620,13 @@ Thay thế mọi quyền truy cập của đối tượng con bằng quyền được kế thừa từ đối tượng này - Đóng ngăn hiện hoạt + Đóng ngăn - Bật chế độ thu gọn + Mở chế độ thu gọn - Tắt chế độ thu gọn + Thoát chế độ thu gọn Mô tả tệp @@ -1626,7 +1659,7 @@ WSL - Ẩn phần {0} + Ẩn mục {0} Thêm tệp @@ -1646,15 +1679,6 @@ Trang chủ - - Ctrl+Shift+1 - - - Ctrl+Shift+2 - - - Ctrl+Shift+6 - Quá trình giải nén tệp đã hoàn tất. @@ -1680,7 +1704,7 @@ Mở ổ đĩa - Vui lòng cho đĩa vào ổ đĩa {0} + Vui lòng cho đĩa vào ổ {0} Chèn đĩa @@ -1712,8 +1736,8 @@ Cột - - Lát + + Thẻ Mở thư mục trong thẻ mới @@ -1749,7 +1773,7 @@ Nhãn - Mở khoản mục bằng một lần nhấp + Mở tệp bằng một lần nhấp Đường dẫn thư mục @@ -1764,7 +1788,7 @@ Không thể nhập các cài đặt. Tệp cài đặt đã bị hỏng. - Xảy ra lỗi khi nhập các cài đặt + Đã xảy ra lỗi khi nhập các cài đặt Dọn sạch thùng rác @@ -1826,6 +1850,18 @@ Gửi yêu cầu khởi động lại chương trình + + Cửa sổ khởi động + + + Cửa sổ bình thường + + + Đã thu nhỏ + + + Đã mở rộng + Chạy với quyền quản trị viên @@ -1853,6 +1889,9 @@ Tùy chọn này sửa đổi các khóa registry của hệ thống và có thể gây ra vấn đề trên thiết bị của bạn. Vui lòng cân nhắc trước khi bật tùy chọn. + + Thao tác khử thư mục con có hiệu lực vĩnh viễn và không thể hoàn tác, không được khuyến khích sử dụng. Vui lòng cân nhắc trước khi thực hiện. + Tạo thư viện @@ -1911,7 +1950,7 @@ Chạy bằng PowerShell - Việc tính toán kích cỡ thư mục có thể tốn nhiều tài nguyên thiết bị và khiến mức sử dụng CPU tăng lên. + Việc tính toán kích cỡ thư mục tốn nhiều tài nguyên thiết bị và có thể khiến mức sử dụng CPU tăng lên. Xoay trái @@ -1931,6 +1970,9 @@ Đóng các thẻ khác + + Đóng tất cả các thẻ + Nhạc @@ -1959,7 +2001,7 @@ Thao tác di chuyển không được hỗ trợ trong ngữ cảnh này. Thay vào đó, bạn có muốn sao chép khoản mục không? - Mục bị ẩn + Khoản mục bị ẩn Tùy chỉnh @@ -1980,16 +2022,28 @@ Ctrl+Shift+7 - Hiện dòng dữ liệu rẽ nhánh + Hiện luồng dữ liệu phụ Hành vi - - Đánh giá Files + + Xin chào! + + + Thích Files? Hãy gửi nhận xét của bạn lên Microsoft Store. + + + Thích Files? Hãy cân nhắc hỗ trợ dự án trên GitHub. - - Bạn có muốn đánh giá Files không? + + Tài trợ + + + Đánh giá + + + Bỏ qua Đặt làm hình nền @@ -2018,8 +2072,8 @@ Màn hình khóa - - Mở thư mục bằng một lần nhấp ở bố cục dạng cột + + Mở thư mục bằng một lần nhấp Mở các khoản mục @@ -2027,17 +2081,32 @@ Nén + + Di chuyển tất cả nội dung từ thư mục con đến vị trí đã chọn + + + Khử thư mục con + + + Khử thư mục con + + + Tùy chọn này sẽ di chuyển tất cả nội dung từ thư mục con đến vị trí đã chọn. Thao tác có hiệu lực vĩnh viễn và không thể hoàn tác. Đây là tính năng thử nghiệm và bằng việc sử dụng, bạn đã hiểu các rủi ro và đồng ý miễn trừ trách nhiệm của đội ngũ Files khi xảy ra mất mát dữ liệu. + + + Hiện tùy chọn khử thư mục con + Chọn tệp và thư mục khi di chuột lên - Bạn có chắc chắn muốn khôi phục tất cả khoản mục từ Thùng rác không? + Bạn có chắc chắn muốn khôi phục tất cả khoản mục từ thùng rác không? Khôi phục tất cả khoản mục - Bạn có muốn khôi phục {0} khoản mục đã chọn không? + Bạn có muốn khôi phục {0, plural, other {{0} khoản mục}} đã chọn không? Khôi phục các khoản mục đã chọn @@ -2060,6 +2129,12 @@ Mật khẩu tệp nén + + Mã hóa + + + {0} (đã phát hiện) + Đường dẫn @@ -2102,9 +2177,24 @@ Kích cỡ chia nhỏ - + Không chia nhỏ + + Tự động + + + Kích cỡ từ điển + + + Kích cỡ từ + + + Mức sử dụng bộ nhớ ước tính: {0} + + + Bộ nhớ khả dụng: {0} + CD @@ -2159,8 +2249,14 @@ Chỉnh sửa tệp cài đặt - - Có gì mới + + Mở tệp cài đặt trong trình soạn thảo mặc định + + + Nội dung bản cập nhật + + + Mở nội dung bản cập nhật Tạo lối tắt ở vị trí này yêu cầu quyền quản trị viên @@ -2199,7 +2295,7 @@ Truy cập nhanh - Nhập thông tin đăng nhập của bạn để kết nối đến: {0} + Nhập chứng danh của bạn để kết nối đến: {0} Nhập thông tin đăng nhập mạng @@ -2241,25 +2337,22 @@ Tùy chọn menu ngữ cảnh - Hiện phần mở rộng tệp - - - Hiện khoản mục bị ẩn + Phần mở rộng tệp - Định dạng... + Định dạng Trợ giúp - Bật/Tắt toàn màn hình + Toàn màn hình Bạn có chắc chắn muốn xóa nhãn này không? - Phát tất cả + Phát Chiều cao @@ -2319,100 +2412,115 @@ Tăng kích thước - Chuyển đổi hướng sắp xếp + Hướng sắp xếp Tên không hợp lệ - Tên không được để trống hoặc bắt đầu/kết thúc với một dấu chấm. + Tên không được để trống hoặc bắt đầu/kết thúc bằng dấu chấm. Video - Mở cửa sổ xem trước + Cửa sổ xem trước - Bật/Tắt chế độ thu gọn + Bật tắt chế độ thu gọn Mở trang trợ giúp trực tuyến trong trình duyệt - Chuyển đổi bật/tắt toàn màn hình + Bật hoặc tắt chế độ toàn màn hình - Mở cửa sổ dạng thu gọn + Mở chế độ thu gọn - Thoát cửa sổ dạng thu gọn + Thoát chế độ thu gọn - Chuyển đổi bật/tắt cửa sổ dạng thu gọn + Bật hoặc tắt chế độ thu gọn - Đặt tiêu điểm vào hộp tìm kiếm + Tìm kiếm nội dung trong thanh kết hợp - Chuyển đổi ẩn/hiện khoản mục bị ẩn + Ẩn hoặc hiện các tệp bị ẩn + + + Ẩn hoặc hiện tệp có tên bắt đầu bằng dấu chấm - Chuyển đổi ẩn/hiện phần mở rộng tệp + Ẩn hoặc hiện phần mở rộng tệp - Chuyển đổi ẩn/hiện ngăn xem trước để xem nhanh nội dung tệp + Ẩn hoặc hiện ngăn xem trước để xem nhanh nội dung tệp - Chuyển đổi ẩn/hiện thanh bên + Ẩn hoặc hiện thanh bên - Sao chép khoản mục vào bộ nhớ tạm + Sao chép {0, plural, other {khoản mục}} đã chọn - Sao chép đường dẫn khoản mục đã chọn vào bộ nhớ tạm + Sao chép đường dẫn thư mục hiện tại + + + Sao chép đường dẫn khoản mục đã chọn + + + Sao chép đường dẫn khoản mục đã chọn kèm dấu ngoặc kép - Sao chép đường dẫn khoản mục đã chọn kèm dấu ngoặc kép vào bộ nhớ tạm + Sao chép đường dẫn thư mục hiện tại kèm dấu ngoặc kép - Chuyển khoản mục vào bộ nhớ tạm + Cắt {0, plural, other {khoản mục}} đã chọn - Dán khoản mục từ bộ nhớ tạm vào thư mục hiện tại + Dán khoản mục vào thư mục hiện tại + + + Dán khoản mục vào thư mục hiện tại dưới dạng lối tắt - Dán khoản mục từ bộ nhớ tạm vào thư mục đã chọn + Dán khoản mục vào thư mục đã chọn + + + Dán vào thư mục đã chọn - Xóa khoản mục + Xóa bỏ {0, plural, other {khoản mục}} đã chọn Tạo thư mục mới - Tạo lối tắt mới đến khoản mục đã chọn + Tạo {0, plural, other {lối tắt}} đến {0, plural, other {khoản mục}} đã chọn Tạo lối tắt mới đến khoản mục bất kỳ - Dọn sạch thùng rác + Dọn sạch thùng rác Mở menu "Định dạng ổ lưu trữ" cho khoản mục đã chọn - Khôi phục khoản mục đã chọn từ thùng rác + Khôi phục {0, plural, other {khoản mục}} đã chọn từ thùng rác Khôi phục tất cả khoản mục từ thùng rác - Mở khoản mục + Mở {0, plural, other {khoản mục}} - Mở khoản mục bằng ứng dụng đã chọn + Mở {0, plural, other {khoản mục}} bằng ứng dụng đã chọn Mở thư mục nguồn của khoản mục đã tìm được @@ -2427,34 +2535,34 @@ Chọn tất cả khoản mục - Đảo lựa chọn + Đảo lựa chọn khoản mục - Bỏ lựa chọn + Hủy chọn khoản mục - Chuyển đổi chọn/bỏ chọn khoản mục đã thao tác gần nhất + Hoán đổi lựa chọn cuối - Chia sẻ tệp đã chọn với người khác + Chia sẻ {0, plural, other {khoản mục}} với người khác - Ghim mục vào menu Bắt đầu + Ghim {0, plural, other {khoản mục}} vào menu Bắt đầu - Bỏ ghim mục khỏi menu Bắt đầu + Bỏ ghim {0, plural, other {khoản mục}} khỏi menu Bắt đầu - Ghim thư mục vào thanh bên + Ghim {0, plural, other {thư mục}} vào thanh bên - Bỏ ghim thư mục khỏi thanh bên + Bỏ ghim {0, plural, other {thư mục}} khỏi thanh bên Đặt hình ảnh đã chọn làm hình nền màn hình chính - Đặt hình ảnh đã chọn để trình chiếu trên màn hình chính + Dùng hình ảnh đã chọn để trình chiếu trên màn hình chính Đặt hình ảnh đã chọn làm hình nền màn hình khóa @@ -2462,14 +2570,23 @@ Đặt hình ảnh đã chọn làm hình nền ứng dụng + + Cài đặt phông chữ + + + Cài đặt trình điều khiển + + + Cài đặt chứng chỉ + - Cài đặt phông chữ đã chọn + Cài đặt {0, plural, other {phông chữ}} đã chọn - Cài đặt trình điều khiển bằng tệp inf đã chọn + Cài đặt {0, plural, other {trình điều khiển}} bằng {0, plural, other {tệp}} inf đã chọn - Cài đặt chứng chỉ đã chọn + Cài đặt {0, plural, other {chứng chỉ}} đã chọn Chạy ứng dụng đã chọn với quyền quản trị viên @@ -2484,28 +2601,28 @@ Khởi chạy bản xem trước trong cửa sổ bật lên - Tạo tệp nén từ khoản mục đã chọn + Tạo tệp nén từ {0, plural, other {khoản mục}} đã chọn - Tạo tệp nén 7z ngay lập tức từ khoản mục đã chọn + Tạo tệp nén 7z từ {0, plural, other {khoản mục}} đã chọn - Tạo tệp nén zip ngay lập tức từ khoản mục đã chọn + Tạo tệp nén zip từ {0, plural, other {khoản mục}} đã chọn - Giải nén tệp đã chọn vào thư mục bất kỳ + Giải nén {0, plural, other {tệp}} đã chọn vào thư mục bất kỳ - Giải nén tệp đã chọn vào thư mục hiện tại + Giải nén {0, plural, other {tệp}} đã chọn vào thư mục hiện tại - Giải nén tệp đã chọn vào thư mục mới + Giải nén {0, plural, other {tệp}} đã chọn vào thư mục mới - Xoay hình ảnh đã chọn sang trái + Xoay trái {0, plural, other {hình ảnh}} đã chọn - Xoay hình ảnh đã chọn sang phải + Xoay phải {0, plural, other {hình ảnh}} đã chọn Mở trang cài đặt @@ -2525,8 +2642,8 @@ Chuyển sang bố cục chi tiết - - Chuyển sang bố cục lát + + Chuyển sang bố cục thẻ Chuyển sang bố cục danh sách @@ -2541,7 +2658,7 @@ Chuyển sang bố cục lưới - Chuyển sang bố cục dạng cột + Chuyển sang bố cục cột Tự động lựa chọn bố cục phù hợp @@ -2625,13 +2742,13 @@ Chuyển đổi hướng sắp xếp nhóm - Mở thẻ mới + Mở thẻ mới - Điều hướng lùi về trong lịch sử duyệt + Điều hướng lùi về - Điều hướng tiến lên trong lịch sử duyệt + Điều hướng tiến lên Điều hướng về thư mục nguồn @@ -2660,8 +2777,11 @@ Đóng các thẻ khác ngoài thẻ đã chọn + + Đóng tất cả các thẻ bao gồm thẻ hiện tại + - Mở lại thẻ đã đóng + Mở lại thẻ đã đóng gần đây Chuyển đến thẻ trước đó @@ -2682,7 +2802,7 @@ Đặt tiêu điểm vào ngăn còn lại - Bật/Tắt thanh bên + Bật tắt thanh bên Alt @@ -2887,13 +3007,13 @@ Chưa gắn nhãn - Chuyển đến thẻ trước đó + Thẻ trước đó - Chuyển đến thẻ tiếp theo + Thẻ tiếp theo - Đóng thẻ hiện tại + Đóng thẻ Chỉnh sửa đường dẫn @@ -2923,7 +3043,7 @@ Hiện hộp kiểm khi chọn khoản mục - Đặt tiêu điểm vào thanh đường dẫn + Chỉnh sửa đường dẫn trong thanh kết hợp Tạo khoản mục mới @@ -2938,7 +3058,7 @@ Xóa vĩnh viễn - Xóa vĩnh viễn khoản mục + Xóa vĩnh viễn {0, plural, other {khoản mục}} đã chọn Phát các tệp phương tiện đã chọn @@ -2965,10 +3085,10 @@ Ngày - Chuyển đổi ẩn/hiện đơn vị khi nhóm theo ngày tháng + Ẩn hoặc hiện đơn vị khi nhóm theo ngày tháng - Ẩn/Hiện đơn vị nhóm + Bật tắt đơn vị nhóm Năm @@ -3025,7 +3145,16 @@ Đưa các thay đổi của tôi vào '{0}' - Bạn có những thay đổi chưa được phê duyệt ở nhánh này. Bạn muốn làm gì với chúng? + Bạn có các thay đổi chưa commit ở nhánh này. Bạn muốn làm gì với các thay đổi này? + + + Bạn hiện có merge với conflict chưa giải quyết. Vui lòng thực hiện giải quyết conflict hoặc bỏ merge để chuyển nhánh. + + + Bỏ merge và chuyển sang '{0}' + + + Ở lại '{0}' và giải quyết conflict Chuyển nhánh @@ -3055,14 +3184,20 @@ Chuyển sang nhánh mới - Tạo thư mục với khoản mục đã chọn + Tạo thư mục từ {0, plural, other {khoản mục}} đã chọn Thuộc tính + + Mở bảng thuộc tính gốc + Mở cửa sổ thuộc tính + + Mở cửa sổ thuộc tính gốc của File Explorer + Cục bộ @@ -3081,6 +3216,9 @@ Chạy git fetch + + Clone kho lưu trữ Git + Chạy git pull @@ -3104,13 +3242,13 @@ Tác giả - Ngày phê duyệt + Ngày commit - Ghi chú phê duyệt + Mô tả commit - SHA phê duyệt + SHA commit Git @@ -3125,7 +3263,7 @@ Mica Alt - Kết cấu nền + Nền Màu đơn @@ -3140,13 +3278,13 @@ Chạy git push - Đồng bộ + Sync - Chạy git pull sau đó là git push + Chạy git pull sau đó chạy git push - {0} phê duyệt ra / {1} phê duyệt tới + {0} outgoing commit / {1} incoming commit Kết nối với GitHub @@ -3157,17 +3295,17 @@ Files hiện không thể truy cập GitHub. - - Mở thư mục trong VS Code + + Mở thư mục trong {0} - - Mở thư mục hiện tại trong Visual Studio Code + + Mở thư mục hiện tại trong {0} - - Mở kho lưu trữ trong VS Code + + Mở kho lưu trữ trong {0} - - Mở gốc của kho lưu trữ Git trong Visual Studio Code + + Mở nguồn của kho lưu trữ Git trong {0} Sao chép mã @@ -3194,7 +3332,7 @@ Khởi tạo kho lưu trữ - Khởi tạo một kho lưu trữ Git + Khởi tạo thư mục hiện tại dưới dạng kho lưu trữ Git Tên kho lưu trữ từ xa @@ -3209,13 +3347,13 @@ Sắp xếp khoản mục theo đường dẫn - Mở thư mục trong ngăn mới + Mở thư mục đã chọn trong ngăn mới - Mở thư mục trong thẻ mới + Mở thư mục đã chọn trong thẻ mới - Mở thư mục trong cửa sổ mới + Mở thư mục đã chọn trong cửa sổ mới Mở tất cả @@ -3243,7 +3381,7 @@ Bảng lệnh - Mở bảng lệnh + Mở bảng lệnh trong thanh kết hợp Bắt đầu trong: @@ -3373,6 +3511,9 @@ Đang xử lý khoản mục... + + Đang quét tìm khoản mục... + Tốc độ: @@ -3381,71 +3522,139 @@ Shown in a StatusCenter card. - Đã hủy thao tác nén {0} mục từ "{1}" đến "{2}" + Đã hủy nén {0} mục từ "{1}" đến "{2}" Shown in a StatusCenter card. - Đã nén {0} mục đến "{1}" + Đã nén {0, plural, other {# khoản mục}} vào "{1}" Shown in a StatusCenter card. - Đã nén {0} mục từ "{1}" đến "{2}" + Đã nén {0, plural, other {# khoản mục}} từ "{1}" vào "{2}" Shown in a StatusCenter card. - Xảy ra lỗi khi nén {0} mục đến "{1}" + Đã xảy ra lỗi khi nén {0, plural, other {# khoản mục}} vào "{1}" Shown in a StatusCenter card. - Không thể nén {0} mục từ "{1}" đến "{2}" + Không thể nén {0, plural, other {# khoản mục}} từ "{1}" vào "{2}" Shown in a StatusCenter card. - Đang nén {0} mục đến "{1}" + Đang nén {0, plural, other {# khoản mục}} vào "{1}" Shown in a StatusCenter card. - Đang thực hiện nén {0} mục từ "{1}" đến "{2}" + Đang nén {0, plural, other {# khoản mục}} từ "{1}" vào "{2}" + Shown in a StatusCenter card. + + + Đã hủy clone {0} vào "{1}" + Shown in a StatusCenter card. + + + Đã hủy clone {0} từ "{1}" vào "{2}" + Shown in a StatusCenter card. + + + Đã clone "{0}" vào "{1}" + Shown in a StatusCenter card. + + + Đã clone {0} từ "{1}" vào "{2}" + Shown in a StatusCenter card. + + + Đã xảy ra lỗi khi clone "{0}" vào "{1}" + Shown in a StatusCenter card. + + + Không thể clone {0} từ "{1}" vào "{2}" + Shown in a StatusCenter card. + + + Đang clone "{0}" vào "{1}" + Shown in a StatusCenter card. + + + Đang clone {0} từ "{1}" vào "{2}" + Shown in a StatusCenter card. + + + Đã hủy cài đặt {0} phông chữ + Shown in a StatusCenter card. + + + Đã hủy cài đặt {0, plural, other {# phông chữ}} từ "{1}" + Shown in a StatusCenter card. + + + Đã cài đặt {0, plural, other {# phông chữ}} + Shown in a StatusCenter card. + + + Đã cài đặt {0, plural, other {# phông chữ}} từ "{1}" + Shown in a StatusCenter card. + + + Đã xảy ra lỗi khi cài đặt {0, plural, other {# phông chữ}} + Shown in a StatusCenter card. + + + Không thể cài đặt {0, plural, other {# phông chữ}} từ "{1}" + Shown in a StatusCenter card. + + + Đang cài đặt {0, plural, other {# phông chữ}} + Shown in a StatusCenter card. + + + Đang cài đặt {0, plural, other {# phông chữ}} từ "{1}" Shown in a StatusCenter card. - Đã hủy sao chép {0} mục đến "{1}" + Đã hủy sao chép {0, plural, other {# khoản mục}} đến "{1}" Shown in a StatusCenter card. - Đã hủy thao tác sao chép {0} mục từ "{1}" đến "{2}" + Đã hủy sao chép {0, plural, other {# khoản mục}} từ "{1}" đến "{2}" Shown in a StatusCenter card. - Đã sao chép {0} mục đến "{1}" + Đã sao chép {0, plural, other {# khoản mục}} đến "{1}" Shown in a StatusCenter card. - Đã sao chép {0} mục từ "{1}" đến "{2}" + Đã sao chép {0, plural, other {# khoản mục}} từ "{1}" đến "{2}" Shown in a StatusCenter card. - Xảy ra lỗi khi sao chép {0} mục đến "{1}" + Đã xảy ra lỗi khi sao chép {0, plural, other {# khoản mục}} đến "{1}" Shown in a StatusCenter card. - Không thể sao chép {0} mục từ "{1}" đến "{2}" + Không thể sao chép {0, plural, other {# khoản mục}} từ "{1}" đến "{2}" Shown in a StatusCenter card. - Đang sao chép {0} mục đến "{1}" + Đang sao chép {0, plural, other {# khoản mục}} đến "{1}" Shown in a StatusCenter card. - Đang thực hiện sao chép {0} mục từ "{1}" đến "{2}" + Đang sao chép {0, plural, other {# khoản mục}} từ "{1}" đến "{2}" Shown in a StatusCenter card. + + Đã tìm thấy {0, plural, other {# khoản mục}} + Shown in a StatusCenter card during file discovery phase. + Đã hủy giải nén "{0}" vào "{1}" Shown in a StatusCenter card. - Đã hủy thao tác giải nén "{0}" từ "{1}" vào "{2}" + Đã hủy giải nén "{0}" từ "{1}" vào "{2}" Shown in a StatusCenter card. @@ -3457,7 +3666,7 @@ Shown in a StatusCenter card. - Xảy ra lỗi khi giải nén "{0}" vào "{1}" + Đã xảy ra lỗi khi giải nén "{0}" vào "{1}" Shown in a StatusCenter card. @@ -3469,59 +3678,59 @@ Shown in a StatusCenter card. - Đang thực hiện giải nén "{0}" từ "{1}" vào "{2}" + Đang giải nén "{0}" từ "{1}" vào "{2}" Shown in a StatusCenter card. - Đã hủy xóa {0} mục khỏi "{1}" + Đã hủy xóa {0, plural, other {# khoản mục}} khỏi "{1}" Shown in a StatusCenter card. - Đã xóa {0} mục khỏi "{1}" + Đã xóa {0, plural, other {# khoản mục}} khỏi "{1}" Shown in a StatusCenter card. - Xảy ra lỗi khi xóa {0} mục khỏi "{1}" + Đã xảy ra lỗi khi xóa {0, plural, other {# khoản mục}} khỏi "{1}" Shown in a StatusCenter card. - Không thể xóa {0} mục khỏi "{1}" + Không thể xóa {0, plural, other {# khoản mục}} khỏi "{1}" Shown in a StatusCenter card. - Đang xóa {0} mục khỏi "{1}" + Đang xóa {0, plural, other {# khoản mục}} khỏi "{1}" Shown in a StatusCenter card. - Đã hủy di chuyển {0} mục đến "{1}" + Đã hủy di chuyển {0, plural, other {# khoản mục}} đến "{1}" Shown in a StatusCenter card. - Đã hủy thao tác di chuyển {0} mục từ "{1}" đến "{2}" + Đã hủy di chuyển {0, plural, other {# khoản mục}} từ "{1}" đến "{2}" Shown in a StatusCenter card. - Đã di chuyển {0} mục đến "{1}" + Đã di chuyển {0, plural, other {# khoản mục}} đến "{1}" Shown in a StatusCenter card. - Đã di chuyển {0} mục từ "{1}" đến "{2}" + Đã di chuyển {0, plural, other {# khoản mục}} từ "{1}" đến "{2}" Shown in a StatusCenter card. - Đang di chuyển {0} mục đến "{1}" + Đang di chuyển {0, plural, other {# khoản mục}} đến "{1}" Shown in a StatusCenter card. - Đang thực hiện di chuyển {0} mục từ "{1}" đến "{2}" + Đang di chuyển {0, plural, other {# khoản mục}} từ "{1}" đến "{2}" Shown in a StatusCenter card. - Xảy ra lỗi khi di chuyển {0} mục đến "{1}" + Đã xảy ra lỗi khi di chuyển {0, plural, other {# khoản mục}} đến "{1}" Shown in a StatusCenter card. - Không thể di chuyển {0} mục từ "{1}" đến "{2}" + Không thể di chuyển {0, plural, other {# khoản mục}} từ "{1}" đến "{2}" Shown in a StatusCenter card. @@ -3537,7 +3746,7 @@ Shown in a StatusCenter card. - Xảy ra lỗi khi dọn sạch thùng rác + Đã xảy ra lỗi khi dọn sạch thùng rác Shown in a StatusCenter card. @@ -3549,7 +3758,7 @@ Shown in a StatusCenter card. - {0}/{1} khoản mục đã được xử lý + {0}/{1, plural, other {# khoản mục}} đã được xử lý Shown in a StatusCenter card. Used as "8/20 items processed" @@ -3559,7 +3768,7 @@ Không có tác vụ đang thực hiện nào - Tùy chọn mở Files khi khởi động Windows không khả dụng do các cài đặt hệ thống hoặc group policy của bạn. Để kích hoạt lại, hãy mở mục khởi động trong Trình quản lý Tác vụ. + Tùy chọn mở Files khi khởi động Windows không khả dụng do các cài đặt hệ thống hoặc Group Policy của bạn. Để kích hoạt lại, hãy mở mục khởi động trong Trình quản lý Tác vụ. Không thể khôi phục khoản mục từ thùng rác @@ -3567,6 +3776,9 @@ Không thể đặt làm hình nền + + Không thể mở tệp cài đặt + Xóa nhánh Git @@ -3592,7 +3804,7 @@ Đã xảy ra lỗi khi gắn nhãn - Giải nén tệp đã chọn vào thư mục hiện tại nếu tệp chỉ chứa một khoản mục, hoặc vào thư mục mới nếu tệp chứa nhiều khoản mục + Giải nén {0, plural, other {tệp}} tại đây nếu chỉ chứa một khoản mục, hoặc vào thư mục mới nếu chứa nhiều khoản mục Giải nén tại đây (Thông minh) @@ -3657,9 +3869,6 @@ Câu hỏi & thảo luận - - Các lựa chọn kích thước bổ sung hiện chưa khả dụng trong bố cục lát. - Thu gọn Used to describe layout sizes @@ -3731,16 +3940,16 @@ Khôi phục mặc định - Chọn một tác vụ + Chọn tác vụ Bạn có chắc chắn muốn khôi phục tổ hợp phím mặc định không? Bạn sẽ không thể hoàn tác sau khi tiếp tục. - Tổ hợp phím này đã được sử dụng, vui lòng chọn một tổ hợp phím khác để tiếp tục. + Tổ hợp phím này đã được sử dụng, vui lòng chọn tổ hợp khác để tiếp tục. - Không thể sử dụng tổ hợp phím này, vui lòng chọn một tổ hợp phím khác. + Không thể sử dụng tổ hợp phím này, vui lòng chọn tổ hợp khác. Tùy chỉnh @@ -3821,6 +4030,10 @@ Tệp ICO This is the friendly name for ICO files. + + Tệp ICL + This is the friendly name for ICL files. + Tệp Zip This is the friendly name for ZIP files. @@ -3829,6 +4042,10 @@ Tệp Bitmap This is the friendly name for bitmap files. + + Tệp hình ảnh + This is the friendly name for image files. + Num @@ -3855,22 +4072,22 @@ Hiện thanh công cụ - Setting that controls if the Toolbar is shown in the main view + Setting that controls if the toolbar is shown in the main view Menu tác vụ - - Thêm ngăn dọc + + Chia dọc - Thêm ngăn theo chiều dọc + Chia ngăn theo chiều dọc - - Thêm ngăn ngang + + Chia ngang - - Thêm ngăn theo chiều ngang + + Chia ngăn theo chiều ngang Bố trí dọc @@ -3884,8 +4101,8 @@ Bố trí ngăn theo chiều ngang - - Thêm ngăn + + Chia ngăn Hiện nút menu tác vụ trên thanh tiêu đề @@ -3893,8 +4110,11 @@ Bố trí ngăn - - Bố trí ngăn mặc định + + Hướng chia ngăn mặc định + + + Chế độ hai ngăn Ngang @@ -3902,4 +4122,259 @@ Dọc + + Hiện biểu tượng Files trong khay hệ thống + + + Luồng CPU + + + Điều hướng về trang chủ + + + Thanh công cụ + + + ID người dùng + + + Đổi tên hàng loạt + + + Nén nội dung + + + Hiện tùy chọn tạo luồng dữ liệu phụ + + + Tạo luồng dữ liệu phụ + + + Tạo luồng dữ liệu phụ cho {0, plural, other {khoản mục}} đã chọn + + + Nhập tên luồng dữ liệu + + + Đã xảy ra lỗi khi tạo luồng dữ liệu phụ + + + Lưu ý rằng luồng dữ liệu phụ chỉ có thể hoạt động trên ổ lưu trữ ở định dạng NTFS. + + + Luồng dữ liệu phụ hiện đang ẩn + + + Bạn có muốn hiển thị luồng dữ liệu phụ không? Để sửa đổi cài đặt này, hãy mở phần Tệp & thư mục trong giao diện cài đặt. + + + Quản lý nhãn + + + Khả dụng + + + Tổng + + + Luôn đặt tiêu điểm vào thẻ mới + + + Bật tắt ngăn tạm + + + Ẩn hoặc hiện ngăn tạm + + + Hiện nút ẩn hoặc hiện ngăn tạm trên thanh địa chỉ + + + Ngăn tạm + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + Loại bỏ tất cả + + + Loại bỏ khỏi ngăn tạm + + + Thêm vào ngăn tạm + Tooltip that displays when dragging items to the Shelf Pane + + + Nhập hash để so sánh + Placeholder that appears in the compare hash text box + + + Khớp hàm {0} + Appears when two compared hashes match, e.g. "Matches SHA256" + + + Không khớp với hàm nào + Appears when two compared hashes don't match + + + Đường dẫn hoặc bí danh + + + Đường dẫn không hợp lệ + + + Kiểm thử + + + Không tìm thấy {0}. Vui lòng kiểm tra cài đặt của bạn và thử lại. + + + Không tìm thấy IDE đã cấu hình + + + Mở cài đặt + + + Visual Studio Code + + + Nhập đường dẫn hoặc bí danh khởi chạy + + + Vui lòng nhập tên của IDE + + + Clone kho lưu trữ + Clone repo dialog title + + + Clone + Primary action button in the clone repo dialog + + + URL kho lưu trữ + URL textbox header in the clone repo dialog + + + Không thể clone kho lưu trữ + Cannot clone repo dialog title + + + So sánh tệp + Button that appears in file hash properties that allows the user to compare two files + + + Định dạng kích cỡ + + + Nhị phân + + + Thập phân + + + Bạn có thể thêm phân mục vào thanh bên bằng cách nhấp chuột phải rồi chọn vào mục mình muốn thêm + + + Kéo thả tệp hoặc thư mục vào đây để thao tác xuyên suốt trong các thẻ khác nhau + + + Nhập đường dẫn để mở... + + + Tìm kiếm lệnh và tính năng... + + + Tìm kiếm tệp và thư mục... + + + Trong khi thực hiện thao tác tệp + + + Hiện nút trung tâm trạng thái + + + Vòng tiến trình trong trung tâm trạng thái + Screen reader name for the status center progress ring + + + Tệp biểu tượng + This is the friendly name for a variety of different icon files. + + + Hiện nút thả đường dẫn đã ẩn đi + + + Hiện thư mục trong {0} + + + Hiện thư mục trong Trang chủ + + + Không có lệnh nào chứa {0} + + + Xem thêm + + + Lọc theo + + + Tên tệp + + + Chữ ký + + + Danh sách chữ ký + + + Cấp bởi: + + + Cấp cho: + + + Có giá trị từ: + + + Hết hạn sau: + + + Không tìm thấy chữ ký nào. + + + Hiện tùy chọn mở thư mục trong Windows Terminal + + + Mở tệp nhật ký + + + Mở tệp nhật ký trong trình soạn thảo mặc định + + + Mở vị trí tệp nhật ký trong trình quản lý tệp mặc định + + + Không thể mở tệp nhật ký + + + Hiện thanh trạng thái + + + Chỉ trong bố cục cột + + + New {0} + + + Làm mịn chuyển động cuộn trang + + + Cuộn trang + + + Hiện tùy chọn ghim vào thanh bên + + + Hiện tùy chọn ghim vào menu Bắt đầu + \ No newline at end of file diff --git a/src/Files.App/Strings/zh-Hans/Resources.resw b/src/Files.App/Strings/zh-Hans/Resources.resw index d8021fc2be62..8457ff58c419 100644 --- a/src/Files.App/Strings/zh-Hans/Resources.resw +++ b/src/Files.App/Strings/zh-Hans/Resources.resw @@ -123,9 +123,15 @@ 复制路径 + + 复制项目路径 + 复制路径(带引号) + + 复制选中项的路径(带引号) + 浏览 @@ -133,13 +139,13 @@ 大小 - 创建日期: + 创建日期: 路径 - 大小: + 大小: 占用空间: @@ -160,7 +166,7 @@ 跳过 - 全选 + 全部选择 反选 @@ -169,10 +175,10 @@ 清除选择 - 修改日期: + 修改日期: - 访问日期: + 访问日期: 清空所有项目 @@ -184,7 +190,7 @@ 搜索 - 将在此显示访问过的文件(夹) + 以前访问过的文件和文件夹将显示在此处 移除此项 @@ -208,10 +214,10 @@ 显示已知文件类型的扩展名 - 显示隐藏的文件(夹) + 显示隐藏的文件或文件夹 - 显示仅有扩展名的项 + 显示以英文句点开头的文件 外观 @@ -249,6 +255,9 @@ 粘贴 + + 粘贴为快捷方式 + 新建 @@ -291,8 +300,8 @@ 输入项目名称 - - 命名 + + 创建新 {0} 浅色 @@ -300,6 +309,9 @@ 深色 + + 使用系统设置 + 应用 @@ -424,7 +436,7 @@ 现在 - 不支持请求的操作 + 不支持该操作 {0} 可用,共 {1} @@ -495,16 +507,16 @@ {0} 中全部 - 已用空间: + 已用空间: - 可用空间: + 可用空间: - 容量: + 容量: - 文件系统: + 文件系统: 近期访问 @@ -515,9 +527,6 @@ 状态 - - Windows 默认 - 无结果 @@ -530,6 +539,9 @@ 复制到 {0} + + 克隆到 {0} + 创建快捷方式 @@ -537,19 +549,19 @@ 打开文件位置 - 参数: + 参数: - 目标位置: + 目标位置: - 快捷方式类型: + 快捷方式类型: Web 链接 - 常规 + 通用 快捷方式 @@ -570,13 +582,13 @@ 报告此问题 - 引用的项目无效或无法访问。{0}代码:{0}{1} + 引用的项目无效或无法访问。{0}代码:{0}{1} 无效项 - 版本: + 版本: 目前没有可共享的内容。 @@ -615,7 +627,7 @@ 显示更多选项 - 若要应用这些设置,需要重新启动应用程序。是否立即重启? + 一些设置需要重新启动 Files 才会应用,是否立即重启? 在 GitHub 上赞助我们 @@ -998,6 +1010,9 @@ 在“{1}”中搜索“{0}”的结果 + + “{0}”的搜索结果 + 详细信息 @@ -1026,7 +1041,7 @@ 移除 - 以双面板模式打开标签页 + 以双窗格模式打开标签页 显示“在新面板中打开” @@ -1052,9 +1067,6 @@ 详细信息 (Ctrl+Shift+1) - - 平铺 (Ctrl+Shift+2) - 删除日期 @@ -1080,10 +1092,25 @@ 显示/隐藏信息窗格 - 打开信息窗格以查看详细信息/预览窗格 + 切换详细信息/预览窗格可见性 - 打开/关闭工具栏 + 显示/隐藏工具栏 + + + 切换工具栏的可见性 + + + 显示/隐藏筛选顶栏 + + + 切换筛选顶栏可见性 + + + 切换双面板 + + + 切换双面板模式 没有预览 @@ -1134,7 +1161,7 @@ 未知 - 如果改变文件扩展名,可能会导致文件不可用。确实要更改吗? + 如果改变文件扩展名,可能会导致文件不可用。确定要更改吗? CD-ROM 驱动器 @@ -1215,7 +1242,7 @@ - 位置: + 位置: 设置为默认保存路径 @@ -1238,6 +1265,12 @@ 打开存储感知 + + 打开 Windows 设置中的“存储感知”页面 + + + 清理 + 复制 @@ -1290,10 +1323,10 @@ 用选中的项目创建文件夹 - 类型: + 类型: - 修改日期: + 修改日期: 打开方式 @@ -1380,7 +1413,7 @@ {0} 个项目 - 重新打开关闭的标签页 + 重新打开标签页 重命名 @@ -1446,13 +1479,13 @@ 你无权查看此对象的安全属性。 单击“高级权限”继续。 - 所有者: + 所有者: 未知所有者 - 解压缩 + 解压文件到 完成后打开目标文件夹 @@ -1587,7 +1620,7 @@ 用来自该对象的可继承权限条目替换所有子对象权限条目 - 关闭活动窗格 + 关闭窗格 进入精简模式 @@ -1646,15 +1679,6 @@ 主页 - - Ctrl+Shift+1 - - - Ctrl+Shift+2 - - - Ctrl+Shift+6 - 压缩文件解压完成。 @@ -1712,8 +1736,8 @@ 列视图 - - 平铺 + + 卡片 在新标签页中打开文件夹 @@ -1749,7 +1773,7 @@ 文件标签 - 通过单击打开项目 + 单击打开文件 文件夹路径 @@ -1826,6 +1850,18 @@ 注册此程序以重新启动 + + 启动时的窗口状态 + + + 正常窗口 + + + 最小化 + + + 最大化 + 以管理员身份运行 @@ -1853,6 +1889,9 @@ 此选项会修改系统注册表,并可能对你的设备产生意想不到的副作用。 继续即代表您自行承担风险。 + + 展平 (Flatten) 操作是永久性的,不推荐使用。请自行承担使用风险。 + 创建库 @@ -1866,7 +1905,7 @@ 最近使用的文件 - 在 Windows 启动时打开 Files + 开机自启动 属性 @@ -1890,13 +1929,13 @@ 字节 - 解压缩 + 解压 - 解压缩文件 + 解压文件 - 解压缩到此处 + 解压到当前文件夹 Ctrl+E @@ -1931,6 +1970,9 @@ 关闭其他选项卡 + + 关闭所有标签页 + 音乐 @@ -1959,7 +2001,7 @@ 当前无法执行移动操作。要复制此项目吗? - 隐藏项目 + 隐藏的项目 自定义 @@ -1985,11 +2027,23 @@ 行为 - - 评价 Files + + 您好! + + + 喜欢Files吗?请考虑在 Microsoft Store 中给我们写评价。 + + + 喜欢Files吗?请考虑在 Github 上支持该项目。 - - 给个好评呗 + + 赞助 + + + 为我们评分 + + + 忽略 设置为背景 @@ -2018,8 +2072,8 @@ 锁屏界面 - - 在“列”布局下,通过单击打开文件夹 + + 单击打开文件夹 打开项目 @@ -2027,6 +2081,21 @@ 压缩 + + 将子文件夹中的全部项目移动到选定位置 + + + 展平文件夹 + + + 展平 + + + 展平 (Flatten) 文件夹将把该文件夹的子文件夹中的所有项目统一放置到选定目录下。此操作是永久性的,不可撤消。 使用此实验性功能,即表示您知晓风险且 Files 团队不对任何数据丢失负责。 + + + 显示展平选项 + 鼠标悬停在项目上时自动选择 @@ -2037,7 +2106,7 @@ 还原所有项目 - 是否还原选中的 {0} 个项目? + 是否还原选中的 {0,plural,other{{0} 个项目}}? 还原选定的项目 @@ -2060,6 +2129,12 @@ 压缩文件密码 + + 编码 + + + {0} (检测到) + 路径 @@ -2102,9 +2177,24 @@ 分卷大小 - + 不分卷 + + 自动 + + + 字典大小 + + + 字号 + + + 估计内存使用率: {0} + + + 可用内存: {0} + CD @@ -2157,16 +2247,22 @@ “最近使用的文件”已在 Windows 文件管理器中停用。 - 编辑设置文件 + 编辑配置文件 + + + 在你的默认编辑器中打开配置文件 - - 新功能 + + 发行说明 + + + 打开发行说明 在此位置创建快捷方式需要管理员权限 - 您要在桌面上创建快捷方式吗? + 要改为在桌面上创建快捷方式吗? 指定的项目名称无效 @@ -2241,25 +2337,22 @@ 右键菜单选项 - 显示文件扩展名 - - - 显示隐藏的项目 + 文件扩展名 - 格式化... + 格式化 帮助 - 切换全屏 + 全屏 确定要删除此标签吗? - 播放全部 + 播放 高度 @@ -2319,7 +2412,7 @@ 增加大小 - 切换排序方向 + 排列顺序 无效名称 @@ -2331,34 +2424,37 @@ 视频 - 打开预览弹窗 + 预览弹窗 开关精简模式 - 在浏览器中打开在线帮助页面 + 在你的浏览器中打开在线帮助页面 - 切换全屏显示 + 切换全屏模式 进入精简模式 - 退出精简模式 + 进入精简模式 - 开关精简模式 + 切换精简模式 - 转到搜索框 + 在 OmniBar 中开始搜索 - 切换隐藏项目显示 + 切换隐藏的项目的可见性 + + + 切换以英文句点 (.) 开头的文件的可见性 - 切换文件扩展名显示 + 切换文件扩展名的可见性 切换到预览窗格以预览文件 @@ -2367,52 +2463,64 @@ 切换侧边栏显示 - 复制项目到剪贴板 + 复制选中的{0, plural, other {项目}} - 复制选中项的路径到剪贴板 + 复制当前文件夹的路径 + + + 复制选中项目的路径 + + + 复制选中项目的路径(带引号) - 复制选中项的路径(带引号)到剪贴板 + 复制当前目录的路径(带引号) - 剪切项目到剪贴板 + 剪切选中的{0, plural, other {项目}} - 将剪贴板中的项目粘贴到当前文件夹 + 将文件粘贴到当前文件夹 + + + 将项目在当前文件夹粘贴为快捷方式 - 将剪贴板中的项目粘贴到所选文件夹 + 将文件粘贴到选中的文件夹 + + + 粘贴到选中的文件夹 - 删除项目 + 删除选中的{0, plural, other {项目}} 创建新文件夹 - 创建所选项目的快捷方式 + 创建新的指向选中{0, plural, other {项目}}的{0, plural, other {快捷方式}} 创建指向项目的快捷方式 - 清空回收站 + 清空回收站的内容 打开所选项目的“格式化驱动器”菜单 - 从回收站中还原所选项目 + 从回收站还原选中的{0, plural, other {项目}} 从回收站中还原所有项目 - 打开项目 + 打开{0, plural, other {项目}} - 用所选程序打开项目 + 用选定的应用程序打开{0, plural, other {项目}} 打开搜索项所在的文件夹 @@ -2427,28 +2535,28 @@ 选择所有项目 - 反选项目 + 反向选中项目 - 清除项目选择 + 清除选中项目 切换项目选择 - 共享所选文件 + 与其他人共享选中的{0, plural, other {文件}} - 固定项目到开始菜单 + 固定{0, plural, other {项目}}到“开始”菜单 - 从开始菜单取消固定 + 从“开始”菜单取消固定{0, plural, other {项目}} - 固定文件夹到侧边栏 + 固定{0, plural, other {文件夹}}到侧边栏 - 从侧边栏取消固定文件夹 + 从侧边栏取消固定{0, plural, other {文件夹}} 设置所选图片为桌面背景 @@ -2462,14 +2570,23 @@ 设置所选图片为此应用程序的背景 + + 安装字体 + + + 安装驱动程序 + + + 安装证书 + - 安装所选字体 + 安装选中的{0, plural, other {字体}} - 使用所选的 .inf 文件安装驱动程序 + 从选中的 inf {0, plural, other {文件}}安装{0, plural, other {驱动程序}} - 安装所选证书 + 安装选中的{0, plural, other {证书}} 以管理员身份运行所选程序 @@ -2484,28 +2601,28 @@ 在弹出窗口中打开预览 - 创建包含所选项目的压缩文件 + 创建一个压缩文件并放入当前选中的{0, plural, other {项目}} - 创建包含所选项目的 7z 压缩文件 + 创建一个 7z 压缩文件并放入当前选中的{0, plural, other {项目}} - 创建包含所选项目的 zip 压缩文件 + 创建一个 zip 压缩文件并放入当前选中的{0, plural, other {项目}} - 将所选压缩文件解压到文件夹 + 解压缩选中的{0, plural, other {压缩文件}}到任意文件夹 - 将所选压缩文件解压到当前文件夹 + 解压缩选中的{0, plural, other {压缩文件}}到当前文件夹 - 将所选压缩文件解压到新文件夹 + 解压缩选中的{0, plural, other {压缩文件}}到新文件夹 - 向左旋转所选图像 + 向左旋转选中的{0, plural, other {图像}} - 向右旋转所选图像 + 向右旋转选中的{0, plural, other {图像}} 打开设置页 @@ -2523,10 +2640,10 @@ 增加当前视图中项目的大小 - 切换至详细信息视图 + 切换到详细信息视图 - - 切换至平铺视图 + + 切换到卡片视图 切换到列表视图 @@ -2541,7 +2658,7 @@ 切换到网格视图 - 切换至列视图 + 切换到列视图 自适应切换视图 @@ -2628,10 +2745,10 @@ 打开新标签页 - 在导航历史中向后导航 + 向后导航 - 在导航历史中向前导航 + 向前导航 导航至上一级目录 @@ -2660,8 +2777,11 @@ 关闭除所选标签页以外的标签页 + + 关闭所有标签页(包括当前标签页) + - 打开上一个关闭的标签页 + 打开最近关闭的标签页 移到上一标签页 @@ -2679,7 +2799,7 @@ 切换焦点到其他窗格 - 切换焦点到非活动窗格 + 切换焦点到另一窗格 打开/关闭侧边栏 @@ -2887,13 +3007,13 @@ 无标签 - 移到上一标签页 + 上一个标签页 - 移到下一标签页 + 下一个标签页 - 关闭当前标签页 + 关闭标签页 编辑路径 @@ -2908,7 +3028,7 @@ 无效位置 - 确定要复制这些文件而不同时复制其属性吗? + 确定要复制这些文件而不复制其属性吗? 这些文件的某些属性不能复制到新位置 @@ -2917,13 +3037,13 @@ 这些文件的某些属性不能移动到新位置 - 确定要移动这些文件而不同时移动其属性吗? + 确定要移动这些文件而不移动其属性吗? 选择项目时显示复选框 - 移动焦点到路径栏 + 在 OmniBar 中编辑路径 创建新项目 @@ -2938,7 +3058,7 @@ 永久删除 - 永久删除项目 + 永久删除选中的{0, plural, other {项目}} 播放选中的媒体文件 @@ -2950,7 +3070,7 @@ 重做上一次文件操作 - 位置: + 位置: 按月份分组 @@ -2974,7 +3094,7 @@ - 按日期单位分组 + 按日期分组时的单位 按创建日期分组 @@ -3027,6 +3147,15 @@ 你在此分支上有未提交的更改。如何处理这些更改? + + 您当前存在未解决冲突的合并操作。请解决冲突或取消合并以切换分支。 + + + 中止合并并切换到'{0}' + + + 保持'{0}'并解决冲突 + 切换分支 @@ -3055,14 +3184,20 @@ 切换到新分支 - 用当前选中的项创建文件夹 + 创建一个文件夹并放入当前选中的{0, plural, other {项目}} - 查看属性 + 属性 + + + 打开文件资源管理器属性 打开属性窗口 + + 打开文件资源管理器属性窗口 + 本地 @@ -3070,7 +3205,7 @@ 远程 - 在 Crowdin 上翻译此应用 + 在 Crowdin 上协助翻译此应用 抓取 @@ -3081,6 +3216,9 @@ 运行 git fetch + + 克隆一个 git 存储库 + 运行 git pull @@ -3116,13 +3254,13 @@ Git - Acrylic + 亚克力 - Mica + 云母 - Mica Alt + 云母 Alt 背景材质 @@ -3152,22 +3290,22 @@ 连接到 GitHub - 若要开始使用 GitHub 存储库,请在下方的链接中输入以下验证码。 + 若要开始使用 GitHub 存储库,请点击下方的链接,并输入以下验证码。 Files 目前无法访问 GitHub。 - - 在 VS Code 中打开文件夹 + + 在 {0} 中打开文件夹 - - 在 Visual Studio Code 中打开当前目录 + + 在 {0} 中打开当前目录 - - 在 VS Code 中打开仓库 + + 在 {0} 中打开存储库 - - 在 Visual Studio Code 中打开 Git 仓库的根目录 + + 在 {0} 中打开Git repo 的根目录 复制代码 @@ -3188,13 +3326,13 @@ 无法显示当前所有者。 - 针不戳!你现在已获得授权。 + 真不错!你现在已获得授权。 初始化存储库 - 初始化 Git 存储库 + 将当前文件夹初始化为 git 仓库 远程仓库名称 @@ -3209,13 +3347,13 @@ 按路径排序 - 在新面板中打开目录 + 在新窗格中打开选中的目录 - 在新标签页中打开目录 + 在新标签页中打开选中的目录 - 在新窗口中打开目录 + 在新窗口中打开选中的目录 全部打开 @@ -3243,10 +3381,10 @@ 命令面板 - 打开命令面板 + 在 OmniBar 中打开命令面板 - 起始位置: + 起始位置: 打开 BitLocker @@ -3289,7 +3427,7 @@ One of the custom color themes - 亮酷蓝色 + 浅蓝色 One of the custom color themes @@ -3309,15 +3447,15 @@ One of the custom color themes - 浅薄荷色 + 天青色 One of the custom color themes - 现代红色 + 鸢红色 One of the custom color themes - 亮橙色 + 浅橙色 One of the custom color themes @@ -3329,7 +3467,7 @@ One of the custom color themes - 亮玫红色 + 玫瑰色 One of the custom color themes @@ -3341,7 +3479,7 @@ One of the custom color themes - 浅紫红色 + 菖蒲色 One of the custom color themes @@ -3352,7 +3490,7 @@ 清除已完成的条目 - 名称: + 名称: 已处理 {0},共 {1} @@ -3373,8 +3511,11 @@ 正在处理项目... + + 正在发现项目... + - 速度: + 速度: 已取消将 {0} 个项目压缩到“{1}” @@ -3385,61 +3526,129 @@ Shown in a StatusCenter card. - 已将 {0} 个项目压缩到“{1}” + 已将{0, plural, other {#个项目}}压缩至“{1}” Shown in a StatusCenter card. - 已将 {0} 个项目从“{1}”压缩到“{2}” + 已将{0, plural, other {#个项目}}从“{1}”压缩到“{2}” Shown in a StatusCenter card. - 将 {0} 个项目压缩到“{1}”时出错 + 将{0, plural, other {#个项目}}压缩到“{1}”时出错 Shown in a StatusCenter card. - 无法将 {0} 个项目从“{1}”压缩到“{2}” + 无法将{0, plural, other {#个项目}}从“{1}”压缩到“{2}” Shown in a StatusCenter card. - 正在将 {0} 个项目压缩到“{1}” + 正在将{0, plural, other {#个项目}}压缩到“{1}” Shown in a StatusCenter card. - 正在将 {0} 个项目从“{1}”压缩到“{2}” + 正在将{0, plural, other {#个项目}}从“{1}”压缩到“{2}” + Shown in a StatusCenter card. + + + 已取消将 {0} 克隆到“{1}” + Shown in a StatusCenter card. + + + 已取消将 {0} 从“{1}”克隆到“{2}” + Shown in a StatusCenter card. + + + 已将“{0}”克隆到“{1}” + Shown in a StatusCenter card. + + + 已将“{0}”从“{1}”克隆到“{2}” + Shown in a StatusCenter card. + + + 将“{0}”克隆到“{1}”时出错 + Shown in a StatusCenter card. + + + 无法将 {0} 从“{1}”克隆到“{2}” + Shown in a StatusCenter card. + + + 正在将“{0}”克隆到“{1}” + Shown in a StatusCenter card. + + + 正在将“{0}”从“{1}”克隆到“{2}” + Shown in a StatusCenter card. + + + 已取消安装 {0} 个字体 + Shown in a StatusCenter card. + + + 已取消从“{1}”中安装{0, plural, other {#个字体}} + Shown in a StatusCenter card. + + + 已安装{0, plural, other {#个字体}} + Shown in a StatusCenter card. + + + 已从“{1}”中安装{0, plural, other {#个字体}} + Shown in a StatusCenter card. + + + 无法安装{0, plural, other {#个字体}} + Shown in a StatusCenter card. + + + 无法从“{1}”中安装{0, plural, other {#个字体}} + Shown in a StatusCenter card. + + + 正在安装{0, plural, other {#个字体}} + Shown in a StatusCenter card. + + + 正在从“{1}”中安装{0, plural, other {#个字体}} Shown in a StatusCenter card. - 已取消将 {0} 个项目复制到“{1}” + 已取消将{0, plural, other {#个项目}}复制到“{1}” Shown in a StatusCenter card. - 已取消将 {0} 个项目从“{1}”复制到“{2}” + 已取消将{0, plural, other {#个项目}}从“{1}”复制到“{2}” Shown in a StatusCenter card. - 已将 {0} 个项目复制到“{1}” + 已将{0, plural, other {#个项目}}复制到“{1}” Shown in a StatusCenter card. - 已将 {0} 个项目从“{1}”复制到“{2}” + 已将{0, plural, other {#个项目}}从“{1}”复制到“{2}” Shown in a StatusCenter card. - 将 {0} 个项目复制到“{1}”时出错 + 将{0, plural, other {#个项目}}复制到“{1}”时出错 Shown in a StatusCenter card. - 无法将 {0} 个项目从“{1}”复制到“{2}” + 无法将{0, plural, other {#个项目}}从“{1}”复制到“{2}” Shown in a StatusCenter card. - 正在将 {0} 个项目复制到 {1}" + 正在将{0, plural, other {#个项目}}复制到“{1}” Shown in a StatusCenter card. - 正在将 {0} 个项目从“{1}”复制到“{2}” + 正在将{0, plural, other {#个项目}}从“{1}”复制到“{2}” Shown in a StatusCenter card. + + 已发现 {0, plural, one {# item} other {# items}} + Shown in a StatusCenter card during file discovery phase. + 已取消将 {0} 解压缩到“{1}” Shown in a StatusCenter card. @@ -3473,55 +3682,55 @@ Shown in a StatusCenter card. - 已取消从“{1}”中删除 {0} 个项目 + 已取消从“{1}”中删除{0, plural, other {#个项目}} Shown in a StatusCenter card. - 已从“{1}”中删除 {0} 个项目 + 已从“{1}”中删除{0, plural, other {#个项目}} Shown in a StatusCenter card. - 从“{1}”中删除 {0} 个项目时出错 + 从“{1}”中删除{0, plural, other {#个项目}}时出错 Shown in a StatusCenter card. - 无法从“{1}”中删除 {0} 个项目 + 无法从“{1}”中删除{0, plural, other {#个项目}} Shown in a StatusCenter card. - 正在从 "{1}" 中删除 {0} 个项目 + 正在从“{1}”中删除{0, plural, other {#个项目}} Shown in a StatusCenter card. - 已取消将 {0} 个项目移动到“{1}” + 已取消将{0, plural, other {#个项目}}移动到“{1}” Shown in a StatusCenter card. - 已取消将 {0} 个项目从“{1}”移动到“{2}” + 已取消将{0, plural, other {#个项目}}从“{1}”移动到“{2}” Shown in a StatusCenter card. - 已将 {0} 个项目移动到“{1}” + 已将{0, plural, other {#个项目}}移动到“{1}” Shown in a StatusCenter card. - 已将 {0} 个项目从“{1}”移动到“{2}” + 已将{0, plural, other {#个项目}}从“{1}”移动到“{2}” Shown in a StatusCenter card. - 正在将 {0} 个项目移动到 {1}" + 正在将{0, plural, other {#个项目}}移动到“{1}” Shown in a StatusCenter card. - 正在将 {0} 个项目从“{1}”移动到“{2}” + 正在将{0, plural, other {#个项目}}从“{1}”移动到“{2}” Shown in a StatusCenter card. - 将 {0} 个项目移动到“{1}”时出错 + 将{0, plural, other {#个项目}}移动到“{1}”时出错 Shown in a StatusCenter card. - 无法将 {0} 个项目从“{1}”移动到“{2}” + 无法将{0, plural, other {#个项目}}从“{1}”移动到“{2}” Shown in a StatusCenter card. @@ -3549,7 +3758,7 @@ Shown in a StatusCenter card. - 已处理 {0}/{1} 个项目 + 已处理{0}/{1, plural, other {#个项目}} Shown in a StatusCenter card. Used as "8/20 items processed" @@ -3567,6 +3776,9 @@ 设置背景壁纸失败 + + 无法打开配置文件 + 删除 Git 分支 @@ -3592,7 +3804,7 @@ 应用此标签时发生错误 - 解压缩选中的压缩文件:若其中仅有一个项目,则解压缩到当前目录下;否则,解压缩到当前目录下的新文件夹 + 解压缩选中的{0, plural, other {压缩文件}}:若其中仅有一个项目,解压缩到当前目录下;否则,解压缩到当前目录下与压缩文件同名的新文件夹中 智能解压 @@ -3601,7 +3813,7 @@ 将文件和文件夹一同排序 - 将文件和文件夹一同排序 + 将文件和文件夹混合在同一列表中进行排序 先排序文件 @@ -3657,9 +3869,6 @@ 问题和讨论 - - 平铺视图尚未支持更多项目尺寸。 - 紧凑 Used to describe layout sizes @@ -3796,7 +4005,7 @@ 垂直对齐方式 - 薄 Acrylic + 薄亚克力 This is a type of backdrop for the application background @@ -3821,6 +4030,10 @@ ICO 图标文件 This is the friendly name for ICO files. + + ICL 文件 + This is the friendly name for ICL files. + Zip 压缩文件 This is the friendly name for ZIP files. @@ -3829,6 +4042,10 @@ 位图文件 This is the friendly name for bitmap files. + + 图片文件 + This is the friendly name for image files. + 小键盘 @@ -3848,29 +4065,29 @@ 在记事本中编辑选中的文件 - 分辨率: + 分辨率: - 状态: + 状态: 显示工具栏 - Setting that controls if the Toolbar is shown in the main view + Setting that controls if the toolbar is shown in the main view 标签操作菜单 - - 添加垂直窗格 + + 垂直拆分 - 添加一个垂直窗格 + 垂直拆分窗格 - - 添加水平窗格 + + 水平拆分 - - 添加一个水平窗格 + + 水平拆分窗格 垂直排列 @@ -3884,8 +4101,8 @@ 水平排列窗格 - - 添加窗格 + + 拆分窗格 在标题栏中显示标签操作按钮 @@ -3893,8 +4110,11 @@ 排列面板 - - 默认窗格排列方式 + + 默认双面板分隔方向 + + + 双面板模式 水平 @@ -3902,4 +4122,259 @@ 垂直 + + 在系统托盘中显示 Files 图标 + + + CPU 线程 + + + 返回主页 + + + 工具栏 + + + 用户 ID + + + 批量重命名 + + + 压缩内容 + + + 显示创建备用数据流 (Alternate Data Stream) 的选项 + + + 创建备用数据流 + + + 为选中的{0, plural, other {项目}}创建备用数据流 + + + 输入数据流名称 + + + 创建备用数据流时出错 + + + 请注意:备用数据流 (Alternate Data Streams) 只适用于 NTFS 格式的驱动器。 + + + 已隐藏备用数据流 + + + 是否显示备用数据流 (Alternate Data Stream)?可随时从文件和文件夹设置页面修改此设置。 + + + 管理标签 + + + 可用 + + + 总计 + + + 总是切换到新创建的标签页 + + + 显示/隐藏中转站窗格 + + + 切换中转站窗格的可见性 + + + 在地址栏中显示中转站窗格开关 + + + 中转站 + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + 清除所有项目 + + + 从中转站移除 + + + 添加到中转站 + Tooltip that displays when dragging items to the Shelf Pane + + + 输入哈希值进行比较 + Placeholder that appears in the compare hash text box + + + 与 {0} 匹配 + Appears when two compared hashes match, e.g. "Matches SHA256" + + + 未找到任何匹配项 + Appears when two compared hashes don't match + + + 路径或别名 + + + 路径无效 + + + 测试集成 + + + 找不到 {0}。请检查你的设置并重试。 + + + 找不到设置的 IDE + + + 打开设置 + + + Visual Studio Code + + + 输入路径或启动别名 + + + 请为此 IDE 配置命名 + + + 克隆存储库 + Clone repo dialog title + + + 克隆 + Primary action button in the clone repo dialog + + + 存储库 URL + URL textbox header in the clone repo dialog + + + 无法克隆存储库 + Cannot clone repo dialog title + + + 与另一文件比较 + Button that appears in file hash properties that allows the user to compare two files + + + 大小格式 + + + 二进制 + + + 十进制 + + + 可以通过右击并选择需要的部分将其添加至侧边栏 + + + 将文件或文件夹拖动到此处,以在不同标签页之间进行操作 + + + 输入要导航到的路径… + + + 查找功能和命令… + + + 搜索文件和文件夹… + + + 在文件操作进行期间 + + + 显示状态中心按钮 + + + 状态中心进度 + Screen reader name for the status center progress ring + + + 图标文件 + This is the friendly name for a variety of different icon files. + + + 显示被折叠的路径层级导航栏 + + + 在{0}中显示文件夹 + + + 在主页显示文件夹 + + + 没有包含 {0} 的命令 + + + 查看更多 + + + 筛选 + + + 文件名 + + + 签名 + + + 签名列表 + + + 颁发者: + + + 颁发给: + + + 生效日期: + + + 截止日期: + + + 没有找到签名。 + + + 显示“在 Windows 终端中打开” + + + 打开日志文件 + + + 在你的默认编辑器中打开日志文件 + + + 在你的默认文件管理器中打开日志文件的位置 + + + 无法打开日志文件 + + + 显示状态栏 + + + 仅在列视图时 + + + 新建 {0} + + + 启用平滑翻页 + + + 翻页 + + + 显示固定选项到侧边栏 + + + 显示固定选项到“开始菜单” + \ No newline at end of file diff --git a/src/Files.App/Strings/zh-Hant/Resources.resw b/src/Files.App/Strings/zh-Hant/Resources.resw index 23e68eadfda3..066d3b1cdc74 100644 --- a/src/Files.App/Strings/zh-Hant/Resources.resw +++ b/src/Files.App/Strings/zh-Hant/Resources.resw @@ -123,9 +123,15 @@ 複製路徑 + + 複製檔案路徑 + 複製帶引號的路徑 + + 複製選取項目的帶引號路徑 + 瀏覽 @@ -142,10 +148,10 @@ 大小: - 在磁碟上的大小: + 磁碟大小: - 壓縮前的大小: + 未壓縮大小: 無法完成該操作 @@ -160,7 +166,7 @@ 略過 - 全選 + 選取全部 反選 @@ -211,7 +217,7 @@ 顯示隱藏的檔案和資料夾 - 顯示隱藏檔案 + 顯示以點開頭的隱藏配置文件(Dot files) 外觀 @@ -249,6 +255,9 @@ 貼上 + + 貼上捷徑 + 新增 @@ -291,8 +300,8 @@ 輸入名稱 - - 設置名稱 + + 新增{0} 亮色 @@ -300,6 +309,9 @@ 暗色 + + 使用系統設定 + 應用程式 @@ -310,19 +322,19 @@ 新增資料夾 - 建立檔案 + 新增檔案 該資料夾是空的。 - 返回(Alt + 向左键) + 返回/上一頁 - 下一頁(Alt + 向右鍵) + 前進/下一頁 - 上移(Alt + 向上鍵) + 返回上層 重新整理 @@ -340,7 +352,7 @@ 設為鎖定畫面背景 - 設置為應用程式背景 + 設定為應用程式背景 無法刪除此檔案 @@ -349,7 +361,7 @@ 請插入所需的磁碟機以存取此項目。 - 已斷開與裝置的連接 + 已中斷與裝置的連接 目標檔案可能已被移動或刪除。 @@ -379,49 +391,49 @@ 檔案正在被使用 - 布局 + 版面配置 唯讀 - {0, plural, one {# day ago} other {# days ago}} + {0, plural, other {# 天前}} - 1 day ago + 1 天前 - {0} days ago + {0} 天前 - {0, plural, one {# hour ago} other {# hours ago}} + {0, plural, other {# 小時前}} - 1 hour ago + 1 小時前 - {0} hours ago + {0} 小時前 - {0, plural, one {# minute ago} other {# minutes ago}} + {0, plural, other {# 分鐘前}} - 1 minute ago + 1 分鐘前 - {0} minutes ago + {0} 分鐘前 - {0, plural, one {# second ago} other {# seconds ago}} + {0, plural, other {# 秒前}} - 1 second ago + 1 秒前 - {0} seconds ago + {0} 秒前 - 立刻 + 剛剛 尚不支援此操作 @@ -439,11 +451,11 @@ 指定的項目名稱無效 - {0, plural, one {# item selected} other {# items selected}} + {0, plural, other {已選取 # 個項目}} ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural - {0, plural, one {item} other {items}} + {0, plural, other { 個項目}} ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural @@ -477,10 +489,10 @@ B - {0, number} {0, plural, one {file} other {files}}, {1, number} {1, plural, one {folder} other {folders}} + {0, number} 個{0, plural, other {檔案}},{1, number} 個{1, plural, other {資料夾}} - {0, number} {0, plural, one {file} other {files}}, {1, number} {1, plural, one {folder} other {folders}} from {2, number} {2, plural, one {location} other {locations}} + {0, number} 個{0, plural, other {檔案}},{1, number} 個{1, plural, other {資料夾}},從 {2, number} 個{2, plural, other {位置}} 以另一名使用者之身分執行 @@ -513,10 +525,7 @@ 將索引標籤移至此處 - Status - - - 您的系統語言 + 狀態 沒有結果 @@ -530,6 +539,9 @@ 複製到 {0} + + 複製儲存庫到 {0} + 建立捷徑 @@ -759,7 +771,7 @@ 格式 - 採樣速率 + 取樣速率 專輯演出者 @@ -998,6 +1010,9 @@ 從 {1} 搜尋 {0} + + ‵{0}‵ 的搜尋結果 + 詳細資料 @@ -1008,7 +1023,7 @@ 顯示受保護的系統檔案 - 在不同路徑之間使用相同的布局和排序設定 + 在不同路徑之間同步佈局和排序設定 自訂右鍵選單 @@ -1026,25 +1041,25 @@ 移除 - 在分割畫面中開啟索引標籤 + 在開啟索引標籤時啟用分割畫面 - 顯示在新畫面開啟資料夾選項 + 顯示「在分割畫面開啟資料夾」選項 - 顯示“複製路徑”的選項 + 顯示「複製路徑」選項 - 顯示用選取的檔案建立資料夾選項 + 顯示「為選取的項目建立資料夾」選項 - 顯示建立捷徑選項 + 顯示「建立捷徑」選項 新增畫面 - 在新增畫面中開啟 + 在分割畫面中開啟 檔案與資料夾 @@ -1052,9 +1067,6 @@ 詳細資料(Ctrl + Shift + 1) - - 並排(Ctrl + Shift + 2) - 刪除日期 @@ -1068,22 +1080,37 @@ 您想要的名稱 - 開啟/關閉預覽畫面(Ctrl + P) + 開啟/關閉預覽畫面 - 切換詳細資料預覽畫面 + 開啟/關閉詳細資料預覽畫面 - 切換到詳細資料視窗以查看檔案的基本內容 + 開啟/關閉詳細資料視窗以查看檔案的基本內容 切換詳細資料視窗 - 開啟資料視窗以查看詳細資料/預覽視窗 + 顯示或隱藏詳細資訊/預覽窗格 - Toggle the Toolbar + 切換工具列 + + + 顯示或隱藏工具列 + + + 顯示/關閉篩選列 + + + 顯示或關閉篩選檔案名稱的搜尋列 + + + 顯示/隱藏分割畫面 + + + 開啟或關閉分割畫面模式 無法預覽 @@ -1092,7 +1119,7 @@ 檔案名稱 - 從側邊欄取消固定 + 從側邊欄取消釘選 網路 @@ -1227,7 +1254,7 @@ 此欄位不可留白! - 檔案名稱不可以包含下列字元: \ / : * ? " < > | + 檔案名稱不可以包含下列字元:\ / : * ? " < > | 無法使用此名稱! @@ -1238,41 +1265,47 @@ 開啟儲存空間感知器 + + 在Windows設定中開啟「儲存空間感知器」 + + + 清理 + 複製 - Copy {0, plural, one {item} other {items}} + 複製{0, plural, other {項目}} - Delete {0, plural, one {item} other {items}} + 刪除{0, plural, other {項目}} 移動 - {0, plural, one {One item will be moved} other {# items will be moved}} + {0, plural, other {即將移動 # 個檔案}} - Move {0, plural, one {item} other {items}} + 移動{0, plural, other {項目}} - {0, plural, one {One item will be deleted} other {# items will be deleted}} + {0, plural,other {即將刪除 # 個檔案}} 繼續 - {0, plural, one {There is one conflicting file name} other {There are # conflicting file names}} + {0, plural, other {存在 # 個衝突的檔案名稱}} - {0, plural, one {There is one conflicting file name} other {There are # conflicting file names}}, and {1, plural, one {one outgoing item} other {# outgoing items}} + {0, plural, other {存在 # 個衝突的檔案名稱}},{1, plural, other {即將傳送 # 個項目}} - Conflicting {0, plural, one {file name} other {file names}} + {0, plural, other {檔案名稱衝突}} - {0, plural, one {One item will be copied} other {# items will be copied}} + {0, plural, other {即將複製 # 個項目}} 永久刪除 @@ -1371,7 +1404,7 @@ 去年 - 年 {0} + {0} 年 {0} 個項目 @@ -1380,7 +1413,7 @@ {0} 個項目 - 重新開啟已關閉的索引標籤 + 重新開啟已關閉的分頁 重新命名 @@ -1587,7 +1620,7 @@ 覆蓋所有子物件的權限為此父物件的可繼承權限 - Close active pane + 關閉活動窗格 進入精簡懸浮模式 @@ -1617,7 +1650,7 @@ 從雲端下載檔案並載入預覽 - {0, plural, one {# item} other {# items}} ({1, plural, one {# file} other {# files}}, {2, plural, one {# folder} other {# folders}}) + {0, plural, other {# 項目}} ({1, plural, other {# 個檔案}},{2, plural, other {# 個資料夾}}) 壓縮前的大小 @@ -1646,15 +1679,6 @@ 首頁 - - Ctrl + Shift + 1 - - - Ctrl + Shift + 2 - - - Ctrl + Shift + 6 - 已完成解壓縮。 @@ -1712,8 +1736,8 @@ - - 磁磚(並排) + + 卡片 在新索引標籤開啟資料夾 @@ -1749,7 +1773,7 @@ 檔案標籤 - 透過點擊打開檔案 + 按一下即開啟檔案 資料夾路徑 @@ -1797,7 +1821,7 @@ 登入 Windows 時 - 開啟該應用程式時 + 此應用程式啟動時 系統 @@ -1826,6 +1850,18 @@ 註冊此程式以重新啟動 + + 啟動時視窗狀態 + + + 一般視窗 + + + 最小化 + + + 最大化 + 以系統管理員身分執行 @@ -1848,16 +1884,19 @@ 不使用色彩簡化 - Third party libraries + 第三方函式庫 此項設定會修改您的系統檔案並有可能會有未預期的副作用。我們對於可能會發生的問題並不負責。請您僅在認知到這個動作可能帶來的問題的情況下才繼續進行。 + + 展平 (Flatten) 操作為永久性的,不建議使用。啟用功能代表您將自行承擔風險。 + - 建立媒體庫 + 建立媒體櫃 - 輸入媒體庫名稱 + 輸入媒體櫃名稱 計算資料夾大小 @@ -1931,6 +1970,9 @@ 關閉其他索引標籤 + + 關閉所有分頁 + 音樂 @@ -1971,25 +2013,37 @@ 調整欄位至合適寬度 - Automatically choose the best layout + 調適性 - 自動調整(Ctrl + Shift + 7) + 調適性配置 (Ctrl+Shift+7) Ctrl + Shift + 7 - 顯示備用資料流程 + 顯示替代資料流 行為 - - 評論本軟體 + + 哈囉! + + + 喜歡「Files」嗎?請考慮至 Microsoft Store 留下評論。 + + + 喜歡 Files 嗎?請考慮在 GitHub 上支持本專案。 - - 您想要在商店中評論本軟體嗎? + + 贊助 + + + 為我們評分 + + + 關閉 設為背景 @@ -2001,25 +2055,25 @@ 設為預設值 - 日期欄 + 日期 建立日期 - “大小”列 + 檔案大小 - “標籤”列 + 標籤 - “類型”列 + 類型 鎖定畫面 - - 在格狀檢視模式下,點一下即可開啟資料夾 + + 按一下即可開啟資料夾 開啟項目 @@ -2027,6 +2081,21 @@ 壓縮 + + 將子資料夾中的所有內容移動到選定的位置 + + + 展平資料夾 + + + 展平 + + + 展平資料夾將會把其子資料夾中的所有內容移動到選定的位置。此操作是永久性的,無法復原。使用此實驗性功能即表示您承擔風險並同意不對 Files 團隊因任何資料遺失究責。 + + + 顯示展平選項 + 當游標停留於檔案與資料夾上時,選取它們 @@ -2037,7 +2106,7 @@ 還原​​所有項目​​ - 確定要將 {0} 個項目還原嗎? + 是否要復原選取的{0,plural,other{ {0} 個項目}}? 還原選取項目 @@ -2049,7 +2118,7 @@ 還原 - 無法打開捷徑 + 無法開啟捷徑 找不到捷徑指向的目標 {0} ,您要刪除此捷徑嗎? @@ -2058,7 +2127,13 @@ 您沒有權限存取此資料夾。 - 封存檔案密碼 + 壓縮檔案密碼 + + + 編碼 + + + {0} (偵測) 路徑 @@ -2067,7 +2142,7 @@ 排序與分組 - 建立封存檔案 + 建立壓縮檔案 新增 @@ -2102,9 +2177,24 @@ 分割大小 - + 不分割 + + Auto + + + Dictionary size + + + Word size + + + Estimated memory usage: {0} + + + Available memory: {0} + CD @@ -2145,7 +2235,7 @@ 建立新捷徑 - 建立本地或網路上之程式、檔案、資料夾、電腦或網際網路位址的捷徑。 + 建立捷徑至本機或網路上之程式、檔案、資料夾、電腦或網際網路位址 輸入項目的位置: @@ -2154,13 +2244,19 @@ 建立捷徑 - “最近使用的檔案”已在 Windows 檔案總管中停用。 + 「最近使用的檔案」已在 Windows 檔案總管中停用。 編輯設定檔 - - 什麼是新的 + + 在預設編輯器開啟設定文件 + + + 版本資訊 + + + 開啟版本資訊 在此位置建立捷徑需要系統管理員權限 @@ -2181,25 +2277,25 @@ 檢視更多 - 顯示編輯標籤選單 + 顯示「編輯檔案標籤」選單 - 顯示壓縮選項 + 顯示「壓縮」選項 - 顯示發送到選項 + 顯示「發送到」選項 - 顯示在新索引標籤開啟資料夾選項 + 顯示「在新索引標籤開啟資料夾」選項 - 顯示在新視窗開啟資料夾選項 + 顯示「在新視窗開啟資料夾」選項 快速存取 - 輸入您連線至: {0} 的憑證 + 輸入您連線至:{0} 的憑證 輸入網路憑證 @@ -2241,25 +2337,22 @@ 右鍵選單功能 - 顯示檔案副檔名 - - - 顯示隱藏項目 + 副檔名 - 格式... + 格式化 幫助 - 切換全螢幕 + 全螢幕 您確定要刪除這個標籤? - 全部播放 + 播放 高度 @@ -2286,13 +2379,13 @@ 重新排列側邊欄項目 - 哈希值 + 雜湊值 在計算時發生錯誤 - 計算哈希值失敗,請關閉該檔案並重試。 + 計算雜湊值失敗,請關閉該檔案並重試。 無法檢視線上檔案的雜湊值 @@ -2301,7 +2394,7 @@ 演算法 - 哈希值 + 雜湊值 選擇要顯示的雜湊值 @@ -2310,16 +2403,16 @@ 計算中… - 固定項目 + 釘選項目 - 減少大小 + 縮小 - 增加大小 + 放大 - 切換排序方向 + 排序方向 無效名稱 @@ -2331,88 +2424,103 @@ 影片 - 開啟預覽視窗 + 預覽彈出視窗 切換緊湊檢視 - 在瀏覽器中開啟幫助頁面 + 在瀏覽器中開啟說明頁面 - 切換全螢幕 + 切換全螢幕模式 - 進入精簡懸浮模式 + 開啟懸浮模式 - 返回完整檢視 + 關閉懸浮模式 - 切換緊湊檢視 + 切換懸浮模式 - 前往搜尋 + 在 OmniBar 中搜尋 - 切換是否顯示隱藏項目 + 切換隱藏項目顯示 + + + 顯示/隱藏以點開頭的隱藏配置文件 (Dotfiles) - 切換是否顯示檔案副檔名 + 顯示/隱藏已知檔案類型的副檔名 - 切換預覽視窗以查看文件預覽 + 開啟/關閉預覽視窗以查看文件預覽 切換是否顯示側邊攔 - 將項目複製到剪貼簿 + 複製選取的{0, plural, other {項目}} - 將選取的項目路徑複製到剪貼簿 + 複製目前目錄的路徑 + + + 複製選取項目的路徑 + + + 複製選定項目的路徑(含引號) - 將選取的引號項目路徑複製到剪貼簿 + 複製目前目錄的路徑(含引號) - 剪下項目到剪貼簿 + 剪下選取的{0, plural, other {項目}} - 從剪貼簿貼上項目到目前資料夾 + 貼上檔案至目前的資料夾 + + + 貼上檔案捷徑至目前的資料夾 - 從剪貼簿貼上項目到所選資料夾 + 貼上檔案至選取的資料夾 + + + 貼上至選取的資料夾 - 刪除檔案 + 刪除選取的{0, plural, other {項目}} 新增資料夾 - 建立捷徑到所選項目 + 建立新{0,plural,other{捷徑}}到選取的{0,plural,other{項目}} 建立捷徑至任何項目 - 清理資源回收筒 + 清空回收桶 - 開啟 "格式化磁碟" 選項於選擇項目 + 開啟「格式化磁碟」選單於所選項目 - 從資源回收桶還原選擇項目 + 從資源回收筒復原選取的{0,plural,other{項目}} 從資源回收桶還原所有項目 - 開啟項目 + 開啟{0,plural,other{項目}} - 以應用程式開啟 + 開啟{0,plural,other{項目}}於指定的應用程式 開啟所選項目的資料夾 @@ -2421,40 +2529,40 @@ 重新整理頁面內容 - 將選定的項目重新命名 + 將選取的項目重新命名 選擇所有項目 - 反向選擇 + 反向選取檔案 - 清除選取 + 清除選取的檔案 - 切換選取 + 選取/取消選取所選定的項目 - 分享選取的項目 + 與他人分享選取的{0,plural,other{檔案}} - 釘選至開始功能表 + 釘選{0,plural,other{項目}}到「開始」 - 從開始功能表取消釘選 + 從「開始」取消釘選{0,plural,other{項目}} - 將資料夾 (s) 固定到側邊欄 + 釘選{0,plural,other{資料夾}}到側邊欄 - 取消將資料夾 (s) 固定到側邊欄 + 從側邊欄取消釘選{0,plural,other{資料夾}} 設成桌面背景 - 將選取的圖面設定為桌面幻燈片秀 + 將選取的圖片設定為桌面幻燈片秀 設成鎖定畫面背景 @@ -2462,50 +2570,59 @@ 設成此應用程式背景 + + 安裝字型 + + + 安裝驅動程式 + + + 安裝憑證 + - 安裝選取的字型 + 安裝選取的{0,plural,other{字型}} - 使用選取的 inf 檔案安裝驅動程式 + 從「選取的inf{0,plural,other{檔案}}」安裝{0,plural,other{驅動程式}} - 安裝選取的憑證 + 安裝選取的{0,plural,other{憑證}} - 以系統管理員身分執行選擇的應用程式 + 以系統管理員身分執行選取的應用程式 - 以不同的使用者身分執行選擇的應用程式 + 以不同的使用者身分執行選取的應用程式 - 執行PowerShell腳本 + 執行選取的 PowerShell 腳本 在跳出視窗中開啟預覽 - 使用所選的項目 (s) 建立檔案 + 建立包含「選取的{0,plural,other{項目}}」的壓縮檔 - 使用所選的項目 (s) 建立7z壓縮檔 + 建立包含「選取的{0,plural,other{項目}}」的 7z 壓縮檔 - 使用所選的項目 (s) 建立zip壓縮檔 + 建立包含「選取的{0,plural,other{項目}}」的zip壓縮檔 - 將所選的壓縮文件解壓至文件夾 + 解壓縮選取的{0, plural, other{壓縮檔}}到任一個資料夾 - 將所選的壓縮文件解壓至當前文件夾 + 解壓縮選取的{0,plural,other{壓縮檔}}到目前的資料夾 - 將所選的壓縮文件解壓至新建文件夾 + 解壓縮選取的{0,plural,other{壓縮檔}}到新資料夾 - 向左旋轉所選圖像 + 向左旋轉已選取{0, plural, other{影像}} - 向右旋轉所選圖像 + 向右旋轉已選取{0, plural, other{影像}} 開啟設定頁面 @@ -2517,34 +2634,34 @@ 以系統管理員身分在終端機開啟資料夾 - Decrease item size in the current view + 在目前檢視中縮小項目大小 - Increase item size in the current view + 在目前檢視中放大項目大小 切換至詳細資訊檢視 - - 切換至磁磚檢視 + + 切換至卡片檢視 - 切換至列表檢視 + 切換至清單檢視 - 列表 + 清單 - 網格 + 圖示 - 切換至網格檢視 + 切換至圖示檢視 - 切換至列檢視 + 切換至欄檢視 - Switch views adaptively + 自動切換版面配置 按名稱排序 @@ -2574,16 +2691,16 @@ 按刪除日期排序 - 依升序將項目排序 + 依遞增將項目排序 - 依降序將項目排序 + 依遞減將項目排序 - Toggle item sort direction + 切換項目的排序方向 - List items without grouping + 列出項目,不進行分組 按名稱對項目分組 @@ -2616,25 +2733,25 @@ 按資料夾路徑對項目分組 - 遞增排列分類 + 依遞增將分組排序 - 遞減排列分類 + 依遞減將分組排序 - 切換分類排序方向 + 切換分組排序方向 - 開啟新索引標籤 + 開啟新索引標籤頁面 - Navigate backward in navigation history + 上一頁 - Navigate forward in navigation history + 下一頁 - Navigate up one directory + 上層目錄 複製目前的索引標籤 @@ -2643,25 +2760,28 @@ 複製選定的索引標籤 - Close tabs to the left of current tab + 關閉目前分頁的左邊分頁 - Close tabs to the left of selected tab + 關閉所選分頁的左邊分頁 - Close tabs to the right of current tab + 關閉目前分頁的右邊分頁 - Close tabs to the right of selected tab + 關閉所選分頁的右邊分頁 - Close tabs other than current tab + 除了目前分頁,關閉所有分頁 - Close tabs other than selected tab + 除了所選分頁,關閉所有分頁 + + + 關閉所有分頁,也包含目前的分頁 - 恢復先前關閉的分頁 + 重新開啟已關閉的分頁 移至上一個索引標籤 @@ -2673,13 +2793,13 @@ 關閉目前分頁 - Close active pane + 關閉目前聚焦的分割畫面 - Focus other pane + 切換焦點到另一個分割畫面 - Switch focus to the non active pane + 聚焦到另一個分割畫面 開啟/關閉側邊欄 @@ -2729,19 +2849,19 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Left + 向左鍵 Key name for hotkeys in menus. Use abbreviation if possible. - Right + 向右鍵 Key name for hotkeys in menus. Use abbreviation if possible. - Down + 向下鍵 Key name for hotkeys in menus. Use abbreviation if possible. - 上移(Alt + 向上鍵) + 向上鍵 Key name for hotkeys in menus. Use abbreviation if possible. @@ -2761,7 +2881,7 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Sep + 豎線(|) Key name for hotkeys in menus. Use abbreviation if possible. @@ -2769,7 +2889,7 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Sleep + 睡眠鍵 Key name for hotkeys in menus. Use abbreviation if possible. @@ -2785,11 +2905,11 @@ Key name for hotkeys in menus. Use abbreviation if possible. - Mouse4 + 滑鼠第 4 鍵 Key name for hotkeys in menus. Use abbreviation if possible. - Mouse5 + 滑鼠第 5 鍵 Key name for hotkeys in menus. Use abbreviation if possible. @@ -2861,18 +2981,18 @@ Key name for hotkeys in menus. Use abbreviation if possible. - 減少音量 + 調低音量 Key name for hotkeys in menus. Use abbreviation if possible. - 增加音量 + 調高音量 Key name for hotkeys in menus. Use abbreviation if possible. 變更 - 切換選擇 + 選取/取消選取 修改副檔名時顯示警告 @@ -2887,13 +3007,13 @@ 無標記 - 移到上一索引標籤 + 上一分頁 - 移到下一索引標籤 + 下一分頁 - 關閉目前標籤 + 關閉分頁 編輯路徑 @@ -2923,13 +3043,13 @@ 選擇項目時顯示核取方塊 - 移動焦點至路徑欄 + 在 OmniBar 中編輯路徑 - 建立新項目 + 新增項目 - 沒有群組或使用者擁有全縣存取此檔案,但是,該檔案的擁有者可以授予權限。 + 沒有群組或使用者擁有權限存取此檔案。但是,該檔案的擁有者可以授予權限。 開啟項目位置 @@ -2938,19 +3058,19 @@ 永久刪除 - 永久刪除項目 + 永久刪除選取的{0, plural, other {項目}} 播放所選取的媒體檔 - 復原上次檔案的動作 + 復原上次檔案的操作 - 重做上次檔案的動作 + 重做上次檔案的操作 - 位置: + 位置: 按月對項目分組 @@ -2962,10 +3082,10 @@ - + - 切換按日期分組為單位 + 切換按日期分組的單位 切換分組單位 @@ -2986,7 +3106,7 @@ 按建立日期之年對項目分組 - 按刪除日期將項目分組 + 按刪除日期之日將項目分組 按刪除日期之月對項目分組 @@ -2995,7 +3115,7 @@ 按刪除日期之年對項目分組 - Group items by day of date modified + 按修改日期之日將項目分組 按修改日期之月對項目分組 @@ -3004,29 +3124,38 @@ 按修改日期之年對項目分組 - Click 'Advanced permissions' to continue. + 點擊「進階權限」來繼續 - You must have Read permissions to view the properties of this item. + 您必須擁有讀取權限才能查看此項目的內容。 - To try taking ownership of the item, which includes permission to view its properties, click Change above. + 要嘗試取得此項目的擁有權(包括查看其內容的權限),請點擊上方的「變更」。 無法顯示權限 - Leave my changes on '{0}' + 將我的變更保留在「{0}」 - 放棄變更 + 捨棄變更 - Bring my changes to '{0}' + 將我的變更帶入「{0}」 您在此分支上有未提交的變更,您要如何處理它們? + + 您目前有尚未解決的合併衝突。請先解決這些衝突或放棄合併,以便切換分支。 + + + 終止合併並切換到 '{0}' + + + 停留在 '{0}' 並解決衝突 + 切換分支 @@ -3055,19 +3184,25 @@ 切換至新分支 - Create a folder with the currently selected item(s) + 為已選取{0, plural, other{項目}}建立資料夾 - 打開屬性 + 內容 + + + 開啟檔案總管屬性 - 打開屬性視窗 + 開啟內容視窗 + + + 開啟 Windows 檔案總管的內容視窗 本地 - Remotes + 遠端 在 Crowdin 上進行翻譯 @@ -3079,16 +3214,19 @@ Pull - Run git fetch + 執行 git fetch 以從遠端儲存庫取得最新的變更 + + + 建立儲存庫的完整副本,包含所有歷史記錄和分支 - Run git pull + 執行 git pull 以從遠端儲存庫取得並合併最新的變更 預覽 - Git status + Git 狀態 Git 錯誤 @@ -3097,17 +3235,17 @@ 由於逾時錯誤,git pull 失敗 - (multiple values) + (多個值) Text indicating that multiple selected files have different metadata values. 作者 - Date committed + Commit 日期 - Commit message + Commit 訊息 Commit SHA @@ -3116,58 +3254,58 @@ Git - 亞克力 + 壓克力 - Mica + 柔光背景(Mica) - Mica 替代 + 效能柔光背景(Mica Alt) - 背景材質 + 背景質感 純色 - Manage branches + 管理分支 Push - 執行 git push + 執行 git push 以將本地變更推送到遠端儲存庫 同步 - 執行git pull 然後執行git push + 執行 git pull 然後執行 git push - {0} outgoing / {1} incoming commits + {0} 個已發送的 commit / {1} 個已接收的 commit - 連線至 Github + 登入 GitHub - 在下面的網址欄輸入輸入以下驗證碼,即可開始使用 Github 儲存庫。 + 在下面的連結中輸入以下驗證碼,即可開始使用 GitHub 儲存庫。 - Files 目前無法連線至 Github。 + Files 目前無法連線至 GitHub。 - - 在 VS Code 中開啟檔案 + + 以 {0} 開啟資料夾 - - 在 Visual Studio Code 中開啟目前目錄 + + 以 {0} 開啟當前目錄 - - 在 VS Code 中開啟儲存庫 + + 以 {0} 開啟儲存庫(repo) - - Open the root of the Git repo in Visual Studio Code + + 以 {0} 開啟此儲存庫(repo)的根目錄 複製代碼 @@ -3182,40 +3320,40 @@ 已修改 - Untracked + 未追蹤 - Unable to display current owner. + 無法顯示目前擁有者。 - Great! You are now authorized. + 水啦!你的驗證已通過 - Initialize repo + 初始化 Git 儲存庫 - Initialize a Git repository + 初始化 Git 倉庫 - Remote Repository Name + 遠端儲存庫名稱 - Current Branch + 目前分支 - Path column + 路徑欄 - Sort items by path + 按路徑排序 - Open directory in new pane + 在窗格中開啟選取的目錄 - Open directory in new tab + 在分頁中開啟選取的目錄 - Open directory in new window + 在新視窗中開啟選取的目錄 開啟所有 @@ -3224,29 +3362,29 @@ 開啟所有附有標籤的項目 - Drive ({0}) + 磁碟 ({0}) {0} is the drive letter. - Invalid command + 無效的指令 - '{0}' is not recognized as a command. + 無法辨認指令 "{0}" - Command not executable + 指令無法執行 - The '{0}' command is not ready to be executed. + 指令 "{0}" 目前還無法執行 - 指令面板 + 命令選擇區 - 開啟指令面板 + 在 OmniBar 開啟命令選擇區 - 開始於: + 開始於: 開啟 BitLocker @@ -3267,7 +3405,7 @@ 正以系統管理員身分執行 Files - Due to a limitation with Windows and WinAppSdk, drag and drop isn't available when running Files as admin. If you wish to use drag and drop, you can work around this limitation by opening UAC (User Account Control) from the Start Menu and selecting the third level, and restarting Windows. Otherwise, you can keep using Files without drag & drop. + 由於 Windows 和 WinAppSdk 的限制,以管理員模式執行 Files 時無法使用拖放功能(drag and drop)。如果您希望使用拖放功能,可以透過在「開始」選單中開啟 UAC(使用者帳戶控制),選擇「第三級」(僅在應用程式嘗試變更電腦時通知我 (不會降低桌面亮度)),然後重新啟動 Windows 來解決這個限制。否則,您可以繼續使用 Files,但無法使用拖放功能。 關閉視窗後在背景繼續執行 @@ -3305,7 +3443,7 @@ One of the custom color themes - Iris Pastel + 淡紫羅蘭 One of the custom color themes @@ -3352,14 +3490,14 @@ 清除完成 - 名稱: + 名稱: - {0} of {1} processed + 已處理 {0},共 {1} Shown in a StatusCenter card. Used as "64 MB of 1 GB processed" - 項目 + 個項目 Files 無法將此目錄初始化為 Git 儲存庫 @@ -3373,167 +3511,238 @@ 正在處理檔案... + + 正在探索項目... + 速度: - Canceled compressing {0} items to "{1}" + 已取消將 {0} 個項目壓縮到「{1}」 Shown in a StatusCenter card. - Canceled compressing {0} items from "{1}" to "{2}" + 已取消將 {0} 個項目從「{1}」壓縮到「{2}」 Shown in a StatusCenter card. - Compressed {0} item(s) to "{1}" + 已壓縮 {0, plural, other{# 個項目}}到「{1}」 Shown in a StatusCenter card. - Compressed {0} item(s) from "{1}" to "{2}" + 已從「{1}」壓縮 {0, plural, other{# 個項目}}到「{2}」 Shown in a StatusCenter card. - 將一個項目"{0}"壓縮到"{1}"時發生錯誤 + 壓縮 {0, plural, other{# 個項目}}到 "{1}" 時發生錯誤 Shown in a StatusCenter card. - Failed to compress {0} item(s) from "{1}" to "{2}" + 從「{1}」壓縮 {0, plural, other{# 個項目}}到「{2}」失敗 Shown in a StatusCenter card. - Compressing {0} item(s) to "{1}" + 正在壓縮 {0, plural, other{# 個項目}}到「{1}」 Shown in a StatusCenter card. - Compressing {0} item(s) from "{1}" to "{2}" + 正在從「{1}」壓縮 {0, plural, other{# 個項目}}到「{2}」 + Shown in a StatusCenter card. + + + 已取消將儲存庫「{0}」複製到「{1}」 + Shown in a StatusCenter card. + + + 已取消將儲存庫 {0} 從「{1}」複製到「{2}」 + Shown in a StatusCenter card. + + + 已成功將儲存庫「{0}」複製到「{1}」 + Shown in a StatusCenter card. + + + 已成功將儲存庫 {0} 從「{1}」複製到「{2}」 + Shown in a StatusCenter card. + + + 將儲存庫「{0}」複製到「{1}」時發生錯誤 + Shown in a StatusCenter card. + + + 無法將儲存庫「{0}」從「{1}」複製到「{2}」 + Shown in a StatusCenter card. + + + 正在將儲存庫「{0}」複製到「{1}」 + Shown in a StatusCenter card. + + + 正在將儲存庫「{0}」從「{1}」複製到「{2}」 + Shown in a StatusCenter card. + + + 已取消安裝 {0} 款字型 + Shown in a StatusCenter card. + + + 已取消從「{1}」安裝 {0, plural, other{# 款字體}} + Shown in a StatusCenter card. + + + 已安裝 {0, plural, other{# 款字體}} + Shown in a StatusCenter card. + + + 已從「{1}」安裝 {0, plural, other{# 款字體}} + Shown in a StatusCenter card. + + + 安裝 {0, plural, other{# 款字體}}時發生錯誤 + Shown in a StatusCenter card. + + + 從「{1}」安裝 {0, plural, other{# 款字體}}時發生錯誤 + Shown in a StatusCenter card. + + + 正在安裝 {0, plural, other{# 款字體}} + Shown in a StatusCenter card. + + + 正在從「{1}」安裝 {0, plural, other{# 款字體}} Shown in a StatusCenter card. - Canceled copying {0} item(s) to "{1}" + 已取消複製到「{1}」的 {0, plural, other{# 個項目}} Shown in a StatusCenter card. - Canceled copying {0} item(s) from "{1}" to "{2}" + 已取消從「{1}」複製到「{2}」的 {0, plural, other{# 個項目}} Shown in a StatusCenter card. - Copied {0} item(s) to "{1}" + 已複製 {0, plural, other{# 個項目}}到「{1}」 Shown in a StatusCenter card. - Copied {0} item(s) from "{1}" to "{2}" + 已從「{1}」複製 {0, plural, other{# 個項目}}到「{2}」 Shown in a StatusCenter card. - 將一個項目"{0}"複製到"{1}"時發生錯誤 + 複製 {0, plural, other{# 個項目}}到「{1}」時發生錯誤 Shown in a StatusCenter card. - Failed to copy {0} item(s) from "{1}" to "{2}" + 從「{1}」複製 {0, plural, other{# 個項目}}到「{2}」失敗 Shown in a StatusCenter card. - Copying {0} item(s) to "{1}" + 正在複製 {0, plural, other{# 個項目}}到「{1}」 Shown in a StatusCenter card. - Copying {0} item(s) from "{1}" to "{2}" + 正在從「{1}」複製 {0, plural, other{# 個項目}}到「{2}」 Shown in a StatusCenter card. + + 已找到{0, plural, other{ # 個項目}} + Shown in a StatusCenter card during file discovery phase. + - Canceled extracting "{0}" to "{1}" + 已取消將「{0}」解壓縮到「{1}」 Shown in a StatusCenter card. - Canceled extracting "{0}" from "{1}" to "{2}" + 已取消將「{0}」從「{1}」解壓縮到「{2}」 Shown in a StatusCenter card. - Extracted "{0}" to "{1}" + 已成功將「{0}」解壓縮到「{1}」 Shown in a StatusCenter card. - Extracted "{0}" from "{1}" to "{2}" + 已成功將「{0}」從「{1}」解壓縮到 「{2}」 Shown in a StatusCenter card. - 將“{0}”解壓縮到“{1}”時發生錯誤 + 將「{0}」解壓縮到「{1}」時發生錯誤 Shown in a StatusCenter card. - Failed to extract "{0}" from "{1}" to "{2}" + 無法將「{0}」從「{1}」解壓縮到「{2}」 Shown in a StatusCenter card. - Extracting "{0}" to "{1}" + 正在將「{0}」解壓縮到「{1}」 Shown in a StatusCenter card. - Extracting "{0}" from "{1}" to "{2}" + 正在將「{0}」從「{1}」解壓縮到「{2}」 Shown in a StatusCenter card. - Canceled deleting {0} item(s) from "{1}" + 已取消從「{1}」刪除 {0, plural, other{# 個項目}} Shown in a StatusCenter card. - Deleted {0} item(s) from "{1}" + 已從「{1}」刪除 {0, plural, other{# 個項目}} Shown in a StatusCenter card. - 從“{1}”中刪除 {0} 個項目時發生錯誤 + 從「{1}」刪除 {0, plural, other{# 個項目}}時發生錯誤 Shown in a StatusCenter card. - Failed to delete {0} item(s) from "{1}" + 從「{1}」刪除 {0, plural, other{# 個項目}}失敗 Shown in a StatusCenter card. - Deleting {0} item(s) from "{1}" + 正在從「{1}」刪除 {0, plural, other{# 個項目}} Shown in a StatusCenter card. - Canceled moving {0} item(s) to "{1}" + 已取消將 {0, plural, other{# 個項目}}移至「{1}」 Shown in a StatusCenter card. - Canceled moving {0} item(s) from "{1}" to "{2}" + 已取消將 {0, plural, other{# 個項目}}從「{1}」移至「{2}」 Shown in a StatusCenter card. - Moved {0} item(s) to "{1}" + 已將 {0, plural, other{# 個項目}}移至「{1}」 Shown in a StatusCenter card. - Moved {0} item(s) from "{1}" to "{2}" + 已將 {0, plural, other{# 個項目}}從「{1}」移至「{2}」 Shown in a StatusCenter card. - Moving {0} item(s) to "{1}" + 正在將 {0,plural,other{#}} 個項目移至「{1}」 Shown in a StatusCenter card. - Moving {0} item(s) from "{1}" to "{2}" + 正在將 {0, plural, other{# 個項目}}從「{1}」移至「{2}」 Shown in a StatusCenter card. - 將一個項目 {0} 移動到 {1} 時發生錯誤 + 將 {0, plural, other{# 個項目}}移至「{1}」時發生錯誤 Shown in a StatusCenter card. - 無法將 {0} 個項目從 {1} 移至 {2} + 將 {0, plural, other{# 個項目}}從「{1}」移至「{2}」失敗 Shown in a StatusCenter card. - Canceled emptying Recycle Bin + 已取消清空資源回收桶 Shown in a StatusCenter card. - Emptied Recycle Bin + 已清空資源回收桶 Shown in a StatusCenter card. - Emptying Recycle Bin + 正在清空資源回收桶 Shown in a StatusCenter card. @@ -3545,78 +3754,81 @@ Shown in a StatusCenter card. - Preparing the operation... + 正在準備檔案操作... Shown in a StatusCenter card. - {0}/{1} item(s) processed + 已處理 {0} / {1, plural, other {# 項目}} Shown in a StatusCenter card. Used as "8/20 items processed" - Unblock downloaded file + 解除封鎖已下載的檔案 - No ongoing file operations + 目前沒有正在進行的檔案操作 - The option to open Files on Windows Startup is not available due to your system settings or group policy. To re-enable, open the startup page in Task Manager. + 由於系統設定或群組原則,無法啟用在 Windows 啟動時開啟 Files 的選項。要重新啟用此選項,請在工作管理員中開啟「啟動」頁面。 - Failed to restore items from Recycle Bin + 無法將項目從資源回收桶中復原 - Failed to set the background wallpaper + 無法設定背景桌布 + + + 設定文件開啟失敗 - Delete Git branch + 刪除 Git 分支 - Are you sure you want to permanently delete "{0}" branch? + 確定要永久刪除分支 "{0}" 嗎? - Connected to GitHub + 已登入至 GitHub - Logout + 登出 - 連線至 Github + 登入 GitHub - Login + 登入 - 標籤目前只相容 NTFS格式的磁碟機。 + 標籤目前只相容 NTFS 格式的磁碟機。 套用此標籤時發生錯誤 - Extract items from selected archive(s) to current folder for single-item archive, or to new folder for multi-item archive + 解壓縮選取的{0,plural,other{壓縮檔}}:單一檔案至此,多個檔案至新資料夾 - Extract here (Smart) + 解壓縮至此(智慧) - Sort files and folders together + 同時排序檔案和資料夾 - Sort files and folders together + 檔案與資料夾一同排序 - Sort files first + 優先排序檔案 - Sort files first then folders + 先排序檔案,再排序資料夾 - Sort folders first + 優先排序資料夾 - Sort folders first then files + 先排序資料夾,再排序檔案 - Sort priority + 排序優先順序 旋轉圖片失敗 @@ -3657,9 +3869,6 @@ 問題與討論 - - Additional sizes are not yet available for the Tiles View. - 緊湊 Used to describe layout sizes @@ -3673,23 +3882,23 @@ Used to describe layout sizes - 中等+ + 中 + Used to describe layout sizes - 中等 ++ + 中 ++ Used to describe layout sizes - 中等 +++ + 中 +++ Used to describe layout sizes - 中等 +++++ + 中 ++++ Used to describe layout sizes - 中等 ++++++ + 中 +++++ Used to describe layout sizes @@ -3719,187 +3928,453 @@ 佈局類型 - Actions + 操作 - Commands + 指令 - Add command + 新增指令 - Restore defaults + 恢復預設值 - Choose an action + 請選擇要執行的指令 - Are you sure you want to restore the default key bindings? This action cannot be undone. + 是否確定要重置回預設的按鍵設定? 此操作無法復原 - This key binding is already being used, please choose a different key binding to continue. + 此按鍵組合已被使用,請使用其他按鍵組合 - The key binding you choose cannot be used, please try again using a different key binding. + 此按鍵組合無法使用,請使用其他按鍵組合 - Customized + 自訂 - Adaptive layout is not available when preferences are synced, the default layout has been changed to Details. + 當偏好設定同步時,調適性配置無法使用,預設配置類型已變更為詳細資料。 - Background image + 背景圖片 - Bottom + 底端對齊 Image alignment type - Center + 置中 Image alignment type - Fill + 填滿 Image stretch type - Horizontal alignment + 水平對齊 - Image fit + 影像調整 - Left + 靠左對齊 Image alignment type - Opacity + 透明度 - Right + 靠右對齊 Image alignment type - Top + 頂部對齊 Image alignment type - Uniform + 等比例 Image stretch type - Uniform to fill + 等比例填滿 Image stretch type - Vertical alignment + 垂直對齊 - Thin Acrylic + 薄亞克力 This is a type of backdrop for the application background - Show for all locations + 在所有地方顯示 Setting where users can choose to display "Open IDE" button for Git Repos - Developer tools + 開發者工具 - Configure the "Open IDE" button on the status bar + 在狀態欄顯示「在IDE開啟」的選項 - Show for Git repos + 僅在 Git Repository 時顯示 Setting where users can choose to display "Open IDE" button for all locations. - Application extension + 應用程式擴充 This is the friendly name for DLL files. - ICO File + ICO 檔案 This is the friendly name for ICO files. + + Windows 圖示庫檔案 + This is the friendly name for ICL files. + - Zip File + Zip 檔案 This is the friendly name for ZIP files. - Bitmap Files + 點陣圖檔 This is the friendly name for bitmap files. + + 圖片檔 + This is the friendly name for image files. + - Num + 數字鍵盤 - Network locations + 網路位置 - There are no network locations. If you don't use network locations, you can disable the widget. + 目前沒有網路位置。如果您不使用網路位置,您可以停用這個小工具。 - Disable + 停用 - Edit in Notepad + 在記事本中編輯 - Edit the selected file in Notepad + 在記事本中編輯所選檔案 - Dimensions: + 尺寸: - Status: + 狀態: - Show Toolbar - Setting that controls if the Toolbar is shown in the main view + 顯示工具列 + Setting that controls if the toolbar is shown in the main view - Tab actions menu + 索引標籤功能表 - - Add vertical pane + + 左右分割 - Add a vertical pane + 分隔成左、右面板 - - Add horizontal pane + + 上下分割 - - Add a horizontal pane + + 分隔成上、下面板 - Arrange vertically + 調整為上下分割模式 - Arrange panes vertically + 上下分割畫面 - Arrange horizontally + 調整為左右分割模式 - Arrange panes horizontally + 左右分割畫面 - - Add pane + + 分割面板 - Show tab actions button in the title bar + 在標題欄顯示索引標籤功能表 - Arrange panes + 調整分割畫面 + + + 預設分割面板方式 - - Default pane arrangement + + 分割畫面模式 - Horizontal + 左右排列 - Vertical + 上下分割 + + + 在系統匣顯示 Files 圖示 + + + CPU 執行緒 + + + 前往首頁 + + + 工具列 + + + 使用者 ID + + + 批次重新命名 + + + 壓縮內容 + + + 顯示「建立替代資料流」選項 + + + 建立替代資料流 + + + 為選取的{0,plural,other{項目}}建立替代資料流 + + + 輸入 Data Stream 名稱 + + + 建立替代資料流時發生錯誤 + + + 請注意,替代資料流 (Alternate Data Stream) 只適用於NTFS系統 + + + 目前的替代資料流為隱藏狀態 + + + 是否要顯示替代資料流 (Alternate Data Stream)? 您隨時可以在「檔案與資料夾」頁面中變更設定 + + + 管理標籤 + + + 可用的 + + + 總計 + + + 一律將焦點切換至新的分頁 + + + 顯示/隱藏置放架 + + + 顯示或隱藏置放架面板 + + + 在路徑列中顯示置放架開關 + + + 置放架 + 'Shelf' refers to the Shelf Pane feature, where users can conveniently drag and drop files for quick access and perform bulk actions with ease. + + + 清除項目 + + + 從置放架移除 + + + 加入至置放架 + Tooltip that displays when dragging items to the Shelf Pane + + + 輸入雜湊值進行比較 + Placeholder that appears in the compare hash text box + + + 匹配 {0} + Appears when two compared hashes match, e.g. "Matches SHA256" + + + 無匹配結果 + Appears when two compared hashes don't match + + + 路徑或別名 + + + 無效的路徑 + + + 測試整合性 + + + 找不到 {0},請重新檢查設定後重試。 + + + 找不到目前設定的 IDE + + + 開啟設定 + + + Visual Studio Code + + + 輸入檔案路徑或是開啟指令(alias) + + + 為這個 IDE 命名 + + + 建立儲存庫副本 + Clone repo dialog title + + + 複製儲存庫 + Primary action button in the clone repo dialog + + + 儲存庫網址 + URL textbox header in the clone repo dialog + + + 無法建立儲存庫副本 + Cannot clone repo dialog title + + + 比較檔案 + Button that appears in file hash properties that allows the user to compare two files + + + 大小格式 + + + 二進位 + + + 十進位 + + + 你可以透過右鍵點擊並選取想要新增的區段,來將區段加入側邊欄 + + + 將檔案或資料夾拖曳到這裡,以在不同分頁間互動 + + + 請輸入路徑…… + + + 搜尋功能與指令…… + + + 搜尋資料夾和檔案…… + + + 正在進行的檔案操作 + + + 顯示狀態中心按鈕 + + + 狀態中心進度環 + Screen reader name for the status center progress ring + + + 圖標檔案 + This is the friendly name for a variety of different icon files. + + + 展開已收合的路徑導覽 + + + 開啟 {0} 中的資料夾清單 + + + 在首頁顯示資料夾 + + + 沒有任何包含 {0} 的指令 + + + 查看更多 + + + 篩選檔案名稱 + + + 檔名 + + + 電子簽章 + + + 電子簽章清單 + + + 發行人: + + + 發行給: + + + 有效期自: + + + 有效期至: + + + 找不到電子簽章。 + + + 顯示「在 Windows Terminal 中開啟資料夾」選項 + + + 開啟紀錄檔 + + + 在預設編輯器開啟紀錄檔 + + + 在預設檔案管理器中開啟紀錄檔位置 + + + 無法開啟紀錄檔 + + + 顯示狀態欄 + + + 只在欄檢視模式 + + + 新增 '{0}' + + + 開啟平滑捲動 + + + 捲動 + + + 顯示「釘選到側邊欄」的選項 + + + 顯示「釘選到開始選單」的選項 \ No newline at end of file diff --git a/src/Files.App/Styles/DefaultGridSplitterStyle.xaml b/src/Files.App/Styles/DefaultGridSplitterStyle.xaml index 829fc451354b..2ecb6e64c0c9 100644 --- a/src/Files.App/Styles/DefaultGridSplitterStyle.xaml +++ b/src/Files.App/Styles/DefaultGridSplitterStyle.xaml @@ -1,8 +1,8 @@ - + + xmlns:controls="using:Files.App.Controls"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + M3.97.34C3.91.14,3.72,0,3.51,0c-.21,0-.4.13-.48.32L.03,8.32c-.1.26.03.55.29.64.26.1.55-.03.64-.29l.63-1.68h3.56l.28.74.53-1.42L3.97.34Zm.83,5.66H1.97L3.47,2l1.33,4Zm4.43-4c.3,0,.58.17.7.45l4.91,11.05h.4c.41,0,.75.34.75.75s-.34.75-.75.75h-2.5c-.41,0-.75-.34-.75-.75s.34-.75.75-.75h.46l-1.11-2.5h-5.34l-1,2.5h.49c.41,0,.75.34.75.75s-.34.75-.75.75h-2.5c-.41,0-.75-.34-.75-.75s.34-.75.75-.75h.39L8.55,2.47c.11-.28.38-.47.68-.47Zm-1.88,7.5h4.07l-2.14-4.82-1.93,4.82Z - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + M 4.9219 18.75 C 4.4336 18.75 3.9665 18.6507 3.5205 18.4521 C 3.0745 18.2536 2.6839 17.9867 2.3486 17.6514 C 2.0133 17.3161 1.7464 16.9255 1.5479 16.4795 C 1.3493 16.0335 1.25 15.5664 1.25 15.0781 L 1.25 4.9219 C 1.25 4.4336 1.3493 3.9665 1.5479 3.5205 C 1.7464 3.0745 2.0133 2.6839 2.3486 2.3486 C 2.6839 2.0133 3.0745 1.7464 3.5205 1.5479 C 3.9665 1.3493 4.4336 1.25 4.9219 1.25 L 15.0781 1.25 C 15.5664 1.25 16.0335 1.3493 16.4795 1.5479 C 16.9255 1.7464 17.3161 2.0133 17.6514 2.3486 C 17.9867 2.6839 18.2536 3.0745 18.4521 3.5205 C 18.6507 3.9665 18.75 4.4336 18.75 4.9219 L 18.75 15.0781 C 18.75 15.5664 18.6507 16.0335 18.4521 16.4795 C 18.2536 16.9255 17.9867 17.3161 17.6514 17.6514 C 17.3161 17.9867 16.9255 18.2536 16.4795 18.4521 C 16.0335 18.6507 15.5664 18.75 15.0781 18.75 Z M 15.0488 17.5 C 15.3743 17.5 15.6852 17.4333 15.9814 17.2998 C 16.2777 17.1663 16.5381 16.9873 16.7627 16.7627 C 16.9873 16.5381 17.1663 16.2777 17.2998 15.9814 C 17.4333 15.6852 17.5 15.3743 17.5 15.0488 L 17.5 4.9512 C 17.5 4.6257 17.4333 4.3148 17.2998 4.0186 C 17.1663 3.7223 16.9873 3.4619 16.7627 3.2373 C 16.5381 3.0127 16.2777 2.8337 15.9814 2.7002 C 15.6852 2.5667 15.3743 2.5 15.0488 2.5 L 4.9512 2.5 C 4.6257 2.5 4.3148 2.5667 4.0186 2.7002 C 3.7223 2.8337 3.4619 3.0127 3.2373 3.2373 C 3.0127 3.4619 2.8337 3.7223 2.7002 4.0186 C 2.5667 4.3148 2.5 4.6257 2.5 4.9512 L 2.5 15.0488 C 2.5 15.3743 2.5667 15.6852 2.7002 15.9814 C 2.8337 16.2777 3.0127 16.5381 3.2373 16.7627 C 3.4619 16.9873 3.7223 17.1663 4.0186 17.2998 C 4.3148 17.4333 4.6257 17.5 4.9512 17.5 Z M 5.625 6.25 C 5.4557 6.25 5.3092 6.1882 5.1855 6.0645 C 5.0618 5.9408 5 5.7943 5 5.625 C 5 5.4557 5.0618 5.3092 5.1855 5.1855 C 5.3092 5.0618 5.4557 5 5.625 5 L 14.375 5 C 14.5443 5 14.6908 5.0618 14.8145 5.1855 C 14.9382 5.3092 15 5.4557 15 5.625 C 15 5.7943 14.9382 5.9408 14.8145 6.0645 C 14.6908 6.1882 14.5443 6.25 14.375 6.25 Z Z Z - + F1 M 0.625 0 C 0.794271 0 0.940755 0.06185 1.064453 0.185547 L 19.814453 18.935547 C 19.93815 19.059244 20 19.205729 20 19.375 C 20 19.544271 19.93815 19.690756 19.814453 19.814453 C 19.690754 19.93815 19.54427 20 19.375 20 C 19.205729 20 19.059244 19.93815 18.935547 19.814453 L 13.544922 14.433594 C 13.336588 14.720053 13.100586 14.975586 12.836914 15.200195 C 12.573242 15.424805 12.290039 15.615234 11.987305 15.771484 C 11.68457 15.927734 11.363932 16.046549 11.025391 16.12793 C 10.686849 16.209311 10.341797 16.25 9.990234 16.25 C 9.384766 16.25 8.816731 16.134441 8.286133 15.90332 C 7.755534 15.672201 7.293294 15.359701 6.899414 14.96582 C 6.505534 14.57194 6.193034 14.109701 5.961914 13.579102 C 5.730794 13.048503 5.615234 12.480469 5.615234 11.875 C 5.615234 11.165365 5.776367 10.499675 6.098633 9.87793 C 6.420898 9.256186 6.868489 8.736979 7.441406 8.320312 L 5.46875 6.347656 C 4.635417 6.888021 3.914388 7.548828 3.305664 8.330078 C 2.69694 9.111328 2.216797 9.967448 1.865234 10.898438 C 1.79362 11.08724 1.730143 11.276042 1.674805 11.464844 C 1.619466 11.653646 1.565755 11.842448 1.513672 12.03125 C 1.468099 12.174479 1.394857 12.291667 1.293945 12.382812 C 1.193034 12.473959 1.064453 12.519531 0.908203 12.519531 C 0.738932 12.519531 0.592448 12.457683 0.46875 12.333984 C 0.345052 12.210287 0.283203 12.063803 0.283203 11.894531 C 0.283203 11.861979 0.289714 11.813151 0.302734 11.748047 C 0.615234 10.472006 1.131185 9.290365 1.850586 8.203125 C 2.569987 7.115886 3.476562 6.197917 4.570312 5.449219 L 0.185547 1.064453 C 0.061849 0.940756 0 0.794271 0 0.625 C 0 0.45573 0.061849 0.309246 0.185547 0.185547 C 0.309245 0.06185 0.455729 0 0.625 0 Z M 7.119141 4.189453 C 8.043619 3.896484 9.00065 3.75 9.990234 3.75 C 11.18815 3.75 12.312825 3.953451 13.364258 4.360352 C 14.415689 4.767254 15.361327 5.328777 16.201172 6.044922 C 17.041016 6.761068 17.75716 7.604167 18.349609 8.574219 C 18.942057 9.544271 19.384766 10.595703 19.677734 11.728516 C 19.690754 11.79362 19.697266 11.845703 19.697266 11.884766 C 19.697266 12.054037 19.633789 12.198894 19.506836 12.319336 C 19.379883 12.439779 19.23177 12.5 19.0625 12.5 C 18.90625 12.5 18.780924 12.456055 18.686523 12.368164 C 18.592121 12.280273 18.518879 12.164714 18.466797 12.021484 C 18.434244 11.923828 18.404947 11.826172 18.378906 11.728516 L 18.330078 11.542969 C 18.304035 11.445312 18.274738 11.347656 18.242188 11.25 C 18.072916 10.748698 17.871094 10.266928 17.636719 9.804688 C 17.259113 9.088542 16.801758 8.434245 16.264648 7.841797 C 15.727539 7.24935 15.130208 6.743164 14.472656 6.323242 C 13.815104 5.90332 13.108724 5.5778 12.353516 5.34668 C 11.598307 5.115561 10.810547 5.000001 9.990234 5 C 9.677734 5.000001 9.366861 5.017904 9.057617 5.053711 C 8.748372 5.089519 8.440755 5.143229 8.134766 5.214844 Z M 10.449219 7.519531 C 10.963541 7.571615 11.445312 7.708334 11.894531 7.929688 C 12.343749 8.151042 12.740885 8.434245 13.085938 8.779297 C 13.430989 9.12435 13.714192 9.521484 13.935547 9.970703 C 14.1569 10.419922 14.293619 10.901693 14.345703 11.416016 Z M 8.339844 9.228516 C 7.877604 9.514975 7.516275 9.890951 7.255859 10.356445 C 6.995442 10.82194 6.865234 11.328125 6.865234 11.875 C 6.865234 12.304688 6.946614 12.709961 7.109375 13.09082 C 7.272135 13.47168 7.495117 13.803711 7.77832 14.086914 C 8.061523 14.370117 8.393555 14.5931 8.774414 14.755859 C 9.155273 14.91862 9.560547 15 9.990234 15 C 10.537109 15 11.043294 14.869792 11.508789 14.609375 C 11.974283 14.348959 12.353516 13.987631 12.646484 13.525391 Z - - + \ No newline at end of file diff --git a/src/Files.App/Styles/PropertiesStyles.xaml b/src/Files.App/Styles/PropertiesStyles.xaml index e27051dc66f8..f042f9542702 100644 --- a/src/Files.App/Styles/PropertiesStyles.xaml +++ b/src/Files.App/Styles/PropertiesStyles.xaml @@ -1,4 +1,4 @@ - + + + xmlns:wctlabs="using:CommunityToolkit.Labs.WinUI"> - - \ No newline at end of file + diff --git a/src/Files.App/Styles/SidebarStyles.xaml b/src/Files.App/Styles/SidebarStyles.xaml deleted file mode 100644 index 069c75b77419..000000000000 --- a/src/Files.App/Styles/SidebarStyles.xaml +++ /dev/null @@ -1,471 +0,0 @@ - - - - 300 - -300 - 56 - -56 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Files.App/Styles/StatusCenterInfoBadgeStyles.xaml b/src/Files.App/Styles/StatusCenterInfoBadgeStyles.xaml new file mode 100644 index 000000000000..744569cd754f --- /dev/null +++ b/src/Files.App/Styles/StatusCenterInfoBadgeStyles.xaml @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App/Styles/StatusCenterStyles.xaml b/src/Files.App/Styles/StatusCenterStyles.xaml index cd66426f0bef..52921015c1fb 100644 --- a/src/Files.App/Styles/StatusCenterStyles.xaml +++ b/src/Files.App/Styles/StatusCenterStyles.xaml @@ -1,9 +1,9 @@ - + + xmlns:wctconverters="using:CommunityToolkit.WinUI.Converters"> diff --git a/src/Files.App/Styles/TextBlockStyles.xaml b/src/Files.App/Styles/TextBlockStyles.xaml index e599f48ea488..0e5a448a6386 100644 --- a/src/Files.App/Styles/TextBlockStyles.xaml +++ b/src/Files.App/Styles/TextBlockStyles.xaml @@ -1,4 +1,4 @@ - + - \ No newline at end of file diff --git a/src/Files.App/Styles/ToolbarButtonStyle.xaml b/src/Files.App/Styles/ToolbarButtonStyle.xaml index e32ffad69fc8..11b452a8a4ca 100644 --- a/src/Files.App/Styles/ToolbarButtonStyle.xaml +++ b/src/Files.App/Styles/ToolbarButtonStyle.xaml @@ -1,4 +1,4 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Files.App/UserControls/AddressToolbar.xaml.cs b/src/Files.App/UserControls/AddressToolbar.xaml.cs deleted file mode 100644 index a993603e223e..000000000000 --- a/src/Files.App/UserControls/AddressToolbar.xaml.cs +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Microsoft.UI.Input; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; -using Microsoft.UI.Xaml.Input; -using Microsoft.UI.Xaml.Media; -using Windows.System; -using FocusManager = Microsoft.UI.Xaml.Input.FocusManager; - -namespace Files.App.UserControls -{ - public sealed partial class AddressToolbar : UserControl - { - private readonly IUserSettingsService userSettingsService = Ioc.Default.GetRequiredService(); - public ICommandManager Commands { get; } = Ioc.Default.GetRequiredService(); - - // Using a DependencyProperty as the backing store for ShowOngoingTasks. This enables animation, styling, binding, etc... - public static readonly DependencyProperty ShowOngoingTasksProperty = - DependencyProperty.Register(nameof(ShowOngoingTasks), typeof(bool), typeof(AddressToolbar), new(null)); - public bool ShowOngoingTasks - { - get => (bool)GetValue(ShowOngoingTasksProperty); - set => SetValue(ShowOngoingTasksProperty, value); - } - - // Using a DependencyProperty as the backing store for ShowSettingsButton. This enables animation, styling, binding, etc... - public static readonly DependencyProperty ShowSettingsButtonProperty = - DependencyProperty.Register(nameof(ShowSettingsButton), typeof(bool), typeof(AddressToolbar), new(null)); - public bool ShowSettingsButton - { - get => (bool)GetValue(dp: ShowSettingsButtonProperty); - set => SetValue(ShowSettingsButtonProperty, value); - } - - // Using a DependencyProperty as the backing store for CollapseSearchBox. This enables animation, styling, binding, etc... - public static readonly DependencyProperty ShowSearchBoxProperty = - DependencyProperty.Register(nameof(ShowSearchBox), typeof(bool), typeof(AddressToolbar), new(null)); - public bool ShowSearchBox - { - get { return (bool)GetValue(ShowSearchBoxProperty); } - set { SetValue(ShowSearchBoxProperty, value); } - } - - // Using a DependencyProperty as the backing store for ViewModel. This enables animation, styling, binding, etc... - public static readonly DependencyProperty ViewModelProperty = - DependencyProperty.Register(nameof(ViewModel), typeof(AddressToolbarViewModel), typeof(AddressToolbar), new PropertyMetadata(null)); - public AddressToolbarViewModel? ViewModel - { - get => (AddressToolbarViewModel)GetValue(ViewModelProperty); - set => SetValue(ViewModelProperty, value); - } - - public StatusCenterViewModel? OngoingTasksViewModel { get; set; } - - public AddressToolbar() => InitializeComponent(); - - private void NavToolbar_Loading(FrameworkElement _, object e) - { - Loading -= NavToolbar_Loading; - if (OngoingTasksViewModel is not null) - OngoingTasksViewModel.NewItemAdded += OngoingTasksActions_ProgressBannerPosted; - } - - private void VisiblePath_Loaded(object _, RoutedEventArgs e) - { - // AutoSuggestBox won't receive focus unless it's fully loaded - VisiblePath.Focus(FocusState.Programmatic); - - if (DependencyObjectHelpers.FindChild(VisiblePath) is TextBox textBox) - { - if (textBox.Text.StartsWith(">")) - textBox.Select(1, textBox.Text.Length - 1); - else - textBox.SelectAll(); - } - } - - private void ManualPathEntryItem_Click(object _, PointerRoutedEventArgs e) - { - if (e.Pointer.PointerDeviceType is PointerDeviceType.Mouse) - { - var ptrPt = e.GetCurrentPoint(NavToolbar); - if (ptrPt.Properties.IsMiddleButtonPressed) - return; - } - ViewModel.IsEditModeEnabled = true; - } - - private void VisiblePath_KeyDown(object _, KeyRoutedEventArgs e) - { - if (e.Key is VirtualKey.Escape) - ViewModel.IsEditModeEnabled = false; - } - private void VisiblePath_LostFocus(object _, RoutedEventArgs e) - { - if (App.AppModel.IsMainWindowClosed) - return; - - var element = FocusManager.GetFocusedElement(MainWindow.Instance.Content.XamlRoot); - if (element is FlyoutBase or AppBarButton or Popup) - return; - - var control = element as Control; - if (control is null) - { - if (ViewModel.IsEditModeEnabled) - ViewModel.IsEditModeEnabled = false; - return; - } - - if (control.FocusState is not FocusState.Programmatic and not FocusState.Keyboard) - ViewModel.IsEditModeEnabled = false; - else if (ViewModel.IsEditModeEnabled) - VisiblePath.Focus(FocusState.Programmatic); - } - - private void SearchRegion_OnGotFocus(object sender, RoutedEventArgs e) => ViewModel.SearchRegion_GotFocus(sender, e); - private void SearchRegion_LostFocus(object sender, RoutedEventArgs e) => ViewModel.SearchRegion_LostFocus(sender, e); - private void SearchRegion_AccessKeyInvoked(UIElement sender, AccessKeyInvokedEventArgs args) - { - // Suppress access key invocation if any dialog is open - if (VisualTreeHelper.GetOpenPopupsForXamlRoot(MainWindow.Instance.Content.XamlRoot).Any()) - args.Handled = true; - else - sender.Focus(FocusState.Keyboard); - } - - private void VisiblePath_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args) - => ViewModel.VisiblePath_QuerySubmitted(sender, args); - - private void OngoingTasksActions_ProgressBannerPosted(object? _, StatusCenterItem e) - { - if (OngoingTasksViewModel is not null) - OngoingTasksViewModel.NewItemAdded -= OngoingTasksActions_ProgressBannerPosted; - - // Displays a teaching tip the first time a banner is posted - if (userSettingsService.AppSettingsService.ShowStatusCenterTeachingTip) - { - StatusCenterTeachingTip.IsOpen = true; - userSettingsService.AppSettingsService.ShowStatusCenterTeachingTip = false; - } - } - - private void Button_AccessKeyInvoked(UIElement sender, AccessKeyInvokedEventArgs args) - { - // Suppress access key invocation if any dialog is open - if (VisualTreeHelper.GetOpenPopupsForXamlRoot(MainWindow.Instance.Content.XamlRoot).Any()) - args.Handled = true; - } - } -} diff --git a/src/Files.App/UserControls/ComboBoxEx/ComboBoxEx.cs b/src/Files.App/UserControls/ComboBoxEx/ComboBoxEx.cs index e31007ed4985..fa63796ca6bd 100644 --- a/src/Files.App/UserControls/ComboBoxEx/ComboBoxEx.cs +++ b/src/Files.App/UserControls/ComboBoxEx/ComboBoxEx.cs @@ -1,13 +1,12 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Windows.Foundation; namespace Files.App.UserControls { - public class ComboBoxEx : ComboBox + public partial class ComboBoxEx : ComboBox { double _cachedWidth; diff --git a/src/Files.App/UserControls/DataGridHeader.xaml b/src/Files.App/UserControls/DataGridHeader.xaml index 10e829cea616..c4fa4416da7e 100644 --- a/src/Files.App/UserControls/DataGridHeader.xaml +++ b/src/Files.App/UserControls/DataGridHeader.xaml @@ -1,4 +1,4 @@ - + "SortAscending", + SortDirection.Descending => "SortDescending", + _ => "Unsorted", + }, + true); } public DataGridHeader() { InitializeComponent(); } - - public event PropertyChangedEventHandler PropertyChanged; - - private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } } -} \ No newline at end of file +} diff --git a/src/Files.App/UserControls/FileIcon.xaml b/src/Files.App/UserControls/FileIcon.xaml index 97ed5d506cde..165c655639a9 100644 --- a/src/Files.App/UserControls/FileIcon.xaml +++ b/src/Files.App/UserControls/FileIcon.xaml @@ -1,4 +1,4 @@ - + SetValue(FileIconImageSourceProperty, value); } - public static DependencyProperty FileIconImageDataProperty { get; } = DependencyProperty.Register(nameof(FileIconImageData), typeof(byte[]), typeof(FileIcon), null); + public static readonly DependencyProperty FileIconImageDataProperty = DependencyProperty.Register(nameof(FileIconImageData), typeof(byte[]), typeof(FileIcon), null); public byte[] FileIconImageData { @@ -67,7 +64,7 @@ public byte[] FileIconImageData SetValue(FileIconImageDataProperty, value); if (value is not null) { - UpdateImageSourceAsync(); + _ = UpdateImageSourceAsync(); } } } diff --git a/src/Files.App/UserControls/FilePreviews/BasicPreview.xaml b/src/Files.App/UserControls/FilePreviews/BasicPreview.xaml index cca2477b198b..e29a20b8e42f 100644 --- a/src/Files.App/UserControls/FilePreviews/BasicPreview.xaml +++ b/src/Files.App/UserControls/FilePreviews/BasicPreview.xaml @@ -1,4 +1,4 @@ - + + + + + + diff --git a/src/Files.App/UserControls/FilePreviews/MarkdownPreview.xaml.cs b/src/Files.App/UserControls/FilePreviews/MarkdownPreview.xaml.cs index 4b440f5544c1..d088c598aa0c 100644 --- a/src/Files.App/UserControls/FilePreviews/MarkdownPreview.xaml.cs +++ b/src/Files.App/UserControls/FilePreviews/MarkdownPreview.xaml.cs @@ -1,9 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using CommunityToolkit.WinUI.UI.Controls; +using CommunityToolkit.Labs.WinUI.MarkdownTextBlock; using Files.App.ViewModels.Previews; +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media; using Windows.System; namespace Files.App.UserControls.FilePreviews @@ -16,12 +18,15 @@ public MarkdownPreview(MarkdownPreviewViewModel model) { ViewModel = model; InitializeComponent(); + + // Workaround for https://github.com/CommunityToolkit/Labs-Windows/issues/611 + PreviewMarkdownTextBlock.Config = MarkdownConfig.Default; + PreviewMarkdownTextBlock.Config.Themes.HeadingForeground = (Brush)Application.Current.Resources["TextControlForeground"]; } - private async void PreviewMarkdownTextBlock_LinkClicked(object sender, LinkClickedEventArgs e) + private async void PreviewMarkdownTextBlock_OnLinkClicked(object sender, CommunityToolkit.Labs.WinUI.MarkdownTextBlock.LinkClickedEventArgs e) { - if (Uri.TryCreate(e.Link, UriKind.Absolute, out Uri? link)) - await Launcher.LaunchUriAsync(link); + await Launcher.LaunchUriAsync(e.Uri); } } } diff --git a/src/Files.App/UserControls/FilePreviews/MediaPreview.xaml b/src/Files.App/UserControls/FilePreviews/MediaPreview.xaml index ec58d8545a6d..455f0cf7d4e0 100644 --- a/src/Files.App/UserControls/FilePreviews/MediaPreview.xaml +++ b/src/Files.App/UserControls/FilePreviews/MediaPreview.xaml @@ -1,4 +1,4 @@ - + + + + + @@ -39,7 +39,7 @@ - + diff --git a/src/Files.App/UserControls/FolderEmptyIndicator.xaml.cs b/src/Files.App/UserControls/FolderEmptyIndicator.xaml.cs index a0bdde379241..a038a7af57a1 100644 --- a/src/Files.App/UserControls/FolderEmptyIndicator.xaml.cs +++ b/src/Files.App/UserControls/FolderEmptyIndicator.xaml.cs @@ -1,27 +1,15 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.App.Extensions; -using Microsoft.UI.Xaml; +using CommunityToolkit.WinUI; using Microsoft.UI.Xaml.Controls; -// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236 - namespace Files.App.UserControls { public sealed partial class FolderEmptyIndicator : UserControl { - public EmptyTextType EmptyTextType - { - get { return (EmptyTextType)GetValue(EmptyTextTypeProperty); } - set { SetValue(EmptyTextTypeProperty, value); } - } - - // Using a DependencyProperty as the backing store for EmptyTextType. This enables animation, styling, binding, etc... - public static readonly DependencyProperty EmptyTextTypeProperty = - DependencyProperty.Register("EmptyTextType", typeof(EmptyTextType), typeof(FolderEmptyIndicator), new PropertyMetadata(null)); - - private string GetTranslated(string resourceName) => resourceName.GetLocalizedResource(); + [GeneratedDependencyProperty] + public partial EmptyTextType EmptyTextType { get; set; } public FolderEmptyIndicator() { @@ -35,4 +23,4 @@ public enum EmptyTextType FolderEmpty, NoSearchResultsFound, } -} \ No newline at end of file +} diff --git a/src/Files.App/UserControls/InnerNavigationToolbar.xaml b/src/Files.App/UserControls/InnerNavigationToolbar.xaml deleted file mode 100644 index 6d80e3d36cc3..000000000000 --- a/src/Files.App/UserControls/InnerNavigationToolbar.xaml +++ /dev/null @@ -1,1224 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - True - False - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Files.App/UserControls/InnerNavigationToolbar.xaml.cs b/src/Files.App/UserControls/InnerNavigationToolbar.xaml.cs deleted file mode 100644 index d79abf7fa353..000000000000 --- a/src/Files.App/UserControls/InnerNavigationToolbar.xaml.cs +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Input; -using Microsoft.UI.Xaml.Media; -using Microsoft.UI.Xaml.Media.Imaging; -using System.IO; - -namespace Files.App.UserControls -{ - public sealed partial class InnerNavigationToolbar : UserControl - { - public InnerNavigationToolbar() - { - InitializeComponent(); - } - - public IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetRequiredService(); - public ICommandManager Commands { get; } = Ioc.Default.GetRequiredService(); - public IModifiableCommandManager ModifiableCommands { get; } = Ioc.Default.GetRequiredService(); - - private readonly IAddItemService addItemService = Ioc.Default.GetRequiredService(); - - public AppModel AppModel => App.AppModel; - - public AddressToolbarViewModel? ViewModel - { - get => (AddressToolbarViewModel)GetValue(ViewModelProperty); - set => SetValue(ViewModelProperty, value); - } - - // Using a DependencyProperty as the backing store for ViewModel. This enables animation, styling, binding, etc... - public static readonly DependencyProperty ViewModelProperty = - DependencyProperty.Register(nameof(ViewModel), typeof(AddressToolbarViewModel), typeof(InnerNavigationToolbar), new PropertyMetadata(null)); - - public bool ShowViewControlButton - { - get { return (bool)GetValue(ShowViewControlButtonProperty); } - set { SetValue(ShowViewControlButtonProperty, value); } - } - - // Using a DependencyProperty as the backing store for ShowViewControlButton. This enables animation, styling, binding, etc... - public static readonly DependencyProperty ShowViewControlButtonProperty = - DependencyProperty.Register("ShowViewControlButton", typeof(bool), typeof(AddressToolbar), new PropertyMetadata(null)); - - public bool ShowPreviewPaneButton - { - get { return (bool)GetValue(ShowPreviewPaneButtonProperty); } - set { SetValue(ShowPreviewPaneButtonProperty, value); } - } - - // Using a DependencyProperty as the backing store for ShowPreviewPaneButton. This enables animation, styling, binding, etc... - public static readonly DependencyProperty ShowPreviewPaneButtonProperty = - DependencyProperty.Register("ShowPreviewPaneButton", typeof(bool), typeof(AddressToolbar), new PropertyMetadata(null)); - private void NewEmptySpace_Opening(object sender, object e) - { - var shell = NewEmptySpace.Items.Where(x => (x.Tag as string) == "CreateNewFile").Reverse().ToList(); - shell.ForEach(x => NewEmptySpace.Items.Remove(x)); - if (!ViewModel.InstanceViewModel.CanCreateFileInPage) - return; - - var cachedNewContextMenuEntries = addItemService.GetEntries(); - if (cachedNewContextMenuEntries is null) - return; - - var separatorIndex = NewEmptySpace.Items.IndexOf(NewEmptySpace.Items.Single(x => x.Name == "NewMenuFileFolderSeparator")); - - ushort key = 0; - string keyFormat = $"D{cachedNewContextMenuEntries.Count.ToString().Length}"; - - foreach (var newEntry in Enumerable.Reverse(cachedNewContextMenuEntries)) - { - MenuFlyoutItem menuLayoutItem; - if (!string.IsNullOrEmpty(newEntry.IconBase64)) - { - byte[] bitmapData = Convert.FromBase64String(newEntry.IconBase64); - using var ms = new MemoryStream(bitmapData); - var image = new BitmapImage(); - _ = image.SetSourceAsync(ms.AsRandomAccessStream()); - menuLayoutItem = new MenuFlyoutItemWithImage() - { - Text = newEntry.Name, - BitmapIcon = image, - Tag = "CreateNewFile" - }; - } - else - { - menuLayoutItem = new MenuFlyoutItem() - { - Text = newEntry.Name, - Icon = new FontIcon - { - Glyph = "\xE7C3" - }, - Tag = "CreateNewFile" - }; - } - menuLayoutItem.AccessKey = (cachedNewContextMenuEntries.Count + 1 - (++key)).ToString(keyFormat); - menuLayoutItem.Command = ViewModel.CreateNewFileCommand; - menuLayoutItem.CommandParameter = newEntry; - NewEmptySpace.Items.Insert(separatorIndex + 1, menuLayoutItem); - } - } - - private void SortGroup_AccessKeyInvoked(UIElement sender, AccessKeyInvokedEventArgs args) - { - if (sender is MenuFlyoutSubItem menu) - { - var items = menu.Items - .TakeWhile(item => item is not MenuFlyoutSeparator) - .Where(item => item.IsEnabled) - .ToList(); - - string format = $"D{items.Count.ToString().Length}"; - - for (ushort index = 0; index < items.Count; ++index) - { - items[index].AccessKey = (index+1).ToString(format); - } - } - - } - - private void AppBarButton_AccessKeyInvoked(UIElement sender, AccessKeyInvokedEventArgs args) - { - // Suppress access key invocation if any dialog is open - if (VisualTreeHelper.GetOpenPopupsForXamlRoot(MainWindow.Instance.Content.XamlRoot).Any()) - args.Handled = true; - } - } -} \ No newline at end of file diff --git a/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.Properties.cs b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.Properties.cs index 3194cdf177ba..4f6b418be639 100644 --- a/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.Properties.cs +++ b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.Properties.cs @@ -1,8 +1,7 @@ // Copyright (c) 2023 Files Community -// Licensed under the MIT License. See the LICENSE. +// Licensed under the MIT License. using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; namespace Files.App.UserControls.KeyboardShortcut { @@ -12,7 +11,7 @@ public sealed partial class KeyboardShortcut DependencyProperty.Register( nameof(ItemType), typeof(KeyboardShortcutItemKind), - typeof(KeyboardShortcutItem), + typeof(KeyboardShortcut), new PropertyMetadata(defaultValue: KeyboardShortcutItemKind.Outlined, (d, e) => ((KeyboardShortcutItem)d).OnItemTypePropertyChanged())); public KeyboardShortcutItemKind ItemType @@ -25,7 +24,7 @@ public KeyboardShortcutItemKind ItemType DependencyProperty.Register( nameof(Size), typeof(KeyboardShortcutItemSize), - typeof(KeyboardShortcutItem), + typeof(KeyboardShortcut), new PropertyMetadata(defaultValue: KeyboardShortcutItemSize.Small, (d, e) => ((KeyboardShortcutItem)d).OnSizePropertyChanged())); public KeyboardShortcutItemSize Size @@ -39,7 +38,7 @@ public KeyboardShortcutItemSize Size nameof(HotKeys), typeof(HotKeyCollection), typeof(KeyboardShortcut), - new PropertyMetadata(defaultValue: new(), (d, e) => ((KeyboardShortcut)d).OnHotKeysPropertyChanged())); + new PropertyMetadata(defaultValue: new HotKeyCollection(), (d, e) => ((KeyboardShortcut)d).OnHotKeysPropertyChanged())); public HotKeyCollection HotKeys { diff --git a/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.cs b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.cs index 3d7c4030affe..61711bed0532 100644 --- a/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.cs +++ b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.cs @@ -1,5 +1,5 @@ // Copyright (c) 2023 Files Community -// Licensed under the MIT License. See the LICENSE. +// Licensed under the MIT License. using Microsoft.UI.Xaml.Controls; @@ -50,7 +50,7 @@ private async void OnHotKeysChanged() items.Add(new() { Text = ",", ItemType = KeyboardShortcutItemKind.TextOnly, Size = Size }); } - switch(item.Key, item.Modifier) + switch (item.Key, item.Modifier) { // No keys or modifiers specified case (Keys.None, KeyModifiers.None): diff --git a/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.xaml b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.xaml index 123627499e07..f22c08df2209 100644 --- a/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.xaml +++ b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.xaml @@ -1,4 +1,4 @@ - + (); + /// + /// Event fired when an item's tags are updated (added/removed). + /// Used to refresh groups in ShellViewModel. + /// + public event EventHandler? TagsChanged; + public IEnumerable SelectedItems { get; } public FileTagsContextMenu(IEnumerable selectedItems) @@ -28,7 +34,7 @@ public FileTagsContextMenu(IEnumerable selectedItems) }; tagItem.Icon = new PathIcon() { - Data = (Geometry)XamlBindingHelper.ConvertValue(typeof(Geometry), (string)Application.Current.Resources["ColorIconFilledTag"]), + Data = (Geometry)XamlBindingHelper.ConvertValue(typeof(Geometry), (string)Application.Current.Resources["App.Theme.PathIcon.FilledTag"]), Foreground = new SolidColorBrush(ColorHelpers.FromHex(tag.Color)) }; tagItem.Click += TagItem_Click; @@ -63,6 +69,7 @@ private void Item_Opening(object? sender, object e) // go through each tag and find the common one for all files var commonFileTags = SelectedItems .Select(x => x?.FileTags ?? Enumerable.Empty()) + .DefaultIfEmpty(Enumerable.Empty()) .Aggregate((x, y) => x.Intersect(y)) .Select(x => Items.FirstOrDefault(y => x == ((TagViewModel)y.Tag)?.Uid)); @@ -80,6 +87,7 @@ private void RemoveFileTag(IEnumerable selectedListedItems, TagViewM selectedItem.FileTags = tagList; } } + TagsChanged?.Invoke(this, EventArgs.Empty); } private void AddFileTag(IEnumerable selectedListedItems, TagViewModel added) @@ -92,6 +100,7 @@ private void AddFileTag(IEnumerable selectedListedItems, TagViewMode selectedItem.FileTags = [.. existingTags, added.Uid]; } } + TagsChanged?.Invoke(this, EventArgs.Empty); } } } diff --git a/src/Files.App/UserControls/Menus/MenuFlyoutHeaderItem.xaml b/src/Files.App/UserControls/Menus/MenuFlyoutHeaderItem.xaml new file mode 100644 index 000000000000..5f66c0af7c38 --- /dev/null +++ b/src/Files.App/UserControls/Menus/MenuFlyoutHeaderItem.xaml @@ -0,0 +1,78 @@ + + + + + + + diff --git a/src/Files.App/UserControls/Menus/MenuFlyoutHeaderItem.xaml.cs b/src/Files.App/UserControls/Menus/MenuFlyoutHeaderItem.xaml.cs new file mode 100644 index 000000000000..2b393c339708 --- /dev/null +++ b/src/Files.App/UserControls/Menus/MenuFlyoutHeaderItem.xaml.cs @@ -0,0 +1,15 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Controls; + +namespace Files.App.UserControls +{ + public sealed partial class MenuFlyoutHeaderItem : MenuFlyoutItem + { + public MenuFlyoutHeaderItem() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/src/Files.App/UserControls/Menus/MenuFlyoutItemWithImage.xaml b/src/Files.App/UserControls/Menus/MenuFlyoutItemWithImage.xaml index 2d21155f1d0c..78c8eb0bb243 100644 --- a/src/Files.App/UserControls/Menus/MenuFlyoutItemWithImage.xaml +++ b/src/Files.App/UserControls/Menus/MenuFlyoutItemWithImage.xaml @@ -1,4 +1,4 @@ - + + diff --git a/src/Files.App/UserControls/Menus/MenuFlyoutItemWithImage.xaml.cs b/src/Files.App/UserControls/Menus/MenuFlyoutItemWithImage.xaml.cs index 72c794e0ee9b..0da88fa1d4e2 100644 --- a/src/Files.App/UserControls/Menus/MenuFlyoutItemWithImage.xaml.cs +++ b/src/Files.App/UserControls/Menus/MenuFlyoutItemWithImage.xaml.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; diff --git a/src/Files.App/UserControls/Menus/MenuFlyoutItemWithOpacityIcon.xaml b/src/Files.App/UserControls/Menus/MenuFlyoutItemWithOpacityIcon.xaml deleted file mode 100644 index 75cd73c6a0a6..000000000000 --- a/src/Files.App/UserControls/Menus/MenuFlyoutItemWithOpacityIcon.xaml +++ /dev/null @@ -1,151 +0,0 @@ - - - - - - - diff --git a/src/Files.App/UserControls/Menus/MenuFlyoutItemWithOpacityIcon.xaml.cs b/src/Files.App/UserControls/Menus/MenuFlyoutItemWithOpacityIcon.xaml.cs deleted file mode 100644 index 64144a5e5d46..000000000000 --- a/src/Files.App/UserControls/Menus/MenuFlyoutItemWithOpacityIcon.xaml.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; - -// The User Control element template is documented at https://go.microsoft.com/fwlink/?LinkId=234236 - -namespace Files.App.UserControls -{ - public sealed partial class MenuFlyoutItemWithOpacityIcon: MenuFlyoutItem - { - public Style OpacityStyle - { - get { return (Style)GetValue(OpacityStyleProperty); } - set { SetValue(OpacityStyleProperty, value); } - } - - public static readonly DependencyProperty OpacityStyleProperty = - DependencyProperty.Register("OpacityStyle", typeof(Style), typeof(MenuFlyoutItemWithOpacityIcon), new PropertyMetadata(null, OnOpacityStyleChanged)); - - private static void OnOpacityStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - (d as MenuFlyoutItem).Icon = e.NewValue is not null ? new IconSourceElement() : null; - } - - public MenuFlyoutItemWithOpacityIcon() - { - InitializeComponent(); - } - } -} \ No newline at end of file diff --git a/src/Files.App/UserControls/Menus/MenuFlyoutItemWithThemedIcon.xaml b/src/Files.App/UserControls/Menus/MenuFlyoutItemWithThemedIcon.xaml new file mode 100644 index 000000000000..38a9043e0aa5 --- /dev/null +++ b/src/Files.App/UserControls/Menus/MenuFlyoutItemWithThemedIcon.xaml @@ -0,0 +1,153 @@ + + + + + + + diff --git a/src/Files.App/UserControls/Menus/MenuFlyoutItemWithThemedIcon.xaml.cs b/src/Files.App/UserControls/Menus/MenuFlyoutItemWithThemedIcon.xaml.cs new file mode 100644 index 000000000000..834a77f6a0d1 --- /dev/null +++ b/src/Files.App/UserControls/Menus/MenuFlyoutItemWithThemedIcon.xaml.cs @@ -0,0 +1,32 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +// The User Control element template is documented at https://go.microsoft.com/fwlink/?LinkId=234236 + +namespace Files.App.UserControls +{ + public sealed partial class MenuFlyoutItemWithThemedIcon : MenuFlyoutItem + { + public Style ThemedIconStyle + { + get { return (Style)GetValue(ThemedIconStyleProperty); } + set { SetValue(ThemedIconStyleProperty, value); } + } + + public static readonly DependencyProperty ThemedIconStyleProperty = + DependencyProperty.Register("ThemedIconStyle", typeof(Style), typeof(MenuFlyoutItemWithThemedIcon), new PropertyMetadata(null, OnThemedIconStyleChanged)); + + private static void OnThemedIconStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + (d as MenuFlyoutItem).Icon = e.NewValue is not null ? new IconSourceElement() : null; + } + + public MenuFlyoutItemWithThemedIcon() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/src/Files.App/UserControls/Menus/MenuFlyoutSubItemCustomProperties.cs b/src/Files.App/UserControls/Menus/MenuFlyoutSubItemCustomProperties.cs index 3f6a2fc4ee54..261aa6367169 100644 --- a/src/Files.App/UserControls/Menus/MenuFlyoutSubItemCustomProperties.cs +++ b/src/Files.App/UserControls/Menus/MenuFlyoutSubItemCustomProperties.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; diff --git a/src/Files.App/UserControls/Menus/ToggleMenuFlyoutItemWithOpacityIcon.xaml b/src/Files.App/UserControls/Menus/ToggleMenuFlyoutItemWithOpacityIcon.xaml deleted file mode 100644 index dc465aacd554..000000000000 --- a/src/Files.App/UserControls/Menus/ToggleMenuFlyoutItemWithOpacityIcon.xaml +++ /dev/null @@ -1,167 +0,0 @@ - - - - - - - diff --git a/src/Files.App/UserControls/Menus/ToggleMenuFlyoutItemWithOpacityIcon.xaml.cs b/src/Files.App/UserControls/Menus/ToggleMenuFlyoutItemWithOpacityIcon.xaml.cs deleted file mode 100644 index a122272b1cc0..000000000000 --- a/src/Files.App/UserControls/Menus/ToggleMenuFlyoutItemWithOpacityIcon.xaml.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; - -// The User Control element template is documented at https://go.microsoft.com/fwlink/?LinkId=234236 - -namespace Files.App.UserControls -{ - public sealed partial class ToggleMenuFlyoutItemWithOpacityIcon: ToggleMenuFlyoutItem - { - public Style OpacityStyle - { - get { return (Style)GetValue(OpacityStyleProperty); } - set { SetValue(OpacityStyleProperty, value); } - } - - public static readonly DependencyProperty OpacityStyleProperty = - DependencyProperty.Register("OpacityStyle", typeof(Style), typeof(ToggleMenuFlyoutItemWithOpacityIcon), new PropertyMetadata(null, OnOpacityStyleChanged)); - - private static void OnOpacityStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - (d as ToggleMenuFlyoutItem).Icon = e.NewValue is not null ? new IconSourceElement() : null; - } - - public ToggleMenuFlyoutItemWithOpacityIcon() - { - InitializeComponent(); - } - } -} \ No newline at end of file diff --git a/src/Files.App/UserControls/Menus/ToggleMenuFlyoutItemWithThemedIcon.xaml b/src/Files.App/UserControls/Menus/ToggleMenuFlyoutItemWithThemedIcon.xaml new file mode 100644 index 000000000000..c907b4ac5535 --- /dev/null +++ b/src/Files.App/UserControls/Menus/ToggleMenuFlyoutItemWithThemedIcon.xaml @@ -0,0 +1,169 @@ + + + + + + + diff --git a/src/Files.App/UserControls/Menus/ToggleMenuFlyoutItemWithThemedIcon.xaml.cs b/src/Files.App/UserControls/Menus/ToggleMenuFlyoutItemWithThemedIcon.xaml.cs new file mode 100644 index 000000000000..88f0cd7d474b --- /dev/null +++ b/src/Files.App/UserControls/Menus/ToggleMenuFlyoutItemWithThemedIcon.xaml.cs @@ -0,0 +1,32 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +// The User Control element template is documented at https://go.microsoft.com/fwlink/?LinkId=234236 + +namespace Files.App.UserControls +{ + public sealed partial class ToggleMenuFlyoutItemWithThemedIcon : ToggleMenuFlyoutItem + { + public Style ThemedIconStyle + { + get { return (Style)GetValue(ThemedIconStyleProperty); } + set { SetValue(ThemedIconStyleProperty, value); } + } + + public static readonly DependencyProperty ThemedIconStyleProperty = + DependencyProperty.Register("ThemedIconStyle", typeof(Style), typeof(ToggleMenuFlyoutItemWithThemedIcon), new PropertyMetadata(null, OnThemedIconStyleChanged)); + + private static void OnThemedIconStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + (d as ToggleMenuFlyoutItem).Icon = e.NewValue is not null ? new IconSourceElement() : null; + } + + public ToggleMenuFlyoutItemWithThemedIcon() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/src/Files.App/UserControls/NavigationToolbar.xaml b/src/Files.App/UserControls/NavigationToolbar.xaml new file mode 100644 index 000000000000..1cbb9df0ebf5 --- /dev/null +++ b/src/Files.App/UserControls/NavigationToolbar.xaml @@ -0,0 +1,630 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Files.App/UserControls/NavigationToolbar.xaml.cs b/src/Files.App/UserControls/NavigationToolbar.xaml.cs new file mode 100644 index 000000000000..2640525588c9 --- /dev/null +++ b/src/Files.App/UserControls/NavigationToolbar.xaml.cs @@ -0,0 +1,471 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using CommunityToolkit.WinUI; +using Files.App.Controls; +using Microsoft.UI.Input; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +using Windows.AI.Actions.Hosting; +using Windows.System; +using Windows.UI.Core; + +namespace Files.App.UserControls +{ + public sealed partial class NavigationToolbar : UserControl + { + // Dependency injections + + private readonly IUserSettingsService userSettingsService = Ioc.Default.GetRequiredService(); + private readonly MainPageViewModel MainPageViewModel = Ioc.Default.GetRequiredService(); + private readonly ICommandManager Commands = Ioc.Default.GetRequiredService(); + private readonly StatusCenterViewModel OngoingTasksViewModel = Ioc.Default.GetRequiredService(); + private readonly IContentPageContext ContentPageContext = Ioc.Default.GetRequiredService(); + + // Properties + + [GeneratedDependencyProperty] + public partial bool IsSidebarPaneOpenToggleButtonVisible { get; set; } + + [GeneratedDependencyProperty] + public partial bool ShowOngoingTasks { get; set; } + + [GeneratedDependencyProperty] + public partial bool ShowSettingsButton { get; set; } + + [GeneratedDependencyProperty] + public partial NavigationToolbarViewModel? ViewModel { get; set; } + + // Constructor + + public NavigationToolbar() + { + InitializeComponent(); + } + + // Methods + + private void NavToolbar_Loading(FrameworkElement _, object e) + { + Loading -= NavToolbar_Loading; + if (OngoingTasksViewModel is not null) + OngoingTasksViewModel.NewItemAdded += OngoingTasksActions_ProgressBannerPosted; + } + + private void OngoingTasksActions_ProgressBannerPosted(object? _, StatusCenterItem e) + { + if (OngoingTasksViewModel is not null) + OngoingTasksViewModel.NewItemAdded -= OngoingTasksActions_ProgressBannerPosted; + + // Displays a teaching tip the first time a banner is posted + if (userSettingsService.AppSettingsService.ShowStatusCenterTeachingTip) + { + StatusCenterTeachingTip.IsOpen = true; + userSettingsService.AppSettingsService.ShowStatusCenterTeachingTip = false; + } + } + + private void Button_AccessKeyInvoked(UIElement sender, AccessKeyInvokedEventArgs args) + { + // Suppress access key invocation if any dialog is open + if (VisualTreeHelper.GetOpenPopupsForXamlRoot(MainWindow.Instance.Content.XamlRoot).Any()) + args.Handled = true; + } + + private async void BackHistoryFlyout_Opening(object? sender, object e) + { + var shellPage = Ioc.Default.GetRequiredService().ShellPage; + if (shellPage is null) + return; + + await AddHistoryItemsAsync(shellPage.BackwardStack, BackHistoryFlyout.Items, true); + } + + private async void ForwardHistoryFlyout_Opening(object? sender, object e) + { + var shellPage = Ioc.Default.GetRequiredService().ShellPage; + if (shellPage is null) + return; + + await AddHistoryItemsAsync(shellPage.ForwardStack, ForwardHistoryFlyout.Items, false); + } + + private async Task AddHistoryItemsAsync(IEnumerable items, IList flyoutItems, bool isBackMode) + { + // This may not seem performant, however it's the most viable trade-off to make. + // Instead of constantly keeping track of back/forward stack and performing lookups + // (which may degrade performance), we only add items in bulk when it's needed. + // There's also a high chance the user might not use the feature at all in which case + // the former approach would just waste extra performance gain + + flyoutItems.Clear(); + foreach (var item in items.Reverse()) + { + if (item.Parameter is not NavigationArguments args || args.NavPathParam is null) + continue; + + var fileName = SystemIO.Path.GetFileName(args.NavPathParam); + + // The fileName is empty if the path is (root) drive path + if (string.IsNullOrEmpty(fileName)) + fileName = args.NavPathParam; + + var flyoutItem = new MenuFlyoutItem + { + Icon = new FontIcon() { Glyph = "\uE8B7" }, // Placeholder icon + Text = fileName, + Command = new RelayCommand(HistoryItemClicked), + CommandParameter = new ToolbarHistoryItemModel(item, isBackMode) + }; + + flyoutItems?.Add(flyoutItem); + + // Start loading the thumbnail in the background + _ = LoadFlyoutItemIconAsync(flyoutItem, args.NavPathParam); + + async Task LoadFlyoutItemIconAsync(MenuFlyoutItem flyoutItem, string path) + { + var imageSource = await NavigationHelpers.GetIconForPathAsync(path); + + if (imageSource is not null) + flyoutItem.Icon = new ImageIcon() { Source = imageSource }; + } + + void HistoryItemClicked(ToolbarHistoryItemModel? itemModel) + { + if (itemModel is null) + return; + + var shellPage = Ioc.Default.GetRequiredService().ShellPage; + if (shellPage is null) + return; + + if (itemModel.IsBackMode) + { + // Remove all entries after the target entry in the BackwardStack + while (shellPage.BackwardStack.Last() != itemModel.PageStackEntry) + { + shellPage.BackwardStack.RemoveAt(shellPage.BackwardStack.Count - 1); + } + + // Navigate back + shellPage.Back_Click(); + } + else + { + // Remove all entries before the target entry in the ForwardStack + while (shellPage.ForwardStack.First() != itemModel.PageStackEntry) + { + shellPage.ForwardStack.RemoveAt(0); + } + + // Navigate forward + shellPage.Forward_Click(); + } + } + } + } + + private async void Omnibar_QuerySubmitted(Omnibar sender, OmnibarQuerySubmittedEventArgs args) + { + var mode = Omnibar.CurrentSelectedMode; + + // Path mode + if (mode == OmnibarPathMode) + { + await ViewModel.HandleItemNavigationAsync(args.Text); + ContentPageContext.ShellPage!.PaneHolder.FocusActivePane(); + return; + } + + // Command palette mode + else if (mode == OmnibarCommandPaletteMode) + { + var item = args.Item as NavigationBarSuggestionItem; + + // Try invoking built-in command + foreach (var command in Commands) + { + if (command == Commands.None) + continue; + + if (!string.Equals(command.Description, item?.Text, StringComparison.OrdinalIgnoreCase) && + !string.Equals(command.Description, args.Text, StringComparison.OrdinalIgnoreCase)) + continue; + + await command.ExecuteAsync(); + ContentPageContext.ShellPage!.PaneHolder.FocusActivePane(); + return; + } + + // Try invoking Windows app action + if (ActionManager.Instance.ActionRuntime is not null && item?.ActionInstance is ActionInstance actionInstance) + { + // Workaround for https://github.com/microsoft/App-Actions-On-Windows-Samples/issues/7 + var action = ActionManager.Instance.ActionRuntime.ActionCatalog.GetAllActions() + .FirstOrDefault(a => a.Id == actionInstance.Context.ActionId); + + if (action is not null) + { + var overload = action.GetOverloads().FirstOrDefault(); + await overload?.InvokeAsync(actionInstance.Context); + } + + ContentPageContext.ShellPage!.PaneHolder.FocusActivePane(); + return; + } + + await DialogDisplayHelper.ShowDialogAsync(Strings.InvalidCommand.GetLocalizedResource(), + string.Format(Strings.InvalidCommandContent.GetLocalizedResource(), args.Text)); + + ContentPageContext.ShellPage!.PaneHolder.FocusActivePane(); + return; + } + + // Search mode + else if (mode == OmnibarSearchMode) + { + var shellPage = ContentPageContext.ShellPage; + + if (args.Item is SuggestionModel item && !string.IsNullOrWhiteSpace(item.ItemPath) && shellPage is not null) + await NavigationHelpers.OpenPath(item.ItemPath, shellPage); + else + { + var searchQuery = args.Item is SuggestionModel x && !string.IsNullOrWhiteSpace(x.Name) + ? x.Name + : args.Text; + + shellPage?.SubmitSearch(searchQuery); // use the resolved shellPage for consistency + ViewModel.SaveSearchQueryToList(searchQuery); + } + + ContentPageContext.ShellPage!.PaneHolder.FocusActivePane(); + return; + } + } + + private async void Omnibar_TextChanged(Omnibar sender, OmnibarTextChangedEventArgs args) + { + if (args.Reason is not OmnibarTextChangeReason.UserInput) + return; + + if (Omnibar.CurrentSelectedMode == OmnibarPathMode) + { + await DispatcherQueue.EnqueueOrInvokeAsync(ViewModel.PopulateOmnibarSuggestionsForPathMode); + } + else if (Omnibar.CurrentSelectedMode == OmnibarCommandPaletteMode) + { + await DispatcherQueue.EnqueueOrInvokeAsync(ViewModel.PopulateOmnibarSuggestionsForCommandPaletteMode); + } + else if (Omnibar.CurrentSelectedMode == OmnibarSearchMode) + { + await DispatcherQueue.EnqueueOrInvokeAsync(ViewModel.PopulateOmnibarSuggestionsForSearchMode); + } + } + + private async void BreadcrumbBar_ItemClicked(Controls.BreadcrumbBar sender, Controls.BreadcrumbBarItemClickedEventArgs args) + { + if (args.IsRootItem) + { + await ViewModel.HandleItemNavigationAsync("Home"); + return; + } + + // Validate index before accessing the collection + if (args.Index < 0 || args.Index >= ViewModel.PathComponents.Count) + return; + + // Navigation to the current folder should not happen + if (args.Index == ViewModel.PathComponents.Count - 1 || + ViewModel.PathComponents[args.Index].Path is not { } path) + return; + + // If user clicked the item with middle mouse button, open it in new tab + var openInNewTab = args.PointerRoutedEventArgs?.GetCurrentPoint(null).Properties.PointerUpdateKind is PointerUpdateKind.MiddleButtonReleased; + await ViewModel.HandleFolderNavigationAsync(path, openInNewTab); + } + + private async void BreadcrumbBar_ItemDropDownFlyoutOpening(object sender, BreadcrumbBarItemDropDownFlyoutEventArgs e) + { + if (e.IsRootItem) + { + IHomeFolder homeFolder = new HomeFolder(); + IContentPageContext contentPageContext = Ioc.Default.GetRequiredService(); + + e.Flyout.Items.Add(new MenuFlyoutHeaderItem() { Text = Strings.QuickAccess.GetLocalizedResource() }); + + await foreach (var storable in homeFolder.GetQuickAccessFolderAsync()) + { + if (storable is not IWindowsStorable windowsStorable) + continue; + + var flyoutItem = new MenuFlyoutItem() + { + Text = windowsStorable.GetDisplayName(Windows.Win32.UI.Shell.SIGDN.SIGDN_PARENTRELATIVEFORUI), + DataContext = windowsStorable.GetDisplayName(Windows.Win32.UI.Shell.SIGDN.SIGDN_DESKTOPABSOLUTEPARSING), + Icon = new FontIcon() { Glyph = "\uE8B7" }, // As a placeholder + }; + + e.Flyout.Items.Add(flyoutItem); + + windowsStorable.TryGetThumbnail((int)(16f * App.AppModel.AppWindowDPI), Windows.Win32.UI.Shell.SIIGBF.SIIGBF_ICONONLY, out var thumbnailData); + flyoutItem.Icon = new ImageIcon() { Source = await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => thumbnailData.ToBitmapAsync(), Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal) }; + + windowsStorable.Dispose(); + + flyoutItem.Click += (sender, args) => + { + // NOTE: We should not pass a path string but pass the storable object itself in the future. + contentPageContext.ShellPage!.NavigateToPath((string)flyoutItem.DataContext); + }; + } + + e.Flyout.Items.Add(new MenuFlyoutHeaderItem() { Text = Strings.Drives.GetLocalizedResource() }); + + await foreach (var storable in homeFolder.GetLogicalDrivesAsync()) + { + if (storable is not IWindowsStorable windowsStorable) + continue; + + var flyoutItem = new MenuFlyoutItem() + { + Text = windowsStorable.GetDisplayName(Windows.Win32.UI.Shell.SIGDN.SIGDN_PARENTRELATIVEFORUI), + DataContext = windowsStorable.GetDisplayName(Windows.Win32.UI.Shell.SIGDN.SIGDN_DESKTOPABSOLUTEPARSING), + Icon = new FontIcon() { Glyph = "\uE8B7" }, // As a placeholder + }; + + e.Flyout.Items.Add(flyoutItem); + + windowsStorable.TryGetThumbnail((int)(16f * App.AppModel.AppWindowDPI), Windows.Win32.UI.Shell.SIIGBF.SIIGBF_ICONONLY, out var thumbnailData); + flyoutItem.Icon = new ImageIcon() { Source = await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => thumbnailData.ToBitmapAsync(), Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal) }; + + windowsStorable.Dispose(); + + flyoutItem.Click += (sender, args) => + { + // NOTE: We should not pass a path string but pass the storable object itself in the future. + contentPageContext.ShellPage!.NavigateToPath((string)flyoutItem.DataContext); + }; + } + + return; + } + + await ViewModel.SetPathBoxDropDownFlyoutAsync(e.Flyout, ViewModel.PathComponents[e.Index]); + } + + private void BreadcrumbBar_ItemDropDownFlyoutClosed(object sender, BreadcrumbBarItemDropDownFlyoutEventArgs e) + { + // Clear the flyout items to save memory + e.Flyout.Items.Clear(); + } + + /// + /// Handles mode changes in the Omnibar control. This event can fire even when the Omnibar + /// already has focus (e.g., user switching from Command Palette to Search mode). + /// Updates the appropriate text property and populates suggestions based on the new mode. + /// + private async void Omnibar_ModeChanged(object sender, OmnibarModeChangedEventArgs e) + { + if (e.NewMode == OmnibarPathMode) + { + // Initialize with current working directory or fallback to home path + ViewModel.PathText = string.IsNullOrEmpty(ContentPageContext.ShellPage?.ShellViewModel?.WorkingDirectory) + ? Constants.UserEnvironmentPaths.HomePath + : ContentPageContext.ShellPage.ShellViewModel.WorkingDirectory; + + await DispatcherQueue.EnqueueOrInvokeAsync(ViewModel.PopulateOmnibarSuggestionsForPathMode); + } + else if (e.NewMode == OmnibarCommandPaletteMode) + { + // Clear text and load command suggestions + ViewModel.OmnibarCommandPaletteModeText = string.Empty; + + await DispatcherQueue.EnqueueOrInvokeAsync(ViewModel.PopulateOmnibarSuggestionsForCommandPaletteMode); + } + else if (e.NewMode == OmnibarSearchMode) + { + // Preserve existing search query or clear for new search + if (!ViewModel.InstanceViewModel.IsPageTypeSearchResults) + ViewModel.OmnibarSearchModeText = string.Empty; + else + ViewModel.OmnibarSearchModeText = ViewModel.InstanceViewModel.CurrentSearchQuery; + + await DispatcherQueue.EnqueueOrInvokeAsync(ViewModel.PopulateOmnibarSuggestionsForSearchMode); + } + } + + /// + /// Handles focus state changes for the Omnibar control. + /// When focused: Updates Path Mode content (Path Mode has both focused/unfocused states). + /// When unfocused: Automatically switches back to Path Mode to display the BreadcrumbBar. + /// + private async void Omnibar_IsFocusedChanged(Omnibar sender, OmnibarIsFocusedChangedEventArgs args) + { + if (args.IsFocused) + { + // Path Mode needs special handling when gaining focus since it has an unfocused state + if (Omnibar.CurrentSelectedMode == OmnibarPathMode) + { + ViewModel.PathText = string.IsNullOrEmpty(ContentPageContext.ShellPage?.ShellViewModel?.WorkingDirectory) + ? Constants.UserEnvironmentPaths.HomePath + : ContentPageContext.ShellPage.ShellViewModel.WorkingDirectory; + + await DispatcherQueue.EnqueueOrInvokeAsync(ViewModel.PopulateOmnibarSuggestionsForPathMode); + } + } + else + { + // When Omnibar loses focus, revert to Path Mode to display BreadcrumbBar + Omnibar.CurrentSelectedMode = OmnibarPathMode; + } + } + + private async void Omnibar_PreviewKeyDown(object sender, KeyRoutedEventArgs e) + { + if (e.Key is VirtualKey.Escape) + { + Omnibar.IsFocused = false; + ContentPageContext.ShellPage!.PaneHolder.FocusActivePane(); + } + else if (e.Key is VirtualKey.Tab && Omnibar.IsFocused && !InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(CoreVirtualKeyStates.Down)) + { + var currentSelectedMode = Omnibar.CurrentSelectedMode; + Omnibar.IsFocused = false; + await Task.Delay(15); + + if (currentSelectedMode == OmnibarPathMode) + BreadcrumbBar.Focus(FocusState.Keyboard); + else if (currentSelectedMode == OmnibarCommandPaletteMode) + OmnibarCommandPaletteMode.Focus(FocusState.Keyboard); + else if (currentSelectedMode == OmnibarSearchMode) + OmnibarSearchMode.Focus(FocusState.Keyboard); + } + } + + private void NavigationButtonOverflowFlyoutButton_LosingFocus(UIElement sender, LosingFocusEventArgs args) + { + // Prevent the Omnibar from taking focus if the overflow button is hidden while the button is focused + if (args.NewFocusedElement is TextBox) + args.Cancel = true; + } + + private void BreadcrumbBarItem_DragLeave(object sender, DragEventArgs e) + { + ViewModel.PathBoxItem_DragLeave(sender, e); + } + + private async void BreadcrumbBarItem_DragOver(object sender, DragEventArgs e) + { + await ViewModel.PathBoxItem_DragOver(sender, e); + } + + private async void BreadcrumbBarItem_Drop(object sender, DragEventArgs e) + { + await ViewModel.PathBoxItem_Drop(sender, e); + } + } +} diff --git a/src/Files.App/UserControls/OpacityIcon.xaml b/src/Files.App/UserControls/OpacityIcon.xaml deleted file mode 100644 index fa2f87154643..000000000000 --- a/src/Files.App/UserControls/OpacityIcon.xaml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - diff --git a/src/Files.App/UserControls/OpacityIcon.xaml.cs b/src/Files.App/UserControls/OpacityIcon.xaml.cs deleted file mode 100644 index 5b2cb8cf7ba4..000000000000 --- a/src/Files.App/UserControls/OpacityIcon.xaml.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using CommunityToolkit.WinUI.UI; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; - -namespace Files.App.UserControls -{ - public sealed partial class OpacityIcon : Control - { - public OpacityIcon() - { - InitializeComponent(); - } - - - public bool IsSelected - { - get => (bool)GetValue(IsSelectedProperty); - set => SetValue(IsSelectedProperty, value); - } - - public static readonly DependencyProperty IsSelectedProperty = - DependencyProperty.Register(nameof(IsSelected), typeof(bool), typeof(OpacityIcon), new PropertyMetadata(null)); - - private void IsEnabledChange(DependencyObject sender, DependencyProperty dp) - { - if (sender.GetValue(dp) is false) - VisualStateManager.GoToState(this, "Disabled", true); - else if (IsSelected) - VisualStateManager.GoToState(this, "Selected", true); - else - VisualStateManager.GoToState(this, "Normal", true); - } - - private void OpacityIcon_Loading(FrameworkElement sender, object e) - { - var control = this.FindAscendant(); - control?.RegisterPropertyChangedCallback(IsEnabledProperty, IsEnabledChange); - RegisterPropertyChangedCallback(IsSelectedProperty, IsEnabledChange); - } - - private void OpacityIcon_Loaded(object sender, RoutedEventArgs e) - { - var control = this.FindAscendant(); - if (control is not null && !control.IsEnabled) - VisualStateManager.GoToState(this, "Disabled", false); - - if (control is not null && control.IsEnabled && IsSelected) - VisualStateManager.GoToState(this, "Selected", true); - } - } -} diff --git a/src/Files.App/UserControls/Pane/InfoPane.xaml b/src/Files.App/UserControls/Pane/InfoPane.xaml index 9210740274c1..9eb077d7b958 100644 --- a/src/Files.App/UserControls/Pane/InfoPane.xaml +++ b/src/Files.App/UserControls/Pane/InfoPane.xaml @@ -1,17 +1,18 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -316,10 +403,13 @@ Tapped="TagItem_Tapped" ToolTipService.ToolTip="{x:Bind AsTag.Tag.Name, Mode=OneWay}"> - + - + Style="{StaticResource App.ThemedIcons.TagEdit}" /> - + @@ -415,7 +505,7 @@ - + @@ -429,7 +519,7 @@ - + @@ -463,6 +553,23 @@ + + + + + + + + + + + + + + + + + diff --git a/src/Files.App/UserControls/Pane/InfoPane.xaml.cs b/src/Files.App/UserControls/Pane/InfoPane.xaml.cs index d2d50794d269..f337430a1424 100644 --- a/src/Files.App/UserControls/Pane/InfoPane.xaml.cs +++ b/src/Files.App/UserControls/Pane/InfoPane.xaml.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -52,8 +52,6 @@ public void UpdatePosition(double panelWidth, double panelHeight) } } - private string GetLocalizedResource(string resName) => resName.GetLocalizedResource(); - private void Root_Unloaded(object sender, RoutedEventArgs e) { PreviewControlPresenter.Content = null; @@ -76,7 +74,7 @@ private void FileTag_PointerExited(object sender, PointerRoutedEventArgs e) VisualStateManager.GoToState((UserControl)sender, "Normal", true); } - private sealed class ObservableContext : ObservableObject + private sealed partial class ObservableContext : ObservableObject { private bool isHorizontal = false; public bool IsHorizontal @@ -85,14 +83,14 @@ public bool IsHorizontal set => SetProperty(ref isHorizontal, value); } } - + private void TagItem_Tapped(object sender, TappedRoutedEventArgs e) { var tagName = ((sender as StackPanel)?.Children[1] as TextBlock)?.Text; if (tagName is null) return; - contentPageContext.ShellPage?.SubmitSearch($"tag:{tagName}"); + contentPageContext.ShellPage?.SubmitSearch(FolderSearch.FormatTagQuery(tagName)); } } } \ No newline at end of file diff --git a/src/Files.App/UserControls/Pane/ShelfPane.xaml b/src/Files.App/UserControls/Pane/ShelfPane.xaml new file mode 100644 index 000000000000..121268eeaf12 --- /dev/null +++ b/src/Files.App/UserControls/Pane/ShelfPane.xaml @@ -0,0 +1,162 @@ + + + + + + + + + + + + ms-appx:///Assets/Shelf/EmptyShelf_48_ThemeLight.svg + ms-appx:///Assets/Shelf/EmptyShelf_100_ThemeLight.svg + ms-appx:///Assets/Shelf/EmptyShelf_200_ThemeLight.svg + + + ms-appx:///Assets/Shelf/EmptyShelf_48_ThemeDark.svg + ms-appx:///Assets/Shelf/EmptyShelf_100_ThemeDark.svg + ms-appx:///Assets/Shelf/EmptyShelf_200_ThemeDark.svg + + + ms-appx:///Assets/Shelf/EmptyShelf_48_ThemeDark.svg + ms-appx:///Assets/Shelf/EmptyShelf_100_ThemeDark.svg + ms-appx:///Assets/Shelf/EmptyShelf_200_ThemeDark.svg + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App/UserControls/Pane/ShelfPane.xaml.cs b/src/Files.App/UserControls/Pane/ShelfPane.xaml.cs new file mode 100644 index 000000000000..01c1c2e6e23d --- /dev/null +++ b/src/Files.App/UserControls/Pane/ShelfPane.xaml.cs @@ -0,0 +1,140 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using System.Windows.Input; +using Vanara.PInvoke; +using Vanara.Windows.Shell; +using Windows.ApplicationModel.DataTransfer; +using WinRT; +using DragEventArgs = Microsoft.UI.Xaml.DragEventArgs; + +namespace Files.App.UserControls +{ + public sealed partial class ShelfPane : UserControl + { + public ShelfPane() + { + InitializeComponent(); + } + + private void Shelf_DragOver(object sender, DragEventArgs e) + { + if (!FilesystemHelpers.HasDraggedStorageItems(e.DataView)) + return; + + e.Handled = true; + e.DragUIOverride.Caption = Strings.AddToShelf.GetLocalizedResource(); + e.AcceptedOperation = DataPackageOperation.Link; + } + + private async void Shelf_Drop(object sender, DragEventArgs e) + { + if (ItemsSource is null) + return; + + // Get items + var storageService = Ioc.Default.GetRequiredService(); + var storageItems = (await FilesystemHelpers.GetDraggedStorageItems(e.DataView)).ToArray(); + + // Add to list + foreach (var item in storageItems) + { + // Avoid adding duplicates + if (ItemsSource.Any(x => x.Inner.Id == item.Path)) + continue; + + var storable = item switch + { + StorageFileWithPath => (IStorableChild?)await storageService.TryGetFileAsync(item.Path), + StorageFolderWithPath => (IStorableChild?)await storageService.TryGetFolderAsync(item.Path), + _ => null + }; + + if (storable is null) + continue; + + var shelfItem = new ShelfItem(storable, ItemsSource); + _ = shelfItem.InitAsync(); + + ItemsSource.Add(shelfItem); + } + } + + private void ListView_DragItemsStarting(object sender, DragItemsStartingEventArgs e) + { + var apidl = SafetyExtensions.IgnoreExceptions(() => e.Items + .Cast() + .Select(x => new ShellItem(x.Inner.Id).PIDL) + .ToArray()); + + if (apidl is null) + return; + + if (!Shell32.SHGetDesktopFolder(out var pDesktop).Succeeded) + return; + + if (!Shell32.SHGetIDListFromObject(pDesktop, out var pDesktopPidl).Succeeded) + return; + + e.Data.Properties["Files_ActionBinder"] = "Files_ShelfBinder"; + if (!Shell32.SHCreateDataObject(pDesktopPidl, apidl, null, out var ppDataObject).Succeeded) + return; + + var dataObjectProvider = e.Data.As(); + dataObjectProvider.SetDataObject(ppDataObject); + } + + private void ShelfItemsList_RightTapped(object sender, Microsoft.UI.Xaml.Input.RightTappedRoutedEventArgs e) + { + if (e.OriginalSource is not Microsoft.UI.Xaml.FrameworkElement widgetCardItem || + widgetCardItem.DataContext is not ShelfItem item || + item.Path is null) + return; + + var menuFlyout = new MenuFlyout(); + + menuFlyout.Items.Add(new MenuFlyoutItem + { + Text = Strings.RemoveFromShelf.GetLocalizedResource(), + Icon = new FontIcon { Glyph = "\uE738" }, + Command = new RelayCommand(item.Remove) + }); + + menuFlyout.ShowAt(widgetCardItem); + e.Handled = true; + } + + private void ShelfItemsList_GotFocus(object sender, RoutedEventArgs e) + { + if (ItemFocusedCommand is not null) + ItemFocusedCommand.Execute(null); + } + + public ObservableCollection? ItemsSource + { + get => (ObservableCollection?)GetValue(ItemsSourceProperty); + set => SetValue(ItemsSourceProperty, value); + } + public static readonly DependencyProperty ItemsSourceProperty = + DependencyProperty.Register(nameof(ItemsSource), typeof(ObservableCollection), typeof(ShelfPane), new PropertyMetadata(null)); + + public ICommand? ClearCommand + { + get => (ICommand?)GetValue(ClearCommandProperty); + set => SetValue(ClearCommandProperty, value); + } + public static readonly DependencyProperty ClearCommandProperty = + DependencyProperty.Register(nameof(ClearCommand), typeof(ICommand), typeof(ShelfPane), new PropertyMetadata(null)); + + public ICommand? ItemFocusedCommand + { + get => (ICommand?)GetValue(ItemFocusedCommandProperty); + set => SetValue(ItemFocusedCommandProperty, value); + } + public static readonly DependencyProperty ItemFocusedCommandProperty = + DependencyProperty.Register(nameof(ItemFocusedCommand), typeof(ICommand), typeof(ShelfPane), new PropertyMetadata(null)); + + } +} diff --git a/src/Files.App/UserControls/PathBreadcrumb.xaml b/src/Files.App/UserControls/PathBreadcrumb.xaml deleted file mode 100644 index ab48a329e4c2..000000000000 --- a/src/Files.App/UserControls/PathBreadcrumb.xaml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Files.App/UserControls/PathBreadcrumb.xaml.cs b/src/Files.App/UserControls/PathBreadcrumb.xaml.cs deleted file mode 100644 index 2bbdba6d55cc..000000000000 --- a/src/Files.App/UserControls/PathBreadcrumb.xaml.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Input; - -namespace Files.App.UserControls -{ - [DependencyProperty("ViewModel")] - public sealed partial class PathBreadcrumb : UserControl - { - public PathBreadcrumb() - { - InitializeComponent(); - } - - private void PathItemSeparator_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args) - { - ViewModel.PathItemSeparator_DataContextChanged(sender, args); - } - - private void PathBoxItemFlyout_Opened(object sender, object e) - { - ViewModel.PathboxItemFlyout_Opened(sender, e); - } - - private void PathBoxItem_DragLeave(object sender, DragEventArgs e) - { - ViewModel.PathBoxItem_DragLeave(sender, e); - } - - private async void PathBoxItem_DragOver(object sender, DragEventArgs e) - { - await ViewModel.PathBoxItem_DragOver(sender, e); - } - - private async void PathBoxItem_Drop(object sender, DragEventArgs e) - { - await ViewModel.PathBoxItem_Drop(sender, e); - } - - private async void PathBoxItem_Tapped(object sender, TappedRoutedEventArgs e) - { - await ViewModel.PathBoxItem_Tapped(sender, e); - } - - private void PathBoxItem_PointerPressed(object sender, PointerRoutedEventArgs e) - { - ViewModel.PathBoxItem_PointerPressed(sender, e); - } - } -} diff --git a/src/Files.App/UserControls/SearchBox.xaml b/src/Files.App/UserControls/SearchBox.xaml deleted file mode 100644 index 9208d30dd540..000000000000 --- a/src/Files.App/UserControls/SearchBox.xaml +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Files.App/UserControls/SearchBox.xaml.cs b/src/Files.App/UserControls/SearchBox.xaml.cs deleted file mode 100644 index 25e6d97cd8c8..000000000000 --- a/src/Files.App/UserControls/SearchBox.xaml.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.App.ViewModels; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Input; - -namespace Files.App.UserControls -{ - public sealed partial class SearchBox : UserControl - { - public static readonly DependencyProperty SearchBoxViewModelProperty = - DependencyProperty.Register(nameof(SearchBoxViewModel), typeof(SearchBoxViewModel), typeof(SearchBox), new PropertyMetadata(null)); - - public SearchBoxViewModel SearchBoxViewModel - { - get => (SearchBoxViewModel)GetValue(SearchBoxViewModelProperty); - set => SetValue(SearchBoxViewModelProperty, value); - } - - public SearchBox() => InitializeComponent(); - - private void SearchRegion_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs e) - => SearchBoxViewModel.SearchRegion_TextChanged(sender, e); - - private void SearchRegion_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs e) - => SearchBoxViewModel.SearchRegion_QuerySubmitted(sender, e); - - private void SearchRegion_Escaped(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs e) - => SearchBoxViewModel.SearchRegion_Escaped(sender, e); - - private void SearchRegion_KeyDown(object sender, KeyRoutedEventArgs e) - => SearchBoxViewModel.SearchRegion_KeyDown(sender, e); - } -} diff --git a/src/Files.App/UserControls/Selection/ExtendPreviousItemSelectionStrategy.cs b/src/Files.App/UserControls/Selection/ExtendPreviousItemSelectionStrategy.cs index 82d72c590c1d..460ac84cfcef 100644 --- a/src/Files.App/UserControls/Selection/ExtendPreviousItemSelectionStrategy.cs +++ b/src/Files.App/UserControls/Selection/ExtendPreviousItemSelectionStrategy.cs @@ -1,7 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using System.Collections.Generic; using System.Runtime.InteropServices; namespace Files.App.UserControls.Selection diff --git a/src/Files.App/UserControls/Selection/IgnorePreviousItemSelectionStrategy.cs b/src/Files.App/UserControls/Selection/IgnorePreviousItemSelectionStrategy.cs index c226f0ac8461..f82e03c43215 100644 --- a/src/Files.App/UserControls/Selection/IgnorePreviousItemSelectionStrategy.cs +++ b/src/Files.App/UserControls/Selection/IgnorePreviousItemSelectionStrategy.cs @@ -1,7 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using System.Collections.Generic; using System.Runtime.InteropServices; namespace Files.App.UserControls.Selection diff --git a/src/Files.App/UserControls/Selection/InvertPreviousItemSelectionStrategy.cs b/src/Files.App/UserControls/Selection/InvertPreviousItemSelectionStrategy.cs index 0ce775d031ff..399caa93286f 100644 --- a/src/Files.App/UserControls/Selection/InvertPreviousItemSelectionStrategy.cs +++ b/src/Files.App/UserControls/Selection/InvertPreviousItemSelectionStrategy.cs @@ -1,7 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using System.Collections.Generic; using System.Runtime.InteropServices; namespace Files.App.UserControls.Selection diff --git a/src/Files.App/UserControls/Selection/ItemSelectionStrategy.cs b/src/Files.App/UserControls/Selection/ItemSelectionStrategy.cs index 209473768dfe..cc04f4dd5281 100644 --- a/src/Files.App/UserControls/Selection/ItemSelectionStrategy.cs +++ b/src/Files.App/UserControls/Selection/ItemSelectionStrategy.cs @@ -1,7 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System.Collections.Generic; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.UserControls.Selection { diff --git a/src/Files.App/UserControls/Selection/RectangleSelection.cs b/src/Files.App/UserControls/Selection/RectangleSelection.cs index b9d82703e2a9..a3dc27287475 100644 --- a/src/Files.App/UserControls/Selection/RectangleSelection.cs +++ b/src/Files.App/UserControls/Selection/RectangleSelection.cs @@ -1,11 +1,10 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Input; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Shapes; -using System; using Windows.Foundation; namespace Files.App.UserControls.Selection diff --git a/src/Files.App/UserControls/Selection/RectangleSelection_ListViewBase.cs b/src/Files.App/UserControls/Selection/RectangleSelection_ListViewBase.cs index ebe1f5d3220b..eb64ebb415aa 100644 --- a/src/Files.App/UserControls/Selection/RectangleSelection_ListViewBase.cs +++ b/src/Files.App/UserControls/Selection/RectangleSelection_ListViewBase.cs @@ -1,14 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using CommunityToolkit.WinUI.UI; +using CommunityToolkit.WinUI; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; using Microsoft.UI.Xaml.Shapes; -using System; -using System.Collections.Generic; -using System.Linq; using Windows.Foundation; using Windows.System; using DispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue; diff --git a/src/Files.App/UserControls/Settings/SettingsBlockControl.xaml b/src/Files.App/UserControls/Settings/SettingsBlockControl.xaml deleted file mode 100644 index 6ef5ea716c51..000000000000 --- a/src/Files.App/UserControls/Settings/SettingsBlockControl.xaml +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Files.App/UserControls/Settings/SettingsBlockControl.xaml.cs b/src/Files.App/UserControls/Settings/SettingsBlockControl.xaml.cs deleted file mode 100644 index dbf0806d9b7c..000000000000 --- a/src/Files.App/UserControls/Settings/SettingsBlockControl.xaml.cs +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Automation; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Markup; -using System; -using System.Windows.Input; - -namespace Files.App.UserControls.Settings -{ - [ContentProperty(Name = nameof(SettingsActionableElement))] - public sealed partial class SettingsBlockControl : UserControl - { - public FrameworkElement SettingsActionableElement { get; set; } - - public ICommand ButtonCommand - { - get { return (ICommand)GetValue(ButtonCommandProperty); } - set { SetValue(ButtonCommandProperty, value); } - } - public static readonly DependencyProperty ButtonCommandProperty = - DependencyProperty.Register("ButtonCommand", typeof(ICommand), typeof(SettingsBlockControl), new PropertyMetadata(null)); - - public static readonly DependencyProperty ExpandableContentProperty = DependencyProperty.Register( - "ExpandableContent", - typeof(FrameworkElement), - typeof(SettingsBlockControl), - new PropertyMetadata(null) - ); - - public FrameworkElement ExpandableContent - { - get => (FrameworkElement)GetValue(ExpandableContentProperty); - set => SetValue(ExpandableContentProperty, value); - } - - public static readonly DependencyProperty AdditionalDescriptionContentProperty = DependencyProperty.Register( - "AdditionalDescriptionContent", - typeof(FrameworkElement), - typeof(SettingsBlockControl), - new PropertyMetadata(null) - ); - - public FrameworkElement AdditionalDescriptionContent - { - get => (FrameworkElement)GetValue(AdditionalDescriptionContentProperty); - set => SetValue(AdditionalDescriptionContentProperty, value); - } - - public static readonly DependencyProperty TitleProperty = DependencyProperty.Register( - "Title", - typeof(string), - typeof(SettingsBlockControl), - new PropertyMetadata(null) - ); - - public string Title - { - get => (string)GetValue(TitleProperty); - set => SetValue(TitleProperty, value); - } - - public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register( - "Description", - typeof(string), - typeof(SettingsBlockControl), - new PropertyMetadata(null) - ); - - public string Description - { - get => (string)GetValue(DescriptionProperty); - set => SetValue(DescriptionProperty, value); - } - - public static readonly DependencyProperty IconProperty = DependencyProperty.Register( - "Icon", - typeof(IconElement), - typeof(SettingsBlockControl), - new PropertyMetadata(null) - ); - - public IconElement Icon - { - get => (IconElement)GetValue(IconProperty); - set => SetValue(IconProperty, value); - } - - public static readonly DependencyProperty IsClickableProperty = DependencyProperty.Register( - "IsClickable", - typeof(bool), - typeof(SettingsBlockControl), - new PropertyMetadata(false) - ); - - public bool IsClickable - { - get => (bool)GetValue(IsClickableProperty); - set => SetValue(IsClickableProperty, value); - } - - public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register( - "IsExpanded", - typeof(bool), - typeof(SettingsBlockControl), - new PropertyMetadata(false) - ); - - public bool IsExpanded - { - get => (bool)GetValue(IsExpandedProperty); - set => SetValue(IsExpandedProperty, value); - } - - // - // Summary: - // Occurs when a button control is clicked. - public event EventHandler Click; - - public SettingsBlockControl() - { - InitializeComponent(); - Loaded += SettingsBlockControl_Loaded; - } - - private void SettingsBlockControl_Loaded(object sender, RoutedEventArgs e) - { - if (ActionableButton is not null) - { - AutomationProperties.SetName(ActionableButton, Title); - } - } - - private void Expander_Expanding(Microsoft.UI.Xaml.Controls.Expander sender, Microsoft.UI.Xaml.Controls.ExpanderExpandingEventArgs args) - { - Click?.Invoke(this, true); - } - - private void Expander_Collapsed(Microsoft.UI.Xaml.Controls.Expander sender, Microsoft.UI.Xaml.Controls.ExpanderCollapsedEventArgs args) - { - Click?.Invoke(this, false); - } - } -} \ No newline at end of file diff --git a/src/Files.App/UserControls/Settings/SettingsDisplayControl.xaml b/src/Files.App/UserControls/Settings/SettingsDisplayControl.xaml deleted file mode 100644 index 76cff1976be6..000000000000 --- a/src/Files.App/UserControls/Settings/SettingsDisplayControl.xaml +++ /dev/null @@ -1,110 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Files.App/UserControls/Settings/SettingsDisplayControl.xaml.cs b/src/Files.App/UserControls/Settings/SettingsDisplayControl.xaml.cs deleted file mode 100644 index b9e94d63fa61..000000000000 --- a/src/Files.App/UserControls/Settings/SettingsDisplayControl.xaml.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Markup; - -namespace Files.App.UserControls.Settings -{ - [ContentProperty(Name = nameof(SettingsActionableElement))] - public sealed partial class SettingsDisplayControl : UserControl - { - public FrameworkElement SettingsActionableElement { get; set; } - - public static readonly DependencyProperty AdditionalDescriptionContentProperty = DependencyProperty.Register( - "AdditionalDescriptionContent", - typeof(FrameworkElement), - typeof(SettingsDisplayControl), - new PropertyMetadata(null) - ); - - public FrameworkElement AdditionalDescriptionContent - { - get => (FrameworkElement)GetValue(AdditionalDescriptionContentProperty); - set => SetValue(AdditionalDescriptionContentProperty, value); - } - - public static readonly DependencyProperty TitleProperty = DependencyProperty.Register( - "Title", - typeof(string), - typeof(SettingsDisplayControl), - new PropertyMetadata(null) - ); - - public string Title - { - get => (string)GetValue(TitleProperty); - set => SetValue(TitleProperty, value); - } - - public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register( - "Description", - typeof(string), - typeof(SettingsDisplayControl), - new PropertyMetadata(null) - ); - - public string Description - { - get => (string)GetValue(DescriptionProperty); - set => SetValue(DescriptionProperty, value); - } - - public static readonly DependencyProperty IconProperty = DependencyProperty.Register( - "Icon", - typeof(IconElement), - typeof(SettingsDisplayControl), - new PropertyMetadata(null) - ); - - public IconElement Icon - { - get => (IconElement)GetValue(IconProperty); - set => SetValue(IconProperty, value); - } - - public SettingsDisplayControl() - { - InitializeComponent(); - VisualStateManager.GoToState(this, "NormalState", false); - } - - private void MainPanel_SizeChanged(object sender, SizeChangedEventArgs e) - { - if (e.NewSize.Width == e.PreviousSize.Width || ActionableElement is null) - return; - - var stateToGoName = (ActionableElement.ActualWidth > e.NewSize.Width / 3) ? "CompactState" : "NormalState"; - VisualStateManager.GoToState(this, stateToGoName, false); - } - } -} diff --git a/src/Files.App/UserControls/Shimmer/Shimmer.Properties.cs b/src/Files.App/UserControls/Shimmer/Shimmer.Properties.cs deleted file mode 100644 index e5d409fc66cd..000000000000 --- a/src/Files.App/UserControls/Shimmer/Shimmer.Properties.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; - -namespace Files.App.UserControls -{ - public partial class Shimmer : Control - { - /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty DurationProperty = - DependencyProperty.Register( - nameof(Duration), - typeof(object), - typeof(Shimmer), - new PropertyMetadata(defaultValue: TimeSpan.FromMilliseconds(1600), PropertyChanged)); - - /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty IsActiveProperty = - DependencyProperty.Register( - nameof(IsActive), - typeof(bool), - typeof(Shimmer), - new PropertyMetadata(defaultValue: true, PropertyChanged)); - - /// - /// Gets or sets the animation duration - /// - public TimeSpan Duration - { - get => (TimeSpan)GetValue(DurationProperty); - set => SetValue(DurationProperty, value); - } - - /// - /// Gets or sets if the animation is playing - /// - public bool IsActive - { - get => (bool)GetValue(IsActiveProperty); - set => SetValue(IsActiveProperty, value); - } - - private static void PropertyChanged(DependencyObject s, DependencyPropertyChangedEventArgs e) - { - var self = (Shimmer)s; - if (self.IsActive) - { - self.StopAnimation(); - self.TryStartAnimation(); - } - else - { - self.StopAnimation(); - } - } - } -} diff --git a/src/Files.App/UserControls/Shimmer/Shimmer.cs b/src/Files.App/UserControls/Shimmer/Shimmer.cs deleted file mode 100644 index c76fe385b33b..000000000000 --- a/src/Files.App/UserControls/Shimmer/Shimmer.cs +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using CommunityToolkit.WinUI.UI; -using CommunityToolkit.WinUI; -using CommunityToolkit.WinUI.UI.Animations; -using CommunityToolkit.WinUI.UI.Animations.Expressions; -using Microsoft.UI.Composition; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Hosting; -using Microsoft.UI.Xaml.Shapes; -using System.Numerics; -using Windows.UI; - -namespace Files.App.UserControls -{ - /// - /// A generic shimmer control that can be used to construct a beautiful loading effect. - /// - [TemplatePart(Name = PART_Shape, Type = typeof(Rectangle))] - public partial class Shimmer : Control - { - private const float InitialStartPointX = -7.92f; - private const string PART_Shape = "Shape"; - - private Vector2Node? _sizeAnimation; - private Vector2KeyFrameAnimation? _gradientStartPointAnimation; - private Vector2KeyFrameAnimation? _gradientEndPointAnimation; - private CompositionColorGradientStop? _gradientStop1; - private CompositionColorGradientStop? _gradientStop2; - private CompositionColorGradientStop? _gradientStop3; - private CompositionColorGradientStop? _gradientStop4; - private CompositionRoundedRectangleGeometry? _rectangleGeometry; - private ShapeVisual? _shapeVisual; - private CompositionLinearGradientBrush? _shimmerMaskGradient; - private Border? _shape; - - private bool _initialized; - private bool _animationStarted; - - public Shimmer() - { - DefaultStyleKey = typeof(Shimmer); - Loaded += OnLoaded; - Unloaded += OnUnloaded; - } - - protected override void OnApplyTemplate() - { - base.OnApplyTemplate(); - - _shape = GetTemplateChild(PART_Shape) as Border; - if (_initialized is false && TryInitializationResource() && IsActive) - { - TryStartAnimation(); - } - } - - private void OnLoaded(object sender, RoutedEventArgs e) - { - if (_initialized is false && TryInitializationResource() && IsActive) - { - TryStartAnimation(); - } - - ActualThemeChanged += OnActualThemeChanged; - } - - private void OnUnloaded(object sender, RoutedEventArgs e) - { - ActualThemeChanged -= OnActualThemeChanged; - StopAnimation(); - - if (_initialized && _shape != null) - { - ElementCompositionPreview.SetElementChildVisual(_shape, null); - - _rectangleGeometry!.Dispose(); - _shapeVisual!.Dispose(); - _shimmerMaskGradient!.Dispose(); - _gradientStop1!.Dispose(); - _gradientStop2!.Dispose(); - _gradientStop3!.Dispose(); - _gradientStop4!.Dispose(); - - _initialized = false; - } - } - - private void OnActualThemeChanged(FrameworkElement sender, object args) - { - if (_initialized is false) - { - return; - } - - SetGradientStopColorsByTheme(); - } - - private bool TryInitializationResource() - { - if (_initialized) - { - return true; - } - - if (_shape is null || IsLoaded is false) - { - return false; - } - - var compositor = _shape.GetVisual().Compositor; - - _rectangleGeometry = compositor.CreateRoundedRectangleGeometry(); - _shapeVisual = compositor.CreateShapeVisual(); - _shimmerMaskGradient = compositor.CreateLinearGradientBrush(); - _gradientStop1 = compositor.CreateColorGradientStop(); - _gradientStop2 = compositor.CreateColorGradientStop(); - _gradientStop3 = compositor.CreateColorGradientStop(); - _gradientStop4 = compositor.CreateColorGradientStop(); - SetGradientAndStops(); - SetGradientStopColorsByTheme(); - _rectangleGeometry.CornerRadius = new Vector2((float)CornerRadius.TopLeft); - var spriteShape = compositor.CreateSpriteShape(_rectangleGeometry); - spriteShape.FillBrush = _shimmerMaskGradient; - _shapeVisual.Shapes.Add(spriteShape); - ElementCompositionPreview.SetElementChildVisual(_shape, _shapeVisual); - - _initialized = true; - return true; - } - - private void SetGradientAndStops() - { - _shimmerMaskGradient!.StartPoint = new Vector2(InitialStartPointX, 0.0f); - _shimmerMaskGradient.EndPoint = new Vector2(0.0f, 1.0f); //Vector2.One - - _gradientStop1!.Offset = 0.273f; - _gradientStop2!.Offset = 0.436f; - _gradientStop3!.Offset = 0.482f; - _gradientStop4!.Offset = 0.643f; - - _shimmerMaskGradient.ColorStops.Add(_gradientStop1); - _shimmerMaskGradient.ColorStops.Add(_gradientStop2); - _shimmerMaskGradient.ColorStops.Add(_gradientStop3); - _shimmerMaskGradient.ColorStops.Add(_gradientStop4); - } - - private void SetGradientStopColorsByTheme() - { - switch (ActualTheme) - { - case ElementTheme.Default: - case ElementTheme.Dark: - _gradientStop1!.Color = Color.FromArgb((byte)(255 * 6.05 / 100), 255, 255, 255); - _gradientStop2!.Color = Color.FromArgb((byte)(255 * 3.26 / 100), 255, 255, 255); - _gradientStop3!.Color = Color.FromArgb((byte)(255 * 3.26 / 100), 255, 255, 255); - _gradientStop4!.Color = Color.FromArgb((byte)(255 * 6.05 / 100), 255, 255, 255); - break; - case ElementTheme.Light: - _gradientStop1!.Color = Color.FromArgb((byte)(255 * 5.37 / 100), 0, 0, 0); - _gradientStop2!.Color = Color.FromArgb((byte)(255 * 2.89 / 100), 0, 0, 0); - _gradientStop3!.Color = Color.FromArgb((byte)(255 * 2.89 / 100), 0, 0, 0); - _gradientStop4!.Color = Color.FromArgb((byte)(255 * 5.37 / 100), 0, 0, 0); - break; - } - } - - private void TryStartAnimation() - { - if (_animationStarted || _initialized is false || _shape is null || _shapeVisual is null || _rectangleGeometry is null) - { - return; - } - - var rootVisual = _shape.GetVisual(); - _sizeAnimation = rootVisual.GetReference().Size; - _shapeVisual.StartAnimation(nameof(ShapeVisual.Size), _sizeAnimation); - _rectangleGeometry.StartAnimation(nameof(CompositionRoundedRectangleGeometry.Size), _sizeAnimation); - - _gradientStartPointAnimation = rootVisual.Compositor.CreateVector2KeyFrameAnimation(); - _gradientStartPointAnimation.Duration = Duration; - _gradientStartPointAnimation.IterationBehavior = AnimationIterationBehavior.Forever; - _gradientStartPointAnimation.InsertKeyFrame(0.0f, new Vector2(InitialStartPointX, 0.0f)); - _gradientStartPointAnimation.InsertKeyFrame(1.0f, Vector2.Zero); - _shimmerMaskGradient!.StartAnimation(nameof(CompositionLinearGradientBrush.StartPoint), _gradientStartPointAnimation); - - _gradientEndPointAnimation = rootVisual.Compositor.CreateVector2KeyFrameAnimation(); - _gradientEndPointAnimation.Duration = Duration; - _gradientEndPointAnimation.IterationBehavior = AnimationIterationBehavior.Forever; - _gradientEndPointAnimation.InsertKeyFrame(0.0f, new Vector2(1.0f, 0.0f)); //Vector2.One - _gradientEndPointAnimation.InsertKeyFrame(1.0f, new Vector2(-InitialStartPointX, 1.0f)); - _shimmerMaskGradient.StartAnimation(nameof(CompositionLinearGradientBrush.EndPoint), _gradientEndPointAnimation); - - _animationStarted = true; - } - - private void StopAnimation() - { - if (_animationStarted is false) - { - return; - } - - _shapeVisual!.StopAnimation(nameof(ShapeVisual.Size)); - _rectangleGeometry!.StopAnimation(nameof(CompositionRoundedRectangleGeometry.Size)); - _shimmerMaskGradient!.StopAnimation(nameof(CompositionLinearGradientBrush.StartPoint)); - _shimmerMaskGradient.StopAnimation(nameof(CompositionLinearGradientBrush.EndPoint)); - - _sizeAnimation!.Dispose(); - _gradientStartPointAnimation!.Dispose(); - _gradientEndPointAnimation!.Dispose(); - _animationStarted = false; - } - } -} diff --git a/src/Files.App/UserControls/Sidebar/ISidebarItemModel.cs b/src/Files.App/UserControls/Sidebar/ISidebarItemModel.cs deleted file mode 100644 index fcffffc14ffc..000000000000 --- a/src/Files.App/UserControls/Sidebar/ISidebarItemModel.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; - -namespace Files.App.UserControls.Sidebar -{ - public interface ISidebarItemModel : INotifyPropertyChanged - { - /// - /// The children of this item that will be rendered as child elements of the SidebarItem - /// - object? Children { get; } - - /// - /// The icon source used to generate the icon for the SidebarItem - /// - IconSource? IconSource { get; } - - /// - /// Item decorator for the given item. - /// - FrameworkElement? ItemDecorator { get => null; } - - /// - /// Determines whether the SidebarItem is expanded and the children are visible - /// or if it is collapsed and children are not visible. - /// - bool IsExpanded { get; set; } - - /// - /// The text of this item that will be rendered as the label of the SidebarItem - /// - string Text { get; } - - /// - /// The tooltip used when hovering over this item in the sidebar - /// - object ToolTip { get; } - } -} diff --git a/src/Files.App/UserControls/Sidebar/ISidebarViewModel.cs b/src/Files.App/UserControls/Sidebar/ISidebarViewModel.cs deleted file mode 100644 index 246986f0bb12..000000000000 --- a/src/Files.App/UserControls/Sidebar/ISidebarViewModel.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Microsoft.UI.Input; -using Microsoft.UI.Xaml; -using Windows.ApplicationModel.DataTransfer; -using Windows.Foundation; - -namespace Files.App.UserControls.Sidebar -{ - public record ItemDroppedEventArgs(object DropTarget, DataPackageView DroppedItem, SidebarItemDropPosition dropPosition, DragEventArgs RawEvent) { } - public record ItemDragOverEventArgs(object DropTarget, DataPackageView DroppedItem, SidebarItemDropPosition dropPosition, DragEventArgs RawEvent) { } - public record ItemContextInvokedArgs(object? Item, Point Position) { } - - public interface ISidebarViewModel - { - /// - /// The source/list of items that will be rendered in the sidebar - /// - object SidebarItems { get; } - - /// - /// Gets invoked when the context was requested for an item in the sidebar. - /// Also applies when context was requested for the pane itsself. - /// - /// The sender of this event - /// The for this event. - void HandleItemContextInvokedAsync(object sender, ItemContextInvokedArgs args); - - /// - /// Gets invoked when an item drags over any item of the sidebar. - /// - /// The for this event. - /// A that represents the asynchronous operation. - Task HandleItemDragOverAsync(ItemDragOverEventArgs args); - - /// - /// Gets invoked when an item is dropped on any item of the sidebar. - /// - /// The for this event. - /// A that represents the asynchronous operation. - Task HandleItemDroppedAsync(ItemDroppedEventArgs args); - - /// - /// Gets invoked when an item is invoked (double clicked) on any item of the sidebar. - /// - /// The item that was invoked. - void HandleItemInvokedAsync(object item, PointerUpdateKind pointerUpdateKind); - } -} diff --git a/src/Files.App/UserControls/Sidebar/SidebarViewAutomationPeer.cs b/src/Files.App/UserControls/Sidebar/SidebarViewAutomationPeer.cs deleted file mode 100644 index 990ccdc64672..000000000000 --- a/src/Files.App/UserControls/Sidebar/SidebarViewAutomationPeer.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Microsoft.UI.Xaml.Automation.Peers; -using Microsoft.UI.Xaml.Automation.Provider; - -namespace Files.App.UserControls.Sidebar -{ - public sealed class SidebarViewAutomationPeer : FrameworkElementAutomationPeer, ISelectionProvider - { - public bool CanSelectMultiple => false; - public bool IsSelectionRequired => true; - - private new SidebarView Owner { get; init; } - - public SidebarViewAutomationPeer(SidebarView owner) : base(owner) - { - Owner = owner; - } - - protected override object GetPatternCore(PatternInterface patternInterface) - { - if (patternInterface == PatternInterface.Selection) - { - return this; - } - return base.GetPatternCore(patternInterface); - } - - public IRawElementProviderSimple[] GetSelection() - { - if (Owner.SelectedItemContainer != null) - return - [ - ProviderFromPeer(CreatePeerForElement(Owner.SelectedItemContainer)) - ]; - return []; - } - } -} diff --git a/src/Files.App/UserControls/StatusBar.xaml b/src/Files.App/UserControls/StatusBar.xaml index 8a9e5cd81a96..95d7f9e32fde 100644 --- a/src/Files.App/UserControls/StatusBar.xaml +++ b/src/Files.App/UserControls/StatusBar.xaml @@ -1,104 +1,28 @@ - + - - + + + + - + + + @@ -176,10 +100,10 @@ BorderBrush="Transparent"> - + Style="{StaticResource App.ThemedIcons.Open}" /> @@ -187,17 +111,17 @@ + x:Name="OpenFolderInIDEButton" + Command="{x:Bind Commands.OpenInIDE}" + KeyboardAcceleratorTextOverride="{x:Bind Commands.OpenInIDE.HotKeys, Mode=OneWay}" + Text="{x:Bind Commands.OpenInIDE.Label, Mode=OneWay}" + ToolTipService.ToolTip="{x:Bind Commands.OpenInIDE.LabelWithHotKey, Mode=OneWay}" /> @@ -222,48 +146,39 @@ - - - - - + ThemedIconStyle="{StaticResource App.ThemedIcons.Git.Pull}" + ToolTipService.ToolTip="{x:Bind Commands.GitPull.LabelWithHotKey, Mode=OneWay}" /> - - - - - + ThemedIconStyle="{StaticResource App.ThemedIcons.Git.Push}" + ToolTipService.ToolTip="{x:Bind Commands.GitPush.LabelWithHotKey, Mode=OneWay}" /> - - - - - + ThemedIconStyle="{StaticResource App.ThemedIcons.Git.Sync}" + ToolTipService.ToolTip="{x:Bind Commands.GitSync.LabelWithHotKey, Mode=OneWay}" /> - + Style="{StaticResource App.ThemedIcons.Git}" /> @@ -292,10 +207,10 @@ - + Style="{StaticResource App.ThemedIcons.Git.Branch}" /> @@ -325,17 +240,14 @@ BorderThickness="0,0,0,1"> - - - - + SelectedIndex="0" + Style="{StaticResource PivotSegmentedStyle}"> + + + diff --git a/src/Files.App/UserControls/StatusCenter/StatusCenter.xaml.cs b/src/Files.App/UserControls/StatusCenter/StatusCenter.xaml.cs index c8904dad330f..8798d39d46f5 100644 --- a/src/Files.App/UserControls/StatusCenter/StatusCenter.xaml.cs +++ b/src/Files.App/UserControls/StatusCenter/StatusCenter.xaml.cs @@ -1,7 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using CommunityToolkit.WinUI.UI; +using CommunityToolkit.WinUI; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; diff --git a/src/Files.App/UserControls/TabBar/BaseTabBar.cs b/src/Files.App/UserControls/TabBar/BaseTabBar.cs index 682bcf9838c1..7196b481592c 100644 --- a/src/Files.App/UserControls/TabBar/BaseTabBar.cs +++ b/src/Files.App/UserControls/TabBar/BaseTabBar.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -146,7 +146,7 @@ public void CloseTab(TabBarItem tabItem) Items.Remove(tabItem); tabItem.Unload(); - + // Dispose and save tab arguments PushRecentTab( [ diff --git a/src/Files.App/UserControls/TabBar/ITabBar.cs b/src/Files.App/UserControls/TabBar/ITabBar.cs index 1e01b3b267a4..fad18875b97b 100644 --- a/src/Files.App/UserControls/TabBar/ITabBar.cs +++ b/src/Files.App/UserControls/TabBar/ITabBar.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.UserControls.TabBar { diff --git a/src/Files.App/UserControls/TabBar/ITabBarItem.cs b/src/Files.App/UserControls/TabBar/ITabBarItem.cs index 66a369d75c56..e80b49f9cefa 100644 --- a/src/Files.App/UserControls/TabBar/ITabBarItem.cs +++ b/src/Files.App/UserControls/TabBar/ITabBarItem.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Controls; diff --git a/src/Files.App/UserControls/TabBar/ITabBarItemContent.cs b/src/Files.App/UserControls/TabBar/ITabBarItemContent.cs index 6bc5270d8f19..953acff5a937 100644 --- a/src/Files.App/UserControls/TabBar/ITabBarItemContent.cs +++ b/src/Files.App/UserControls/TabBar/ITabBarItemContent.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; diff --git a/src/Files.App/UserControls/TabBar/TabBar.xaml b/src/Files.App/UserControls/TabBar/TabBar.xaml index fc3534024c33..94613713f1c2 100644 --- a/src/Files.App/UserControls/TabBar/TabBar.xaml +++ b/src/Files.App/UserControls/TabBar/TabBar.xaml @@ -1,15 +1,16 @@ - + @@ -107,59 +108,59 @@ ToolTipService.ToolTip="{helpers:ResourceString Name=TabActions}" Visibility="{x:Bind ShowTabActionsButton, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}"> - + Style="{StaticResource App.ThemedIcons.Panes.Single}" /> - + Text="{x:Bind Commands.NewWindow.Label}" + ThemedIconStyle="{x:Bind Commands.NewWindow.ThemedIconStyle}" /> - + Text="{x:Bind Commands.EnterCompactOverlay.Label}" + ThemedIconStyle="{x:Bind Commands.EnterCompactOverlay.ThemedIconStyle}" /> - + Text="{x:Bind Commands.ExitCompactOverlay.Label}" + ThemedIconStyle="{x:Bind Commands.ExitCompactOverlay.ThemedIconStyle}" /> + x:Load="{x:Bind Commands.SplitPaneHorizontally.IsExecutable, Mode=OneWay}" + Text="{helpers:ResourceString Name=SplitPane}"> - - - - + Command="{x:Bind Commands.SplitPaneVertically, Mode=OneWay}" + KeyboardAcceleratorTextOverride="{x:Bind Commands.SplitPaneVertically.HotKeyText, Mode=OneWay}" + Text="{x:Bind Commands.SplitPaneVertically.Label}" + ThemedIconStyle="{x:Bind Commands.SplitPaneVertically.ThemedIconStyle}" /> + + + @@ -169,37 +170,34 @@ x:Load="{x:Bind Commands.CloseActivePane.IsExecutable, Mode=OneWay}" Text="{helpers:ResourceString Name=ArrangePanes}"> - - - - + Text="{x:Bind Commands.ArrangePanesVertically.Label}" + ThemedIconStyle="{x:Bind Commands.ArrangePanesVertically.ThemedIconStyle}" /> + + + - - - - - + Text="{x:Bind Commands.CloseActivePane.Label}" + ThemedIconStyle="{x:Bind Commands.CloseActivePane.ThemedIconStyle}" /> @@ -262,7 +260,8 @@ Grid.ColumnSpan="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" - Fill="Transparent" /> + Fill="Transparent" + Loaded="DragAreaRectangle_Loaded" /> diff --git a/src/Files.App/UserControls/TabBar/TabBar.xaml.cs b/src/Files.App/UserControls/TabBar/TabBar.xaml.cs index 1151223e4505..5a51b4bae410 100644 --- a/src/Files.App/UserControls/TabBar/TabBar.xaml.cs +++ b/src/Files.App/UserControls/TabBar/TabBar.xaml.cs @@ -1,7 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using CommunityToolkit.WinUI.UI; +using CommunityToolkit.WinUI; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; @@ -18,6 +18,7 @@ public sealed partial class TabBar : BaseTabBar, INotifyPropertyChanged private readonly ICommandManager Commands = Ioc.Default.GetRequiredService(); private readonly IAppearanceSettingsService AppearanceSettingsService = Ioc.Default.GetRequiredService(); + private readonly IWindowContext WindowContext = Ioc.Default.GetRequiredService(); // Fields @@ -44,12 +45,8 @@ public sealed partial class TabBar : BaseTabBar, INotifyPropertyChanged public bool ShowTabActionsButton => AppearanceSettingsService.ShowTabActions; - // Dragging makes the app crash when run as admin. - // For more information: - // - https://github.com/files-community/Files/issues/12390 - // - https://github.com/microsoft/terminal/issues/12017#issuecomment-1004129669 public bool AllowTabsDrag - => !ElevationHelpers.IsAppRunAsAdmin(); + => WindowContext.CanDragAndDrop; public Rectangle DragArea => DragAreaRectangle; @@ -64,18 +61,9 @@ public TabBar() { InitializeComponent(); - tabHoverTimer.Interval = TimeSpan.FromMilliseconds(1000); + tabHoverTimer.Interval = TimeSpan.FromMilliseconds(Constants.DragAndDrop.HoverToOpenTimespan); tabHoverTimer.Tick += TabHoverSelected; - var appWindow = MainWindow.Instance.AppWindow; - - double rightPaddingColumnWidth = - FilePropertiesHelpers.FlowDirectionSettingIsRightToLeft - ? appWindow.TitleBar.LeftInset - : appWindow.TitleBar.RightInset; - - RightPaddingColumn.Width = new(rightPaddingColumnWidth >= 0 ? rightPaddingColumnWidth : 0); - AppearanceSettingsService.PropertyChanged += (s, e) => { switch (e.PropertyName) @@ -172,10 +160,10 @@ private void TabView_TabStripDragOver(object sender, DragEventArgs e) { if (e.DataView.Properties.ContainsKey(TabPathIdentifier)) { - HorizontalTabView.CanReorderTabs = true && !ElevationHelpers.IsAppRunAsAdmin(); + HorizontalTabView.CanReorderTabs = WindowContext.CanDragAndDrop; e.AcceptedOperation = DataPackageOperation.Move; - e.DragUIOverride.Caption = "TabStripDragAndDropUIOverrideCaption".GetLocalizedResource(); + e.DragUIOverride.Caption = Strings.TabStripDragAndDropUIOverrideCaption.GetLocalizedResource(); e.DragUIOverride.IsCaptionVisible = true; e.DragUIOverride.IsGlyphVisible = false; } @@ -187,12 +175,12 @@ private void TabView_TabStripDragOver(object sender, DragEventArgs e) private void TabView_DragLeave(object sender, DragEventArgs e) { - HorizontalTabView.CanReorderTabs = true && !ElevationHelpers.IsAppRunAsAdmin(); + HorizontalTabView.CanReorderTabs = WindowContext.CanDragAndDrop; } private async void TabView_TabStripDrop(object sender, DragEventArgs e) { - HorizontalTabView.CanReorderTabs = true && !ElevationHelpers.IsAppRunAsAdmin(); + HorizontalTabView.CanReorderTabs = WindowContext.CanDragAndDrop; if (!(sender is TabView tabStrip)) return; @@ -207,7 +195,7 @@ private async void TabView_TabStripDrop(object sender, DragEventArgs e) { var item = tabStrip.ContainerFromIndex(i) as TabViewItem; - if (e.GetPosition(item).Y - item.ActualHeight < 0) + if (e.GetPosition(item).X - item.ActualWidth < 0) { index = i; break; @@ -230,8 +218,7 @@ private void TabView_TabDragCompleted(TabView sender, TabViewTabDragCompletedEve else HorizontalTabView.SelectedItem = args.Tab; - if (ApplicationData.Current.LocalSettings.Values.ContainsKey(TabDropHandledIdentifier)) - ApplicationData.Current.LocalSettings.Values.Remove(TabDropHandledIdentifier); + ApplicationData.Current.LocalSettings.Values.Remove(TabDropHandledIdentifier); } private async void TabView_TabDroppedOutside(TabView sender, TabViewTabDroppedOutsideEventArgs args) @@ -274,14 +261,14 @@ private void TabView_PointerWheelChanged(object sender, PointerRoutedEventArgs e if (delta > 0) { // Scroll up, select the next tab - if (HorizontalTabView.SelectedIndex < HorizontalTabView.TabItems.Count - 1) - HorizontalTabView.SelectedIndex++; + if (App.AppModel.TabStripSelectedIndex < Items.Count - 1) + App.AppModel.TabStripSelectedIndex++; } else { // Scroll down, select the previous tab - if (HorizontalTabView.SelectedIndex > 0) - HorizontalTabView.SelectedIndex--; + if (App.AppModel.TabStripSelectedIndex > 0) + App.AppModel.TabStripSelectedIndex--; } e.Handled = true; @@ -345,7 +332,7 @@ private async void TabBarAddNewTabButton_DragOver(object sender, DragEventArgs e e.Handled = true; var deferral = e.GetDeferral(); e.DragUIOverride.IsCaptionVisible = true; - e.DragUIOverride.Caption = string.Format("OpenInNewTab".GetLocalizedResource()); + e.DragUIOverride.Caption = string.Format(Strings.OpenInNewTab.GetLocalizedResource()); e.AcceptedOperation = DataPackageOperation.Link; deferral.Complete(); } @@ -369,5 +356,20 @@ private void TabViewItem_Loaded(object sender, RoutedEventArgs e) }); } } + + private async void DragAreaRectangle_Loaded(object sender, RoutedEventArgs e) + { + if (HorizontalTabView.ActualWidth <= 0 && TabBarAddNewTabButton.Width <= 0) + await Task.Delay(100); + + var titleBarInset = ((AppLanguageHelper.IsPreferredLanguageRtl + ? MainWindow.Instance.AppWindow.TitleBar.LeftInset + : MainWindow.Instance.AppWindow.TitleBar.RightInset) / DragAreaRectangle.XamlRoot.RasterizationScale) + 40; + + RightPaddingColumn.Width = new(titleBarInset > 40 ? titleBarInset : 138); + HorizontalTabView.Measure(new( + HorizontalTabView.ActualWidth - TabBarAddNewTabButton.Width - titleBarInset, + HorizontalTabView.ActualHeight)); + } } -} +} \ No newline at end of file diff --git a/src/Files.App/UserControls/TabBar/TabBarItem.cs b/src/Files.App/UserControls/TabBar/TabBarItem.cs index 2a9335fb90fc..e82897386a41 100644 --- a/src/Files.App/UserControls/TabBar/TabBarItem.cs +++ b/src/Files.App/UserControls/TabBar/TabBarItem.cs @@ -1,14 +1,15 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media.Animation; namespace Files.App.UserControls.TabBar { /// /// Represents item for . /// - public sealed class TabBarItem : ObservableObject, ITabBarItem, IDisposable + public sealed partial class TabBarItem : ObservableObject, ITabBarItem, IDisposable { public Frame ContentFrame { get; private set; } @@ -60,7 +61,7 @@ public TabBarItemParameter NavigationParameter _NavigationArguments = value; if (_NavigationArguments is not null) { - ContentFrame.Navigate(_NavigationArguments.InitialPageType, _NavigationArguments.NavigationParameter); + ContentFrame.Navigate(_NavigationArguments.InitialPageType, _NavigationArguments.NavigationParameter, new SuppressNavigationTransitionInfo()); } else { diff --git a/src/Files.App/UserControls/Toolbar.xaml b/src/Files.App/UserControls/Toolbar.xaml new file mode 100644 index 000000000000..000d1dd38405 --- /dev/null +++ b/src/Files.App/UserControls/Toolbar.xaml @@ -0,0 +1,1194 @@ + + + + + + + + + + + + + + + + + + + + + + + + True + False + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Files.App/UserControls/Toolbar.xaml.cs b/src/Files.App/UserControls/Toolbar.xaml.cs new file mode 100644 index 000000000000..9bea87a1f36e --- /dev/null +++ b/src/Files.App/UserControls/Toolbar.xaml.cs @@ -0,0 +1,191 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using CommunityToolkit.WinUI; +using Files.App.Data.Commands; +using Microsoft.UI.Xaml.Automation; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Media.Imaging; +using System.IO; + +namespace Files.App.UserControls +{ + public sealed partial class Toolbar : UserControl + { + private readonly IUserSettingsService UserSettingsService = Ioc.Default.GetRequiredService(); + private readonly ICommandManager Commands = Ioc.Default.GetRequiredService(); + private readonly IModifiableCommandManager ModifiableCommands = Ioc.Default.GetRequiredService(); + private readonly IAddItemService addItemService = Ioc.Default.GetRequiredService(); + + [GeneratedDependencyProperty] + public partial NavigationToolbarViewModel? ViewModel { get; set; } + + [GeneratedDependencyProperty] + public partial bool ShowViewControlButton { get; set; } + + [GeneratedDependencyProperty] + public partial bool ShowPreviewPaneButton { get; set; } + + public Toolbar() + { + InitializeComponent(); + } + + private void NewEmptySpace_Opening(object sender, object e) + { + if (ViewModel is null) + return; + + NewEmptySpace.Items.Clear(); + + foreach (var item in CreateActionGroupMenuItems(Commands.Groups.NewItem)) + NewEmptySpace.Items.Add(item); + + NewEmptySpace.Items.Add(NewMenuFileFolderSeparator); + + if (!ViewModel.InstanceViewModel.CanCreateFileInPage) + return; + + var cachedNewContextMenuEntries = addItemService.GetEntries(); + if (cachedNewContextMenuEntries is null || cachedNewContextMenuEntries.Count == 0) + return; + + string keyFormat = $"D{cachedNewContextMenuEntries.Count.ToString().Length}"; + + for (int index = 0; index < cachedNewContextMenuEntries.Count; index++) + { + var newEntry = cachedNewContextMenuEntries[index]; + var menuItem = CreateShellNewEntryMenuItem(newEntry); + menuItem.AccessKey = (index + 1).ToString(keyFormat); + menuItem.Command = ViewModel.CreateNewFileCommand; + menuItem.CommandParameter = newEntry; + NewEmptySpace.Items.Add(menuItem); + } + } + + private static MenuFlyoutItem CreateShellNewEntryMenuItem(ShellNewEntry newEntry) + { + if (!string.IsNullOrEmpty(newEntry.IconBase64)) + { + byte[] bitmapData = Convert.FromBase64String(newEntry.IconBase64); + using var ms = new MemoryStream(bitmapData); + var image = new BitmapImage(); + _ = image.SetSourceAsync(ms.AsRandomAccessStream()); + + return new MenuFlyoutItemWithImage + { + Text = newEntry.Name, + BitmapIcon = image, + }; + } + + return new MenuFlyoutItem + { + Text = newEntry.Name, + Icon = new FontIcon + { + Glyph = "\xE7C3" + }, + }; + } + + private void ActionGroupFlyout_Opening(object sender, object e) + { + if (sender is not MenuFlyout flyout) + return; + + var group = flyout == ExtractFlyout ? Commands.Groups.Extract + : flyout == SetAsFlyout ? Commands.Groups.SetAs + : null; + + if (group is null) + return; + + flyout.Items.Clear(); + foreach (var item in CreateActionGroupMenuItems(group)) + flyout.Items.Add(item); + } + + private IEnumerable CreateActionGroupMenuItems(CommandGroup group) + { + return group.Commands + .Select(code => Commands[code]) + .Where(command => command.Code != CommandCodes.None) + .Select(CreateGroupMenuItem) + .ToList(); + } + + private static MenuFlyoutItem CreateGroupMenuItem(IRichCommand command) + { + var item = new MenuFlyoutItem + { + Text = command.Label, + Command = command, + Visibility = command.IsExecutable ? Visibility.Visible : Visibility.Collapsed, + }; + + if (!string.IsNullOrWhiteSpace(command.AccessKey)) + item.AccessKey = command.AccessKey; + + if (command.HotKeyText is string hotKey) + item.KeyboardAcceleratorTextOverride = hotKey; + + if (GetToolbarMenuItemAutomationId(command.Code) is string automationId) + AutomationProperties.SetAutomationId(item, automationId); + + var icon = command.Glyph.ToFontIcon(); + if (icon is not null) + item.Icon = icon; + return item; + } + + private static string? GetToolbarMenuItemAutomationId(CommandCodes code) + { + // These flyout items are generated at runtime, but the interaction tests still + // locate them by the legacy toolbar automation IDs from the old static XAML. + return code switch + { + CommandCodes.CreateFolder => "InnerNavigationToolbarNewFolderButton", + CommandCodes.CreateFile => "File", + CommandCodes.CreateShortcutFromDialog => "InnerNavigationToolbarNewShortcutButton", + _ => null, + }; + } + + private void SortGroup_AccessKeyInvoked(UIElement sender, AccessKeyInvokedEventArgs args) + { + if (sender is MenuFlyoutSubItem menu) + { + var items = menu.Items + .TakeWhile(item => item is not MenuFlyoutSeparator) + .Where(item => item.IsEnabled) + .ToList(); + + string format = $"D{items.Count.ToString().Length}"; + + for (ushort index = 0; index < items.Count; ++index) + { + items[index].AccessKey = (index + 1).ToString(format); + } + } + + } + + private void AppBarButton_AccessKeyInvoked(UIElement sender, AccessKeyInvokedEventArgs args) + { + // Suppress access key invocation if any dialog is open + if (VisualTreeHelper.GetOpenPopupsForXamlRoot(MainWindow.Instance.Content.XamlRoot).Any()) + args.Handled = true; + } + + private void LayoutButton_Click(object sender, RoutedEventArgs e) + { + // Hide flyout after choosing a layout + // Check if LayoutFlyout is not null to handle cases where UI elements are unloaded via x:Load + LayoutFlyout?.Hide(); + } + } +} diff --git a/src/Files.App/UserControls/Widgets/DrivesWidget.xaml b/src/Files.App/UserControls/Widgets/DrivesWidget.xaml index ac2497b73f68..a4024853e5c9 100644 --- a/src/Files.App/UserControls/Widgets/DrivesWidget.xaml +++ b/src/Files.App/UserControls/Widgets/DrivesWidget.xaml @@ -1,4 +1,4 @@ - + + + + + 0 + + @@ -66,7 +71,14 @@ ToolTipService.ToolTip="{helpers:ResourceString Name=ViewMore}"> - + - - + - + - + @@ -155,8 +169,8 @@ TextTrimming="CharacterEllipsis" /> - - + + diff --git a/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml.cs b/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml.cs index 32d5c313fed6..ad1ef36e7890 100644 --- a/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; diff --git a/src/Files.App/UserControls/Widgets/NetworkLocationsWidget.xaml b/src/Files.App/UserControls/Widgets/NetworkLocationsWidget.xaml index 9a624db7b08d..af6080a97546 100644 --- a/src/Files.App/UserControls/Widgets/NetworkLocationsWidget.xaml +++ b/src/Files.App/UserControls/Widgets/NetworkLocationsWidget.xaml @@ -1,4 +1,4 @@ - + + + @@ -37,6 +37,8 @@ x:Name="RecentFilesListView" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" + CanDragItems="True" + DragItemsStarting="RecentFilesListView_DragItemsStarting" IsItemClickEnabled="True" IsRightTapEnabled="True" ItemClick="RecentFilesListView_ItemClick" @@ -46,28 +48,29 @@ SelectionMode="None"> - + + ToolTipService.ToolTip="{x:Bind Path}"> + diff --git a/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs b/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs index 4ae597f560e0..e9bba29b081f 100644 --- a/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs @@ -1,13 +1,15 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; +using Windows.ApplicationModel.DataTransfer; +using Windows.Storage; namespace Files.App.UserControls.Widgets { /// - /// Represents group of control displays a list of recent folders with . + /// Represents a control that displays a list of recent files with . /// public sealed partial class RecentFilesWidget : UserControl { @@ -23,12 +25,39 @@ private void RecentFilesListView_ItemClick(object sender, ItemClickEventArgs e) if (e.ClickedItem is not RecentItem item) return; - ViewModel.NavigateToPath(item.RecentPath); + ViewModel.NavigateToPath(item.Path); } private void RecentFilesListView_RightTapped(object sender, RightTappedRoutedEventArgs e) { ViewModel.BuildItemContextMenu(e.OriginalSource, e); } + + private async void RecentFilesListView_DragItemsStarting(object sender, DragItemsStartingEventArgs e) + { + var item = e.Items.OfType().First(); + var storageItems = new List(); + + try + { + var file = await StorageFile.GetFileFromPathAsync(item.Path); + if (file != null) + storageItems.Add(file); + } + catch + { + e.Cancel = true; + } + + if (storageItems.Count > 0) + { + // Create a new data package and set the storage items + DataPackage dataPackage = new DataPackage(); + dataPackage.SetStorageItems(storageItems); + e.Data.SetDataProvider(StandardDataFormats.StorageItems, request => request.SetData(storageItems)); + + e.Data.RequestedOperation = DataPackageOperation.Move | DataPackageOperation.Copy | DataPackageOperation.Link; + } + } } } diff --git a/src/Files.App/Utils/Cloud/CloudDriveSyncStatus.cs b/src/Files.App/Utils/Cloud/CloudDriveSyncStatus.cs index 10f1d964ccaf..03b1185010b0 100644 --- a/src/Files.App/Utils/Cloud/CloudDriveSyncStatus.cs +++ b/src/Files.App/Utils/Cloud/CloudDriveSyncStatus.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.Cloud { diff --git a/src/Files.App/Utils/Cloud/CloudDriveSyncStatusUI.cs b/src/Files.App/Utils/Cloud/CloudDriveSyncStatusUI.cs index 54904d55743b..4314f96eb293 100644 --- a/src/Files.App/Utils/Cloud/CloudDriveSyncStatusUI.cs +++ b/src/Files.App/Utils/Cloud/CloudDriveSyncStatusUI.cs @@ -1,22 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.App.Utils.Cloud; using Microsoft.UI.Xaml; namespace Files.App.Utils.Cloud { - public sealed class CloudDriveSyncStatusUI : ObservableObject + public sealed partial class CloudDriveSyncStatusUI : ObservableObject { public string Glyph { get; } - public Style OpacityIcon { get; } + public Style ThemedIconStyle { get; } public CloudDriveSyncStatus SyncStatus { get; } public bool LoadSyncStatus { get; } - public string SyncStatusString { get; } = "CloudDriveSyncStatus_Unknown".GetLocalizedResource(); + public string SyncStatusString { get; } = Strings.CloudDriveSyncStatus_Unknown.GetLocalizedResource(); public CloudDriveSyncStatusUI() { @@ -28,11 +27,11 @@ private CloudDriveSyncStatusUI(CloudDriveSyncStatus syncStatus) SyncStatus = syncStatus; } - private CloudDriveSyncStatusUI(string glyph, Style opacityIcon, CloudDriveSyncStatus syncStatus, string SyncStatusStringKey) + private CloudDriveSyncStatusUI(string glyph, Style themedIconStyle, CloudDriveSyncStatus syncStatus, string SyncStatusStringKey) { SyncStatus = syncStatus; Glyph = glyph; - OpacityIcon = opacityIcon; + ThemedIconStyle = themedIconStyle; LoadSyncStatus = true; SyncStatusString = SyncStatusStringKey.GetLocalizedResource(); } @@ -41,23 +40,23 @@ private CloudDriveSyncStatusUI(string glyph, Style opacityIcon, CloudDriveSyncSt { // File CloudDriveSyncStatus.FileOnline - => new CloudDriveSyncStatusUI("\uE753", (Style)Application.Current.Resources["ColorIconCloud"], syncStatus, "CloudDriveSyncStatus_Online"), + => new CloudDriveSyncStatusUI("\uE753", (Style)Application.Current.Resources["App.ThemedIcons.Status.Cloud"], syncStatus, "CloudDriveSyncStatus_Online"), CloudDriveSyncStatus.FileOffline - => new CloudDriveSyncStatusUI("\uE73E", (Style)Application.Current.Resources["ColorIconCloudSynced"], syncStatus, "CloudDriveSyncStatus_Offline"), + => new CloudDriveSyncStatusUI("\uE73E", (Style)Application.Current.Resources["App.ThemedIcons.Status.Available"], syncStatus, "CloudDriveSyncStatus_Offline"), CloudDriveSyncStatus.FileOfflinePinned - => new CloudDriveSyncStatusUI("\uE73E", (Style)Application.Current.Resources["ColorIconCloudKeepOffline"], syncStatus, "CloudDriveSyncStatus_Offline"), + => new CloudDriveSyncStatusUI("\uE73E", (Style)Application.Current.Resources["App.ThemedIcons.Status.KeepOffline"], syncStatus, "CloudDriveSyncStatus_Offline"), CloudDriveSyncStatus.FileSync - => new CloudDriveSyncStatusUI("\uE895", (Style)Application.Current.Resources["ColorIconCloudSyncing"], syncStatus, "CloudDriveSyncStatus_Sync"), + => new CloudDriveSyncStatusUI("\uE895", (Style)Application.Current.Resources["App.ThemedIcons.Status.Syncing"], syncStatus, "CloudDriveSyncStatus_Sync"), - // Folder + //// Folder CloudDriveSyncStatus.FolderOnline or CloudDriveSyncStatus.FolderOfflinePartial - => new CloudDriveSyncStatusUI("\uE753", (Style)Application.Current.Resources["ColorIconCloud"], syncStatus, "CloudDriveSyncStatus_PartialOffline"), + => new CloudDriveSyncStatusUI("\uE753", (Style)Application.Current.Resources["App.ThemedIcons.Status.Cloud"], syncStatus, "CloudDriveSyncStatus_PartialOffline"), CloudDriveSyncStatus.FolderOfflineFull or CloudDriveSyncStatus.FolderEmpty - => new CloudDriveSyncStatusUI("\uE73E", (Style)Application.Current.Resources["ColorIconCloudSynced"], syncStatus, "CloudDriveSyncStatus_Offline"), + => new CloudDriveSyncStatusUI("\uE73E", (Style)Application.Current.Resources["App.ThemedIcons.Status.Available"], syncStatus, "CloudDriveSyncStatus_Offline"), CloudDriveSyncStatus.FolderOfflinePinned - => new CloudDriveSyncStatusUI("\uE73E", (Style)Application.Current.Resources["ColorIconCloudKeepOffline"], syncStatus, "CloudDriveSyncStatus_Offline"), + => new CloudDriveSyncStatusUI("\uE73E", (Style)Application.Current.Resources["App.ThemedIcons.Status.KeepOffline"], syncStatus, "CloudDriveSyncStatus_Offline"), CloudDriveSyncStatus.FolderExcluded - => new CloudDriveSyncStatusUI("\uF140", (Style)Application.Current.Resources["ColorIconCloudUnavailable"], syncStatus, "CloudDriveSyncStatus_Excluded"), + => new CloudDriveSyncStatusUI("\uF140", (Style)Application.Current.Resources["App.ThemedIcons.Status.Unavailable"], syncStatus, "CloudDriveSyncStatus_Excluded"), // Unknown _ => new CloudDriveSyncStatusUI(syncStatus), diff --git a/src/Files.App/Utils/Cloud/CloudDrivesDetector.cs b/src/Files.App/Utils/Cloud/CloudDrivesDetector.cs index f60eff34ced8..ebbdebb0f5c7 100644 --- a/src/Files.App/Utils/Cloud/CloudDrivesDetector.cs +++ b/src/Files.App/Utils/Cloud/CloudDrivesDetector.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.Win32; using System.IO; @@ -41,7 +41,7 @@ private static Task> DetectYandexDisk() var results = new List(); using var yandexKey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Yandex\Yandex.Disk.2"); - var syncedFolder = (string)yandexKey?.GetValue("RootFolder"); + var syncedFolder = (string?)yandexKey?.GetValue("RootFolder"); if (syncedFolder is not null) { results.Add(new CloudProvider(CloudProviders.Yandex) @@ -59,48 +59,29 @@ private static Task> DetectGenericCloudDrive() var results = new List(); using var clsidKey = Registry.ClassesRoot.OpenSubKey(@"CLSID"); using var namespaceKey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace"); + using var syncRootManagerKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\SyncRootManager"); foreach (var subKeyName in namespaceKey?.GetSubKeyNames() ?? []) { - using var clsidSubKey = SafetyExtensions.IgnoreExceptions(() => clsidKey.OpenSubKey(subKeyName)); + using var clsidSubKey = SafetyExtensions.IgnoreExceptions(() => clsidKey?.OpenSubKey(subKeyName)); if (clsidSubKey is not null && (int?)clsidSubKey.GetValue("System.IsPinnedToNameSpaceTree") is 1) { - using var namespaceSubKey = namespaceKey.OpenSubKey(subKeyName); - var driveType = (string)namespaceSubKey?.GetValue(string.Empty); - if (driveType is null) - { + using var namespaceSubKey = namespaceKey?.OpenSubKey(subKeyName); + var driveIdentifier = (string?)namespaceSubKey?.GetValue(string.Empty); + if (driveIdentifier is null) continue; - } - - //Nextcloud specific - var appName = (string)namespaceSubKey?.GetValue("ApplicationName"); - if (!string.IsNullOrEmpty(appName) && appName == "Nextcloud") - { - driveType = appName; - } - // Drive specific - if (driveType.StartsWith("iCloudDrive")) - driveType = "iCloudDrive"; - if (driveType.StartsWith("iCloudPhotos")) - driveType = "iCloudPhotos"; - if (driveType.StartsWith("ownCloud")) - driveType = "ownCloud"; - if (driveType.StartsWith("ProtonDrive")) - driveType = "ProtonDrive"; + var driveType = GetDriveType(driveIdentifier, namespaceSubKey, syncRootManagerKey); using var bagKey = clsidSubKey.OpenSubKey(@"Instance\InitPropertyBag"); - var syncedFolder = (string)bagKey?.GetValue("TargetFolderPath"); + var syncedFolder = (string?)bagKey?.GetValue("TargetFolderPath"); if (syncedFolder is null) - { continue; - } // Also works for OneDrive, Box, Dropbox - CloudProviders? driveID = driveType switch + CloudProviders? cloudProvider = driveType switch { "MEGA" => CloudProviders.Mega, - "Amazon Drive" => CloudProviders.AmazonDrive, "Nextcloud" => CloudProviders.Nextcloud, "Jottacloud" => CloudProviders.Jottacloud, "iCloudDrive" => CloudProviders.AppleCloudDrive, @@ -108,40 +89,42 @@ private static Task> DetectGenericCloudDrive() "Creative Cloud Files" => CloudProviders.AdobeCreativeCloud, "ownCloud" => CloudProviders.ownCloud, "ProtonDrive" => CloudProviders.ProtonDrive, + "kDrive" => CloudProviders.kDrive, + "Lucid" => CloudProviders.LucidLink, + "SyncCom" => CloudProviders.SyncDrive, + "MagentaCLOUD" => CloudProviders.MagentaCloud, _ => null, }; - if (driveID is null) - { + + if (cloudProvider is null) continue; - } - string nextCloudValue = (string)namespaceSubKey?.GetValue(string.Empty); - string ownCloudValue = (string)clsidSubKey?.GetValue(string.Empty); + var nextCloudValue = (string?)namespaceSubKey?.GetValue(string.Empty); + var clsidDefaultValue = (string?)clsidSubKey?.GetValue(string.Empty); - using var defaultIconKey = clsidSubKey.OpenSubKey(@"DefaultIcon"); - string iconPath = (string)defaultIconKey?.GetValue(string.Empty); + using var defaultIconKey = clsidSubKey?.OpenSubKey(@"DefaultIcon"); + var iconPath = (string?)defaultIconKey?.GetValue(string.Empty); - results.Add(new CloudProvider(driveID.Value) + results.Add(new CloudProvider(cloudProvider.Value) { - Name = driveID switch + Name = cloudProvider switch { CloudProviders.Mega => $"MEGA ({Path.GetFileName(syncedFolder.TrimEnd('\\'))})", - CloudProviders.AmazonDrive => $"Amazon Drive", CloudProviders.Nextcloud => !string.IsNullOrEmpty(nextCloudValue) ? nextCloudValue : "Nextcloud", CloudProviders.Jottacloud => $"Jottacloud", CloudProviders.AppleCloudDrive => $"iCloud Drive", CloudProviders.AppleCloudPhotos => $"iCloud Photos", CloudProviders.AdobeCreativeCloud => $"Creative Cloud Files", - CloudProviders.ownCloud => !string.IsNullOrEmpty(ownCloudValue) ? ownCloudValue : "ownCloud", + CloudProviders.ownCloud => !string.IsNullOrEmpty(clsidDefaultValue) ? clsidDefaultValue : "ownCloud", CloudProviders.ProtonDrive => $"Proton Drive", + CloudProviders.kDrive => !string.IsNullOrEmpty(clsidDefaultValue) ? clsidDefaultValue : "kDrive", + CloudProviders.LucidLink => !string.IsNullOrEmpty(clsidDefaultValue) ? clsidDefaultValue : "lucidLink", + CloudProviders.SyncDrive => !string.IsNullOrEmpty(clsidDefaultValue) ? clsidDefaultValue : "Sync", + CloudProviders.MagentaCloud => !string.IsNullOrEmpty(clsidDefaultValue) ? clsidDefaultValue : "MagentaCLOUD", _ => null }, SyncFolder = syncedFolder, - IconData = driveID switch - { - CloudProviders.ProtonDrive => Win32Helper.ExtractSelectedIconsFromDLL(iconPath, new List() { 32512 }).FirstOrDefault()?.IconData, - _ => null - } + IconData = GetIconData(iconPath) }); } } @@ -149,6 +132,17 @@ private static Task> DetectGenericCloudDrive() return Task.FromResult>(results); } + private static byte[]? GetIconData(string iconPath) + { + if (string.IsNullOrEmpty(iconPath) || !File.Exists(iconPath)) + return null; + + if (iconPath.EndsWith(".dll") || iconPath.EndsWith(".exe")) + return Win32Helper.ExtractSelectedIconsFromDLL(iconPath, new List() { 32512 }).FirstOrDefault()?.IconData; + + return File.ReadAllBytes(iconPath); + } + private static Task> DetectOneDrive() { using var oneDriveAccountsKey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\OneDrive\Accounts"); @@ -161,8 +155,8 @@ private static Task> DetectOneDrive() foreach (var account in oneDriveAccountsKey.GetSubKeyNames()) { var accountKeyName = @$"{oneDriveAccountsKey.Name}\{account}"; - var displayName = (string)Registry.GetValue(accountKeyName, "DisplayName", null); - var userFolder = (string)Registry.GetValue(accountKeyName, "UserFolder", null); + var displayName = (string?)Registry.GetValue(accountKeyName, "DisplayName", null); + var userFolder = (string?)Registry.GetValue(accountKeyName, "UserFolder", null); var accountName = string.IsNullOrWhiteSpace(displayName) ? "OneDrive" : $"OneDrive - {displayName}"; if (!string.IsNullOrWhiteSpace(userFolder) && !oneDriveAccounts.Any(x => x.Name == accountName)) @@ -241,7 +235,7 @@ private static Task> DetectpCloudDrive() var results = new List(); using var pCloudDriveKey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\pCloud"); - var syncedFolder = (string)pCloudDriveKey?.GetValue("SyncDrive"); + var syncedFolder = (string?)pCloudDriveKey?.GetValue("SyncDrive"); if (syncedFolder is not null) { string iconPath = Path.Combine(programFilesFolder, "pCloud Drive", "pCloud.exe"); @@ -301,7 +295,7 @@ private static Task> DetectSeadriveDrive() var results = new List(); using var SeadriveKey = Registry.CurrentUser.OpenSubKey(@"Software\SeaDrive\Seafile Drive Client\Settings"); - var syncFolder = (string)SeadriveKey?.GetValue("seadriveRoot"); + var syncFolder = (string?)SeadriveKey?.GetValue("seadriveRoot"); if (SeadriveKey is not null) { string iconPath = Path.Combine(programFilesFolder, "SeaDrive", "bin", "seadrive.exe"); @@ -345,5 +339,34 @@ private static Task> DetectAutodeskDrive() return Task.FromResult>(results); } + + private static string GetDriveType(string driveIdentifier, RegistryKey? namespaceSubKey, RegistryKey? syncRootManagerKey) + { + // Drive specific + if (driveIdentifier.StartsWith("iCloudDrive")) + return "iCloudDrive"; + if (driveIdentifier.StartsWith("iCloudPhotos")) + return "iCloudPhotos"; + if (driveIdentifier.StartsWith("ownCloud")) + return "ownCloud"; + if (driveIdentifier.StartsWith("ProtonDrive")) + return "ProtonDrive"; + if (driveIdentifier.StartsWith("SyncCom")) + return "SyncCom"; + + // Nextcloud specific + var appNameFromNamespace = (string?)namespaceSubKey?.GetValue("ApplicationName"); + if (!string.IsNullOrEmpty(appNameFromNamespace) && appNameFromNamespace == "Nextcloud") + return appNameFromNamespace; + + // kDrive specific + var appNameFromSyncRoot = (string?)syncRootManagerKey?.OpenSubKey(driveIdentifier)?.GetValue(string.Empty); + if (!string.IsNullOrEmpty(appNameFromNamespace) && appNameFromNamespace == "kDrive") + return appNameFromNamespace; + if (!string.IsNullOrEmpty(appNameFromSyncRoot) && appNameFromSyncRoot == "kDrive") + return appNameFromSyncRoot; + + return driveIdentifier; + } } } diff --git a/src/Files.App/Utils/Cloud/CloudDrivesManager.cs b/src/Files.App/Utils/Cloud/CloudDrivesManager.cs index c4eec511b3d9..8e845dd50ed8 100644 --- a/src/Files.App/Utils/Cloud/CloudDrivesManager.cs +++ b/src/Files.App/Utils/Cloud/CloudDrivesManager.cs @@ -1,8 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.Extensions.Logging; using System.Collections.Specialized; +using System.IO; using Windows.Storage; namespace Files.App.Utils.Cloud @@ -10,12 +11,10 @@ namespace Files.App.Utils.Cloud public static class CloudDrivesManager { private static readonly ILogger _logger = Ioc.Default.GetRequiredService>(); - private static readonly ICloudDetector _detector = Ioc.Default.GetRequiredService(); - public static EventHandler DataChanged; - private static readonly List _Drives = []; + public static IReadOnlyList Drives { get @@ -41,7 +40,7 @@ public static async Task UpdateDrivesAsync() { Text = provider.Name, Path = provider.SyncFolder, - Type = DriveType.CloudDrive, + Type = Data.Items.DriveType.CloudDrive, }; try @@ -50,6 +49,14 @@ public static async Task UpdateDrivesAsync() _ = MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => cloudProviderItem.UpdatePropertiesAsync()); } + catch (FileNotFoundException ex) + { + _logger?.LogInformation(ex, "Failed to find the cloud folder"); + } + catch (UnauthorizedAccessException ex) + { + _logger?.LogInformation(ex, " Failed to access the cloud folder"); + } catch (Exception ex) { _logger?.LogWarning(ex, "Cloud provider local folder couldn't be found"); @@ -63,6 +70,25 @@ public static async Task UpdateDrivesAsync() ShowProperties = true, }; + _ = LoadIconAsync(cloudProviderItem, provider); + lock (_Drives) + { + if (_Drives.Any(x => x.Path == cloudProviderItem.Path)) + continue; + + _Drives.Add(cloudProviderItem); + } + DataChanged?.Invoke( + SectionType.CloudDrives, + new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, cloudProviderItem) + ); + } + } + + private static async Task LoadIconAsync(DriveItem cloudProviderItem, ICloudProvider provider) + { + try + { var iconData = provider.IconData; if (iconData is null) @@ -83,20 +109,11 @@ public static async Task UpdateDrivesAsync() await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(async () => cloudProviderItem.Icon = await iconData.ToBitmapAsync()); } - - lock (_Drives) - { - if (_Drives.Any(x => x.Path == cloudProviderItem.Path)) - continue; - - _Drives.Add(cloudProviderItem); - } - - DataChanged?.Invoke( - SectionType.CloudDrives, - new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, cloudProviderItem) - ); + } + catch (Exception ex) + { + _logger?.LogWarning(ex, "Failed to load icon for cloud provider \"{ProviderName}\"", provider.Name); } } } -} +} \ No newline at end of file diff --git a/src/Files.App/Utils/Cloud/CloudProvider.cs b/src/Files.App/Utils/Cloud/CloudProvider.cs index 9444423327ac..b827bd596243 100644 --- a/src/Files.App/Utils/Cloud/CloudProvider.cs +++ b/src/Files.App/Utils/Cloud/CloudProvider.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.Cloud { diff --git a/src/Files.App/Utils/Cloud/CloudProviders.cs b/src/Files.App/Utils/Cloud/CloudProviders.cs index c46f9c232def..e708103929e0 100644 --- a/src/Files.App/Utils/Cloud/CloudProviders.cs +++ b/src/Files.App/Utils/Cloud/CloudProviders.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.Cloud { @@ -19,8 +19,6 @@ public enum CloudProviders AppleCloudPhotos, - AmazonDrive, - Nextcloud, Yandex, @@ -45,6 +43,14 @@ public enum CloudProviders ProtonDrive, - LucidLink + LucidLink, + + kDrive, + + SyncDrive, + + MagentaCloud, + + OXDrive } } diff --git a/src/Files.App/Utils/Cloud/Detector/AbstractCloudDetector.cs b/src/Files.App/Utils/Cloud/Detector/AbstractCloudDetector.cs index 4ec15924ed76..bbd88ff03c72 100644 --- a/src/Files.App/Utils/Cloud/Detector/AbstractCloudDetector.cs +++ b/src/Files.App/Utils/Cloud/Detector/AbstractCloudDetector.cs @@ -1,9 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.App.Utils.Cloud; -using System.Collections.Generic; -using System.Threading.Tasks; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.Cloud { diff --git a/src/Files.App/Utils/Cloud/Detector/BoxCloudDetector.cs b/src/Files.App/Utils/Cloud/Detector/BoxCloudDetector.cs index 64dd61658340..42b7104290ac 100644 --- a/src/Files.App/Utils/Cloud/Detector/BoxCloudDetector.cs +++ b/src/Files.App/Utils/Cloud/Detector/BoxCloudDetector.cs @@ -1,7 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.App.Utils.Cloud; using System.IO; using Windows.Storage; diff --git a/src/Files.App/Utils/Cloud/Detector/CloudDetector.cs b/src/Files.App/Utils/Cloud/Detector/CloudDetector.cs index 24dda707a475..f68aa036f6ef 100644 --- a/src/Files.App/Utils/Cloud/Detector/CloudDetector.cs +++ b/src/Files.App/Utils/Cloud/Detector/CloudDetector.cs @@ -1,7 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.App.Utils.Cloud; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.Cloud { @@ -33,7 +31,7 @@ private static IEnumerable EnumerateDetectors() yield return new BoxCloudDetector(); yield return new GenericCloudDetector(); yield return new SynologyDriveCloudDetector(); - yield return new LucidLinkCloudDetector(); + yield return new OXDriveCloudDetector(); } } } diff --git a/src/Files.App/Utils/Cloud/Detector/DropBoxCloudDetector.cs b/src/Files.App/Utils/Cloud/Detector/DropBoxCloudDetector.cs index 9f15379da01b..5905b16eaec1 100644 --- a/src/Files.App/Utils/Cloud/Detector/DropBoxCloudDetector.cs +++ b/src/Files.App/Utils/Cloud/Detector/DropBoxCloudDetector.cs @@ -1,9 +1,8 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.App.Utils.Cloud; +using Microsoft.Extensions.Logging; using System.IO; -using System.Text.Json; using Windows.Storage; namespace Files.App.Utils.Cloud @@ -15,8 +14,60 @@ public sealed class DropBoxCloudDetector : AbstractCloudDetector { protected override async IAsyncEnumerable GetProviders() { - string jsonPath = Path.Combine(UserDataPaths.GetDefault().LocalAppData, @"Dropbox\info.json"); + string? infoJsonPath = null; + // First, try website version + string websiteJsonPath = Path.Combine(UserDataPaths.GetDefault().LocalAppData, @"Dropbox\info.json"); + if (File.Exists(websiteJsonPath)) + { + infoJsonPath = websiteJsonPath; + App.Logger.LogInformation("Dropbox: Found website version at {Path}", websiteJsonPath); + } + else + { + // Fallback: Check Store versions + string packagesPath = Path.Combine(UserDataPaths.GetDefault().LocalAppData, "Packages"); + if (Directory.Exists(packagesPath)) + { + var dropboxPackages = Directory.GetDirectories(packagesPath, "DropboxInc.Dropbox_*"); + + string? newestInfoJsonPath = null; + DateTime newestTimestamp = DateTime.MinValue; + + foreach (var packageDir in dropboxPackages) + { + string storeJsonPath = Path.Combine(packageDir, @"LocalCache\Local\Dropbox\info.json"); + if (File.Exists(storeJsonPath)) + { + var lastWriteTime = File.GetLastWriteTime(storeJsonPath); + if (lastWriteTime > newestTimestamp) + { + newestTimestamp = lastWriteTime; + newestInfoJsonPath = storeJsonPath; + } + } + } + + if (newestInfoJsonPath is not null) + { + infoJsonPath = newestInfoJsonPath; + App.Logger.LogInformation("Dropbox: Found Store version at {Path} (last modified: {Timestamp})", newestInfoJsonPath, newestTimestamp); + } + } + } + + if (infoJsonPath is null) + yield break; + + // Parse info.json and yield providers + await foreach (var provider in ParseInfoJson(infoJsonPath)) + { + yield return provider; + } + } + + private async IAsyncEnumerable ParseInfoJson(string jsonPath) + { var configFile = await StorageFile.GetFileFromPathAsync(jsonPath); using var jsonDoc = JsonDocument.Parse(await FileIO.ReadTextAsync(configFile)); var jsonElem = jsonDoc.RootElement; diff --git a/src/Files.App/Utils/Cloud/Detector/GenericCloudDetector.cs b/src/Files.App/Utils/Cloud/Detector/GenericCloudDetector.cs index b5e11225a01f..b3e2afa9976f 100644 --- a/src/Files.App/Utils/Cloud/Detector/GenericCloudDetector.cs +++ b/src/Files.App/Utils/Cloud/Detector/GenericCloudDetector.cs @@ -1,7 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.App.Utils.Cloud; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.Cloud { diff --git a/src/Files.App/Utils/Cloud/Detector/GoogleDriveCloudDetector.cs b/src/Files.App/Utils/Cloud/Detector/GoogleDriveCloudDetector.cs index 31c03bacd6b8..502a4c4a1fa7 100644 --- a/src/Files.App/Utils/Cloud/Detector/GoogleDriveCloudDetector.cs +++ b/src/Files.App/Utils/Cloud/Detector/GoogleDriveCloudDetector.cs @@ -1,17 +1,27 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.Data.Sqlite; +using Microsoft.Extensions.Logging; +using Microsoft.Win32; using System.IO; +using Vanara.Windows.Shell; using Windows.Storage; namespace Files.App.Utils.Cloud { /// - /// Provides an utility for Google Drive Cloud detection. + /// Provides a utility for Google Drive Cloud detection. /// public sealed class GoogleDriveCloudDetector : AbstractCloudDetector { + private static readonly ILogger _logger = Ioc.Default.GetRequiredService>(); + + private const string _googleDriveRegKeyName = @"Software\Google\DriveFS"; + private const string _googleDriveRegValName = "PerAccountPreferences"; + private const string _googleDriveRegValPropName = "value"; + private const string _googleDriveRegValPropPropName = "mount_point_path"; + protected override async IAsyncEnumerable GetProviders() { // Google Drive's sync database can be in a couple different locations. Go find it. @@ -28,9 +38,9 @@ await FilesystemTasks.Wrap(() => StorageFile.GetFileFromPathAsync(Path.Combine(a // Build the connection and sql command SQLitePCL.Batteries_V2.Init(); - using var database = new SqliteConnection($"Data Source='{syncDbPath}'"); - using var cmdRoot = new SqliteCommand("SELECT * FROM roots", database); - using var cmdMedia = new SqliteCommand("SELECT * FROM media WHERE fs_type=10", database); + await using var database = new SqliteConnection($"Data Source='{syncDbPath}'"); + await using var cmdRoot = new SqliteCommand("SELECT * FROM roots", database); + await using var cmdMedia = new SqliteCommand("SELECT * FROM media WHERE fs_type=10", database); // Open the connection and execute the command database.Open(); @@ -53,10 +63,18 @@ await FilesystemTasks.Wrap(() => StorageFile.GetFileFromPathAsync(Path.Combine(a path = path.Substring(@"\\?\".Length); } - var folder = await StorageFolder.GetFolderFromPathAsync(path); + var folderResult = await FilesystemTasks.Wrap(() => StorageFolder.GetFolderFromPathAsync(path).AsTask()); + if (!folderResult) + { + _logger.LogWarning($"Could not access Google Drive path as local storage: {path}"); + continue; + } + + var folder = folderResult.Result; string title = reader["title"]?.ToString() ?? folder.Name; - App.AppModel.GoogleDrivePath = path; + Debug.WriteLine("YIELD RETURNING from `GoogleDriveCloudDetector.GetProviders()` (roots): "); + Debug.WriteLine($"Name: Google Drive ({title}); SyncFolder: {path}"); yield return new CloudProvider(CloudProviders.GoogleDrive) { @@ -65,6 +83,7 @@ await FilesystemTasks.Wrap(() => StorageFile.GetFileFromPathAsync(Path.Combine(a }; } + var iconFile = await GetGoogleDriveIconFileAsync(); // Google virtual drive reader = cmdMedia.ExecuteReader(); @@ -74,13 +93,21 @@ await FilesystemTasks.Wrap(() => StorageFile.GetFileFromPathAsync(Path.Combine(a if (string.IsNullOrWhiteSpace(path)) continue; - var folder = await StorageFolder.GetFolderFromPathAsync(path); - string title = reader["name"]?.ToString() ?? folder.Name; - string iconPath = Path.Combine(Environment.GetEnvironmentVariable("ProgramFiles"), "Google", "Drive File Stream", "drive_fs.ico"); + if (!AddMyDriveToPathAndValidate(ref path)) + continue; - App.AppModel.GoogleDrivePath = path; + var folderResult = await FilesystemTasks.Wrap(() => StorageFolder.GetFolderFromPathAsync(path).AsTask()); + if (!folderResult) + { + _logger.LogWarning($"Could not access Google Drive path as local storage: {path}"); + continue; + } - StorageFile iconFile = await FilesystemTasks.Wrap(() => StorageFile.GetFileFromPathAsync(iconPath).AsTask()); + var folder = folderResult.Result; + string title = reader["name"]?.ToString() ?? folder.Name; + + Debug.WriteLine("YIELD RETURNING from `GoogleDriveCloudDetector.GetProviders` (media): "); + Debug.WriteLine($"Name: {title}; SyncFolder: {path}"); yield return new CloudProvider(CloudProviders.GoogleDrive) { @@ -89,6 +116,199 @@ await FilesystemTasks.Wrap(() => StorageFile.GetFileFromPathAsync(Path.Combine(a IconData = iconFile is not null ? await iconFile.ToByteArrayAsync() : null, }; } + + // Log the contents of the root_preferences database to the debug output. + await Inspect(database, "SELECT * FROM roots", "root_preferences db, roots table"); + await Inspect(database, "SELECT * FROM media", "root_preferences db, media table"); + await Inspect(database, "SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY 1", "root_preferences db, all tables"); + + // Query the Windows Registry for the base Google Drive path and time the query. + var sw = Stopwatch.StartNew(); + var googleDrivePath = GetRegistryBasePath() ?? string.Empty; + sw.Stop(); + Debug.WriteLine($"Google Drive path registry check took {sw.Elapsed} seconds."); + + // Add "My Drive" to the base GD path; validate; return the resulting cloud provider. + if (!AddMyDriveToPathAndValidate(ref googleDrivePath)) + yield break; + yield return new CloudProvider(CloudProviders.GoogleDrive) + { + Name = "Google Drive", + SyncFolder = googleDrivePath, + IconData = iconFile is not null ? await iconFile.ToByteArrayAsync() : null + }; + } + + private static async Task Inspect(SqliteConnection database, string sqlCommand, string targetDescription) + { + await using var cmdTablesAll = new SqliteCommand(sqlCommand, database); + var reader = await cmdTablesAll.ExecuteReaderAsync(); + var colNamesList = Enumerable.Range(0, reader.FieldCount).Select(i => reader.GetName(i)).ToList(); + + Debug.WriteLine($"BEGIN LOGGING of {targetDescription}"); + + for (int rowIdx = 0; reader.Read() is not false; rowIdx++) + { + var colVals = new object[reader.FieldCount]; + reader.GetValues(colVals); + + colVals.Select((val, colIdx) => $"row {rowIdx}: column {colIdx}: {colNamesList[colIdx]}: {val}") + .ToList().ForEach(s => Debug.WriteLine(s)); + } + + Debug.WriteLine($"END LOGGING of {targetDescription} contents"); + } + + private static JsonDocument? GetGoogleDriveRegValJson() + { + // This will be null if the key name is not found. + using var googleDriveRegKey = Registry.CurrentUser.OpenSubKey(_googleDriveRegKeyName); + + if (googleDriveRegKey is null) + return null; + + var googleDriveRegVal = googleDriveRegKey.GetValue(_googleDriveRegValName); + + if (googleDriveRegVal is null) + return null; + + JsonDocument? googleDriveRegValueJson = null; + try + { + googleDriveRegValueJson = JsonDocument.Parse(googleDriveRegVal.ToString() ?? ""); + } + catch (JsonException je) + { + _logger.LogWarning(je, $"Google Drive registry value for value name '{_googleDriveRegValName}' could not be parsed as a JsonDocument."); + } + + return googleDriveRegValueJson; + } + + /// + /// Get the base file system path for Google Drive from the Registry. + /// + /// + /// For advanced "Google Drive for desktop" settings reference, see: + /// https://support.google.com/a/answer/7644837 + /// + public static string? GetRegistryBasePath() + { + var googleDriveRegValJson = GetGoogleDriveRegValJson(); + + if (googleDriveRegValJson is null) + return null; + + var googleDriveRegValJsonProperty = googleDriveRegValJson + .RootElement.EnumerateObject() + .FirstOrDefault(); + + // A default "JsonProperty" struct has an undefined "Value.ValueKind" and throws an + // error if you try to call "EnumerateArray" on its value. + if (googleDriveRegValJsonProperty.Value.ValueKind == JsonValueKind.Undefined) + { + _logger.LogWarning($"Root element of Google Drive registry value for value name '{_googleDriveRegValName}' was empty."); + return null; + } + + Debug.WriteLine("REGISTRY LOGGING"); + Debug.WriteLine(googleDriveRegValJsonProperty.ToString()); + + var item = googleDriveRegValJsonProperty.Value.EnumerateArray().FirstOrDefault(); + if (item.ValueKind == JsonValueKind.Undefined) + { + _logger.LogWarning($"Array in the root element of Google Drive registry value for value name '{_googleDriveRegValName}' was empty."); + return null; + } + + if (!item.TryGetProperty(_googleDriveRegValPropName, out var googleDriveRegValProp)) + { + _logger.LogWarning($"First element in the Google Drive Registry Root Array did not have property named {_googleDriveRegValPropName}"); + return null; + } + + if (!googleDriveRegValProp.TryGetProperty(_googleDriveRegValPropPropName, out var googleDriveRegValPropProp)) + { + _logger.LogWarning($"Value from {_googleDriveRegValPropName} did not have property named {_googleDriveRegValPropPropName}"); + return null; + } + + var path = googleDriveRegValPropProp.GetString(); + if (path is not null) + return ConvertDriveLetterToPathAndValidate(ref path) ? path : null; + + _logger.LogWarning($"Could not get string from value from {_googleDriveRegValPropPropName}"); + return null; + } + + /// + /// If Google Drive is mounted as a drive, then the path found in the registry will be + /// *just* the drive letter (e.g. just "G" as opposed to "G:\"), and therefore must be + /// reformatted as a valid path. + /// + private static bool ConvertDriveLetterToPathAndValidate(ref string path) + { + if (path.Length > 1) + return ValidatePath(path); + + DriveInfo driveInfo; + try + { + driveInfo = new DriveInfo(path); + } + catch (ArgumentException e) + { + _logger.LogWarning(e, $"Could not resolve drive letter '{path}' to a valid drive."); + return false; + } + + path = driveInfo.RootDirectory.Name; + return true; + } + + private static bool ValidatePath(string path) + { + if (Directory.Exists(path)) + return true; + _logger.LogWarning($"Invalid path: {path}"); + return false; + } + + private static async Task GetGoogleDriveIconFileAsync() + { + var programFilesEnvVar = Environment.GetEnvironmentVariable("ProgramFiles"); + + if (programFilesEnvVar is null) + return null; + + var iconPath = Path.Combine(programFilesEnvVar, "Google", "Drive File Stream", "drive_fs.ico"); + + var iconFileResult = await FilesystemTasks.Wrap(() => StorageFile.GetFileFromPathAsync(iconPath).AsTask()); + return iconFileResult ? iconFileResult.Result : null; + } + + private static bool AddMyDriveToPathAndValidate(ref string path) + { + // If `path` contains a shortcut named "My Drive", store its target in `shellFolderBaseFirst`. + // This happens when "My Drive syncing options" is set to "Mirror files". + // TODO: Avoid to use Vanara (#15000) + using var rootFolder = ShellFolderExtensions.GetShellItemFromPathOrPIDL(path) as ShellFolder; + var myDriveFolder = Environment.ExpandEnvironmentVariables(( + rootFolder?.FirstOrDefault(si => + si.Name?.Equals("My Drive") ?? false) as ShellLink)?.TargetPath + ?? string.Empty); + + Debug.WriteLine("SHELL FOLDER LOGGING"); + rootFolder?.ForEach(si => Debug.WriteLine(si.Name)); + + if (!string.IsNullOrEmpty(myDriveFolder)) + { + path = myDriveFolder; + return true; + } + + path = Path.Combine(path, "My Drive"); + return ValidatePath(path); } } } diff --git a/src/Files.App/Utils/Cloud/Detector/LucidLinkCloudDetector.cs b/src/Files.App/Utils/Cloud/Detector/LucidLinkCloudDetector.cs deleted file mode 100644 index 49a5f83a3224..000000000000 --- a/src/Files.App/Utils/Cloud/Detector/LucidLinkCloudDetector.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.App.Utils.Cloud; -using System.IO; -using System.Text.Json; -using Windows.Storage; - -namespace Files.App.Utils.Cloud -{ - /// - /// Provides an utility for LucidLink Cloud detection. - /// - public sealed class LucidLinkCloudDetector : AbstractCloudDetector - { - protected override async IAsyncEnumerable GetProviders() - { - string jsonPath = Path.Combine(Environment.GetEnvironmentVariable("UserProfile"), ".lucid", "app.json"); - - var configFile = await StorageFile.GetFileFromPathAsync(jsonPath); - using var jsonFile = JsonDocument.Parse(await FileIO.ReadTextAsync(configFile)); - var jsonElem = jsonFile.RootElement; - - if (jsonElem.TryGetProperty("filespaces", out JsonElement filespaces)) - { - foreach (JsonElement inner in filespaces.EnumerateArray()) - { - string syncFolder = inner.GetProperty("filespaceName").GetString(); - - string[] orgNameFilespaceName = syncFolder.Split("."); - string path = Path.Combine($@"{Environment.GetEnvironmentVariable("SystemDrive")}\Volumes", orgNameFilespaceName[1], orgNameFilespaceName[0]); - string filespaceName = orgNameFilespaceName[0]; - - string iconPath = Path.Combine(Environment.GetEnvironmentVariable("ProgramFiles"), "Lucid", "resources", "Logo.ico"); - StorageFile iconFile = await FilesystemTasks.Wrap(() => StorageFile.GetFileFromPathAsync(iconPath).AsTask()); - - yield return new CloudProvider(CloudProviders.LucidLink) - { - Name = $"Lucid Link ({filespaceName})", - SyncFolder = path, - IconData = iconFile is not null ? await iconFile.ToByteArrayAsync() : null, - }; - } - } - } - } -} \ No newline at end of file diff --git a/src/Files.App/Utils/Cloud/Detector/OXDriveCloudDetector.cs b/src/Files.App/Utils/Cloud/Detector/OXDriveCloudDetector.cs new file mode 100644 index 000000000000..bbc764853bc7 --- /dev/null +++ b/src/Files.App/Utils/Cloud/Detector/OXDriveCloudDetector.cs @@ -0,0 +1,77 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.Win32; +using System.IO; +using Windows.Storage; + +namespace Files.App.Utils.Cloud +{ + /// + /// Provides an utility for OX Drive Cloud detection. + /// + public sealed class OXDriveCloudDetector : AbstractCloudDetector + { + protected override async IAsyncEnumerable GetProviders() + { + var syncFolder = await GetOXDriveSyncFolder(); + if (!string.IsNullOrEmpty(syncFolder)) + { + var iconFile = GetOXDriveIconFile(); + yield return new CloudProvider(CloudProviders.OXDrive) + { + Name = "OX Drive", + SyncFolder = syncFolder, + IconData = iconFile?.IconData + }; + } + } + public static async Task GetOXDriveSyncFolder() + { + var jsonPath = Path.Combine(UserDataPaths.GetDefault().LocalAppData, "Open-Xchange", "OXDrive", "userConfig.json"); + if (!File.Exists(jsonPath)) + return null; + + var configFile = await StorageFile.GetFileFromPathAsync(jsonPath); + using var jsonDoc = JsonDocument.Parse(await FileIO.ReadTextAsync(configFile)); + var jsonElem = jsonDoc.RootElement; + + string? syncFolderPath = null; + + if (jsonElem.TryGetProperty("Accounts", out var accounts) && accounts.GetArrayLength() > 0) + { + var account = accounts[0]; + + if (account.TryGetProperty("MainFolderPath", out var folderPathElem)) + syncFolderPath = folderPathElem.GetString(); + } + + return syncFolderPath; + } + + private static IconFileInfo? GetOXDriveIconFile() + { + var installPath = Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Open-Xchange\OXDrive", "InstallDir", null) as string; + + // Fallback to default known path if not found in the registry. + if (string.IsNullOrEmpty(installPath)) + { + var pfX86 = Environment.GetEnvironmentVariable("ProgramFiles(x86)"); + if (string.IsNullOrEmpty(pfX86)) + return null; + + installPath = Path.Combine(pfX86, "Open-Xchange", "OXDrive"); + } + + var oxDriveFilePath = Path.Combine(installPath, "OXDrive.exe"); + if (!File.Exists(oxDriveFilePath)) + { + return null; + } + + // Extract the icon from the OXDrive executable (though it is executable, it contains icons) + var icons = Win32Helper.ExtractSelectedIconsFromDLL(oxDriveFilePath, new List { 0 }, 32); + return icons.FirstOrDefault(); + } + } +} diff --git a/src/Files.App/Utils/Cloud/Detector/SynologyDriveCloudDetector.cs b/src/Files.App/Utils/Cloud/Detector/SynologyDriveCloudDetector.cs index 2c98b557cf48..df75d90a789f 100644 --- a/src/Files.App/Utils/Cloud/Detector/SynologyDriveCloudDetector.cs +++ b/src/Files.App/Utils/Cloud/Detector/SynologyDriveCloudDetector.cs @@ -1,7 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.App.Utils.Cloud; using Microsoft.Data.Sqlite; using System.IO; using Windows.Storage; @@ -33,9 +32,9 @@ protected override async IAsyncEnumerable GetProviders() // Build the connection and SQL command SQLitePCL.Batteries_V2.Init(); - using var database = new SqliteConnection($"Data Source='{syncDbPath}'"); - using var cmdConnection = new SqliteCommand("SELECT * FROM connection_table", database); - using var cmdTable = new SqliteCommand("SELECT * FROM session_table", database); + await using var database = new SqliteConnection($"Data Source='{syncDbPath}'"); + await using var cmdConnection = new SqliteCommand("SELECT * FROM connection_table", database); + await using var cmdTable = new SqliteCommand("SELECT * FROM session_table", database); // Open the connection and execute the command database.Open(); diff --git a/src/Files.App/Utils/Cloud/ICloudDetector.cs b/src/Files.App/Utils/Cloud/ICloudDetector.cs index c5877a25e133..ecfea8bec318 100644 --- a/src/Files.App/Utils/Cloud/ICloudDetector.cs +++ b/src/Files.App/Utils/Cloud/ICloudDetector.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.Cloud { diff --git a/src/Files.App/Utils/Cloud/ICloudProvider.cs b/src/Files.App/Utils/Cloud/ICloudProvider.cs index 97aae4ab62a7..bfff8f15468d 100644 --- a/src/Files.App/Utils/Cloud/ICloudProvider.cs +++ b/src/Files.App/Utils/Cloud/ICloudProvider.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.Cloud { diff --git a/src/Files.App/Utils/CommandLine/CommandLineParser.cs b/src/Files.App/Utils/CommandLine/CommandLineParser.cs index 8dedecacf741..77164b60e829 100644 --- a/src/Files.App/Utils/CommandLine/CommandLineParser.cs +++ b/src/Files.App/Utils/CommandLine/CommandLineParser.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.IO; @@ -17,7 +17,7 @@ public sealed class CommandLineParser /// A collection of parsed command. public static ParsedCommands ParseUntrustedCommands(string cmdLineString) { - var parsedArgs = Parse(SplitArguments(cmdLineString, true)); + var parsedArgs = Parse(SplitArguments(cmdLineString.TrimEnd(), true)); return ParseSplitArguments(parsedArgs); } diff --git a/src/Files.App/Utils/CommandLine/ParsedCommand.cs b/src/Files.App/Utils/CommandLine/ParsedCommand.cs index 3f9e12f01647..1603afc4c469 100644 --- a/src/Files.App/Utils/CommandLine/ParsedCommand.cs +++ b/src/Files.App/Utils/CommandLine/ParsedCommand.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.CommandLine { diff --git a/src/Files.App/Utils/CommandLine/ParsedCommands.cs b/src/Files.App/Utils/CommandLine/ParsedCommands.cs index 1b8e90fe6a28..a8f51c588a02 100644 --- a/src/Files.App/Utils/CommandLine/ParsedCommands.cs +++ b/src/Files.App/Utils/CommandLine/ParsedCommands.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.CommandLine { /// /// Represents a collection of parsed command. /// - public sealed class ParsedCommands : List + public sealed partial class ParsedCommands : List { } } diff --git a/src/Files.App/Utils/FileTags/FileTagsDatabase.cs b/src/Files.App/Utils/FileTags/FileTagsDatabase.cs index 5d25dd625820..e7d120850dd7 100644 --- a/src/Files.App/Utils/FileTags/FileTagsDatabase.cs +++ b/src/Files.App/Utils/FileTags/FileTagsDatabase.cs @@ -1,19 +1,20 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.Win32; using System.Runtime.CompilerServices; +using System.Security; using Windows.ApplicationModel; -using JsonSerializer = System.Text.Json.JsonSerializer; using static Files.App.Helpers.RegistryHelpers; using static Files.App.Utils.FileTags.TaggedFileRegistry; +using JsonSerializer = System.Text.Json.JsonSerializer; namespace Files.App.Utils.FileTags { public sealed class FileTagsDatabase { private static string? _FileTagsKey; - private string? FileTagsKey => _FileTagsKey ??= SafetyExtensions.IgnoreExceptions(() => @$"Software\Files Community\{Package.Current.Id.FullName}\v1\FileTags"); + private string? FileTagsKey => _FileTagsKey ??= SafetyExtensions.IgnoreExceptions(() => @$"Software\Files Community\{Package.Current.Id.Name}\v1\FileTags"); public void SetTags(string filePath, ulong? frn, string[] tags) { @@ -158,7 +159,16 @@ public IEnumerable GetAll() var list = new List(); if (FileTagsKey is not null) - IterateKeys(list, FileTagsKey, 0); + { + try + { + IterateKeys(list, FileTagsKey, 0); + } + catch (SecurityException) + { + // Handle edge case where IterateKeys results in SecurityException + } + } return list; } @@ -169,7 +179,16 @@ public IEnumerable GetAllUnderPath(string folderPath) var list = new List(); if (FileTagsKey is not null) - IterateKeys(list, CombineKeys(FileTagsKey, folderPath), 0); + { + try + { + IterateKeys(list, CombineKeys(FileTagsKey, folderPath), 0); + } + catch (SecurityException) + { + // Handle edge case where IterateKeys results in SecurityException + } + } return list; } @@ -212,9 +231,7 @@ private void IterateKeys(List list, string path, int depth) { using var key = Registry.CurrentUser.OpenSubKey(path); if (key is null) - { return; - } if (key.ValueCount > 0) { @@ -225,11 +242,9 @@ private void IterateKeys(List list, string path, int depth) foreach (var subKey in key.GetSubKeyNames()) { + // Skip FRN key if (depth == 0 && subKey == "FRN") - { - // Skip FRN key continue; - } IterateKeys(list, CombineKeys(path, subKey), depth + 1); } diff --git a/src/Files.App/Utils/FileTags/FileTagsHelper.cs b/src/Files.App/Utils/FileTags/FileTagsHelper.cs index fabae98d11f1..96e9692cba29 100644 --- a/src/Files.App/Utils/FileTags/FileTagsHelper.cs +++ b/src/Files.App/Utils/FileTags/FileTagsHelper.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Controls; using Windows.Foundation.Metadata; @@ -42,8 +42,8 @@ public static async void WriteFileTag(string filePath, string[] tag) { ContentDialog dialog = new() { - Title = "ErrorApplyingTagTitle".GetLocalizedResource(), - Content = "ErrorApplyingTagContent".GetLocalizedResource(), + Title = Strings.ErrorApplyingTagTitle.GetLocalizedResource(), + Content = Strings.ErrorApplyingTagContent.GetLocalizedResource(), PrimaryButtonText = "Ok".GetLocalizedResource() }; diff --git a/src/Files.App/Utils/FileTags/FileTagsManager.cs b/src/Files.App/Utils/FileTags/FileTagsManager.cs index f3c6cc502818..284dedd09e1d 100644 --- a/src/Files.App/Utils/FileTags/FileTagsManager.cs +++ b/src/Files.App/Utils/FileTags/FileTagsManager.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.Extensions.Logging; using System.Collections.Specialized; @@ -48,14 +48,14 @@ public Task UpdateFileTagsAsync() var tagItem = new FileTagItem { Text = tag.Name, - Path = $"tag:{tag.Name}", + Path = FolderSearch.FormatTagQuery(tag.Name), FileTag = tag, MenuOptions = new ContextMenuOptions { IsLocationItem = true }, }; lock (fileTags) { - if (fileTags.Any(x => x.Path == $"tag:{tag.Name}")) + if (fileTags.Any(x => x.Path == FolderSearch.FormatTagQuery(tag.Name))) { continue; } diff --git a/src/Files.App/Utils/FileTags/TaggedFile.cs b/src/Files.App/Utils/FileTags/TaggedFile.cs index f10fcef3e91b..6e062192caf7 100644 --- a/src/Files.App/Utils/FileTags/TaggedFile.cs +++ b/src/Files.App/Utils/FileTags/TaggedFile.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.FileTags { diff --git a/src/Files.App/Utils/Git/GitHelpers.cs b/src/Files.App/Utils/Git/GitHelpers.cs index 4ed16a5405ac..42660c12b7e5 100644 --- a/src/Files.App/Utils/Git/GitHelpers.cs +++ b/src/Files.App/Utils/Git/GitHelpers.cs @@ -1,20 +1,20 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.Dialogs; using LibGit2Sharp; -using Sentry; using Microsoft.Extensions.Logging; using System.Net.Http; using System.Net.Http.Json; -using System.Net.Sockets; using System.Text; -using System.Text.Json; +using System.Text.RegularExpressions; namespace Files.App.Utils.Git { - internal static class GitHelpers + internal static partial class GitHelpers { + private static readonly StatusCenterViewModel StatusCenterViewModel = Ioc.Default.GetRequiredService(); + private const string GIT_RESOURCE_NAME = "Files:https://github.com"; private const string GIT_RESOURCE_USERNAME = "Personal Access Token"; @@ -36,9 +36,9 @@ internal static class GitHelpers private static readonly PullOptions _pullOptions = new(); - private static readonly string _clientId = AppLifecycleHelper.AppEnvironment is AppEnvironment.Store or AppEnvironment.Stable or AppEnvironment.Preview - ? CLIENT_ID_SECRET - : string.Empty; + private static readonly string _clientId = AppLifecycleHelper.AppEnvironment is AppEnvironment.Dev + ? string.Empty + : CLIENT_ID_SECRET; private static readonly SemaphoreSlim GitOperationSemaphore = new SemaphoreSlim(1, 1); @@ -78,7 +78,7 @@ private set try { - if (Repository.IsValid(path)) + if (IsRepoValid(path)) return path; else { @@ -99,7 +99,7 @@ private set public static string GetOriginRepositoryName(string? path) { - if (string.IsNullOrWhiteSpace(path) || !Repository.IsValid(path)) + if (string.IsNullOrWhiteSpace(path) || !IsRepoValid(path)) return string.Empty; using var repository = new Repository(path); @@ -114,7 +114,7 @@ public static string GetOriginRepositoryName(string? path) public static async Task GetBranchesNames(string? path) { - if (string.IsNullOrWhiteSpace(path) || !Repository.IsValid(path)) + if (string.IsNullOrWhiteSpace(path) || !IsRepoValid(path)) return []; var (result, returnValue) = await DoGitOperationAsync<(GitOperationResult, BranchItem[])>(() => @@ -126,9 +126,9 @@ public static async Task GetBranchesNames(string? path) using var repository = new Repository(path); branches = GetValidBranches(repository.Branches) - .Where(b => !b.IsRemote || b.RemoteName == "origin") .OrderByDescending(b => b.Tip?.Committer.When) - .Take(MAX_NUMBER_OF_BRANCHES) + .GroupBy(b => b.IsRemote) + .SelectMany(g => g.Take(MAX_NUMBER_OF_BRANCHES)) .OrderByDescending(b => b.IsCurrentRepositoryHead) .Select(b => new BranchItem(b.FriendlyName, b.IsCurrentRepositoryHead, b.IsRemote, TryGetTrackingDetails(b)?.AheadBy ?? 0, TryGetTrackingDetails(b)?.BehindBy ?? 0)) .ToArray(); @@ -146,7 +146,7 @@ public static async Task GetBranchesNames(string? path) public static async Task GetRepositoryHead(string? path) { - if (string.IsNullOrWhiteSpace(path) || !Repository.IsValid(path)) + if (string.IsNullOrWhiteSpace(path) || !IsRepoValid(path)) return null; var (_, returnValue) = await DoGitOperationAsync<(GitOperationResult, BranchItem?)>(() => @@ -178,9 +178,10 @@ public static async Task GetBranchesNames(string? path) public static async Task Checkout(string? repositoryPath, string? branch) { - SentrySdk.Metrics.Increment("Triggered git checkout"); + // Re-enable when Metris feature is available again + // SentrySdk.Metrics.Increment("Triggered git checkout"); - if (string.IsNullOrWhiteSpace(repositoryPath) || !Repository.IsValid(repositoryPath)) + if (string.IsNullOrWhiteSpace(repositoryPath) || !IsRepoValid(repositoryPath)) return false; using var repository = new Repository(repositoryPath); @@ -193,7 +194,24 @@ public static async Task Checkout(string? repositoryPath, string? branch) IsExecutingGitAction = true; - if (repository.RetrieveStatus().IsDirty) + if (repository.Index.Conflicts.Any()) + { + var dialog = DynamicDialogFactory.GetFor_GitMergeConflicts(checkoutBranch.FriendlyName, repository.Head.FriendlyName); + await dialog.ShowAsync(); + + var resolveConflictOption = (GitCheckoutOptions)dialog.ViewModel.AdditionalData; + + switch (resolveConflictOption) + { + case GitCheckoutOptions.None: + IsExecutingGitAction = false; + return false; + case GitCheckoutOptions.AbortMerge: + repository.Reset(ResetMode.Hard); + break; + } + } + else if (repository.RetrieveStatus().IsDirty) { var dialog = DynamicDialogFactory.GetFor_GitCheckoutConflicts(checkoutBranch.FriendlyName, repository.Head.FriendlyName); await dialog.ShowAsync(); @@ -203,6 +221,7 @@ public static async Task Checkout(string? repositoryPath, string? branch) switch (resolveConflictOption) { case GitCheckoutOptions.None: + IsExecutingGitAction = false; return false; case GitCheckoutOptions.DiscardChanges: options.CheckoutModifiers = CheckoutModifiers.Force; @@ -211,7 +230,10 @@ public static async Task Checkout(string? repositoryPath, string? branch) case GitCheckoutOptions.StashChanges: var signature = repository.Config.BuildSignature(DateTimeOffset.Now); if (signature is null) + { + IsExecutingGitAction = false; return false; + } repository.Stashes.Add(signature); @@ -250,7 +272,8 @@ public static async Task Checkout(string? repositoryPath, string? branch) public static async Task CreateNewBranchAsync(string repositoryPath, string activeBranch) { - SentrySdk.Metrics.Increment("Triggered create git branch"); + // Re-enable when Metris feature is available again + // SentrySdk.Metrics.Increment("Triggered create git branch"); var viewModel = new AddBranchDialogViewModel(repositoryPath, activeBranch); var loadBranchesTask = viewModel.LoadBranches(); @@ -280,13 +303,14 @@ await Checkout(repositoryPath, viewModel.BasedOn)) public static async Task DeleteBranchAsync(string? repositoryPath, string? activeBranch, string? branchToDelete) { - SentrySdk.Metrics.Increment("Triggered delete git branch"); + // Re-enable when Metris feature is available again + // SentrySdk.Metrics.Increment("Triggered delete git branch"); if (string.IsNullOrWhiteSpace(repositoryPath) || string.IsNullOrWhiteSpace(activeBranch) || string.IsNullOrWhiteSpace(branchToDelete) || activeBranch.Equals(branchToDelete, StringComparison.OrdinalIgnoreCase) || - !Repository.IsValid(repositoryPath)) + !IsRepoValid(repositoryPath)) { return; } @@ -319,7 +343,7 @@ await DoGitOperationAsync(() => public static bool ValidateBranchNameForRepository(string branchName, string repositoryPath) { - if (string.IsNullOrEmpty(branchName) || !Repository.IsValid(repositoryPath)) + if (string.IsNullOrEmpty(branchName) || !IsRepoValid(repositoryPath)) return false; var nameValidator = RegexHelpers.GitBranchName(); @@ -331,7 +355,7 @@ public static bool ValidateBranchNameForRepository(string branchName, string rep branch.FriendlyName.Equals(branchName, StringComparison.OrdinalIgnoreCase)); } - public static async void FetchOrigin(string? repositoryPath) + public static async void FetchOrigin(string? repositoryPath, CancellationToken cancellationToken = default) { if (string.IsNullOrWhiteSpace(repositoryPath)) return; @@ -357,11 +381,15 @@ public static async void FetchOrigin(string? repositoryPath) await DoGitOperationAsync(() => { + cancellationToken.ThrowIfCancellationRequested(); + var result = GitOperationResult.Success; try { foreach (var remote in repository.Network.Remotes) { + cancellationToken.ThrowIfCancellationRequested(); + LibGit2Sharp.Commands.Fetch( repository, remote.Name, @@ -369,6 +397,8 @@ await DoGitOperationAsync(() => _fetchOptions, "git fetch updated a ref"); } + + cancellationToken.ThrowIfCancellationRequested(); } catch (Exception ex) { @@ -382,6 +412,10 @@ await DoGitOperationAsync(() => MainWindow.Instance.DispatcherQueue.TryEnqueue(() => { + if (cancellationToken.IsCancellationRequested) + // Do nothing because the operation was cancelled and another fetch may be in progress + return; + IsExecutingGitAction = false; GitFetchCompleted?.Invoke(null, EventArgs.Empty); }); @@ -441,9 +475,9 @@ public static async Task PullOriginAsync(string? repositoryPath) { var viewModel = new DynamicDialogViewModel() { - TitleText = "GitError".GetLocalizedResource(), - SubtitleText = "PullTimeoutError".GetLocalizedResource(), - CloseButtonText = "Close".GetLocalizedResource(), + TitleText = Strings.GitError.GetLocalizedResource(), + SubtitleText = Strings.PullTimeoutError.GetLocalizedResource(), + CloseButtonText = Strings.Close.GetLocalizedResource(), DynamicButtons = DynamicDialogButtons.Cancel }; var dialog = new DynamicDialog(viewModel); @@ -569,7 +603,7 @@ public static async Task RequireGitAuthenticationAsync() var expiresIn = codeJsonContent.RootElement.GetProperty("expires_in").GetInt32(); var loginCTS = new CancellationTokenSource(); - var viewModel = new GitHubLoginDialogViewModel(userCode, "ConnectGitHubDescription".GetLocalizedResource(), loginCTS); + var viewModel = new GitHubLoginDialogViewModel(userCode, Strings.ConnectGitHubDescription.GetLocalizedResource(), loginCTS); var dialog = _dialogService.GetDialog(viewModel); var loginDialogTask = dialog.TryShowAsync(); @@ -620,7 +654,7 @@ public static async Task RequireGitAuthenticationAsync() GIT_RESOURCE_USERNAME, token); - viewModel.Subtitle = "AuthorizationSucceded".GetLocalizedResource(); + viewModel.Subtitle = Strings.AuthorizationSucceded.GetLocalizedResource(); viewModel.LoginConfirmed = true; } catch (Exception ex) @@ -646,7 +680,7 @@ public static bool IsRepositoryEx(string path, out string repoRootPath) if (string.IsNullOrEmpty(repositoryRootPath)) return false; - if (Repository.IsValid(repositoryRootPath)) + if (IsRepoValid(repositoryRootPath)) { repoRootPath = repositoryRootPath; return true; @@ -686,10 +720,10 @@ public static GitItemModel GetGitInformationForItem(Repository repository, strin { changeKindHumanized = changeKind switch { - ChangeKind.Added => "Added".GetLocalizedResource(), - ChangeKind.Deleted => "Deleted".GetLocalizedResource(), - ChangeKind.Modified => "Modified".GetLocalizedResource(), - ChangeKind.Untracked => "Untracked".GetLocalizedResource(), + ChangeKind.Added => Strings.Added.GetLocalizedResource(), + ChangeKind.Deleted => Strings.Deleted.GetLocalizedResource(), + ChangeKind.Modified => Strings.Modified.GetLocalizedResource(), + ChangeKind.Untracked => Strings.Untracked.GetLocalizedResource(), _ => null, }; } @@ -734,6 +768,11 @@ public static async Task InitializeRepositoryAsync(string? path) } } + private static bool IsRepoValid(string path) + { + return SafetyExtensions.IgnoreExceptions(() => Repository.IsValid(path)); + } + private static IEnumerable GetValidBranches(BranchCollection branches) { foreach (var branch in branches) @@ -839,5 +878,94 @@ private static bool IsAuthorizationException(Exception ex) GitOperationSemaphore.Release(); } } + + /// + /// Gets repository information from a GitHub URL. + /// + /// + /// + public static (string RepoUrl, string RepoName) GetRepoInfo(string url) + { + var match = GitHubRepositoryRegex().Match(url); + + if (!match.Success) + return (string.Empty, string.Empty); + + string platform = match.Groups["domain"].Value; + string userOrOrg = match.Groups["user"].Value; + string repoName = match.Groups["repo"].Value; + + string repoUrl = $"https://{platform}.com/{userOrOrg}/{repoName}"; + return (repoUrl, repoName); + } + + /// + /// Checks if the provided URL is a valid GitHub URL. + /// + /// The URL to validate. + /// True if the URL is a valid GitHub URL; otherwise, false. + public static bool IsValidRepoUrl(string url) + { + return GitHubRepositoryRegex().IsMatch(url); + } + + public static async Task CloneRepoAsync(string repoUrl, string repoName, string targetDirectory) + { + var banner = StatusCenterHelper.AddCard_GitClone(repoName.CreateEnumerable(), targetDirectory.CreateEnumerable(), ReturnResult.InProgress); + var fsProgress = new StatusCenterItemProgressModel(banner.ProgressEventSource, enumerationCompleted: true, FileSystemStatusCode.InProgress); + var errorMessage = string.Empty; + + bool isSuccess = await Task.Run(() => + { + try + { + var cloneOptions = new CloneOptions + { + FetchOptions = + { + OnTransferProgress = progress => + { + banner.CancellationToken.ThrowIfCancellationRequested(); + fsProgress.ItemsCount = progress.TotalObjects; + fsProgress.SetProcessedSize(progress.ReceivedBytes); + fsProgress.AddProcessedItemsCount(1); + fsProgress.Report((int)((progress.ReceivedObjects / (double)progress.TotalObjects) * 100)); + return true; + }, + OnProgress = _ => !banner.CancellationToken.IsCancellationRequested + }, + OnCheckoutProgress = (path, completed, total) => + banner.CancellationToken.ThrowIfCancellationRequested() + }; + + Repository.Clone(repoUrl, targetDirectory, cloneOptions); + return true; + } + catch (Exception ex) + { + errorMessage = ex.Message; + return false; + } + }, banner.CancellationToken); + + if (!string.IsNullOrEmpty(errorMessage)) + { + UIHelpers.CloseAllDialogs(); + await Task.Delay(500); + await DynamicDialogFactory.ShowFor_CannotCloneRepo(errorMessage); + } + + StatusCenterViewModel.RemoveItem(banner); + + StatusCenterHelper.AddCard_GitClone( + repoName.CreateEnumerable(), + targetDirectory.CreateEnumerable(), + isSuccess ? ReturnResult.Success : + banner.CancellationToken.IsCancellationRequested ? ReturnResult.Cancelled : + ReturnResult.Failed); + } + + [GeneratedRegex(@"^(?:https?:\/\/)?(?:www\.)?(?github|gitlab)\.com\/(?[^\/]+)\/(?[^\/]+?)(?=\.git|\/|$)(?:\.git)?(?:\/)?", RegexOptions.IgnoreCase)] + private static partial Regex GitHubRepositoryRegex(); } -} +} \ No newline at end of file diff --git a/src/Files.App/Utils/Git/IVersionControl.cs b/src/Files.App/Utils/Git/IVersionControl.cs new file mode 100644 index 000000000000..b1e21f0403a7 --- /dev/null +++ b/src/Files.App/Utils/Git/IVersionControl.cs @@ -0,0 +1,213 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using LibGit2Sharp; +using System.ComponentModel; + +namespace Files.App.Utils.Git +{ + /// + /// Defines a version control abstraction + /// + /// + /// This interface is intended to decouple the app from a specific backend implementation (e.g. a library such as LibGit2Sharp, or a command-line implementation backed by the git.exe executable). + /// + internal interface IVersionControl + { + /// + /// Attempts to locate the root of a version control repository (by walking up the directory hierarchy). + /// + /// The starting path to search from. + /// The filesystem root boundary (for example, a drive root) at which the search must stop. + /// + /// The repository root path if one is found; otherwise, . + /// + /// + /// This method is used for determining whether a directory is within a repository and, if so, + /// which directory should be treated as the repository root. + /// + string? GetGitRepositoryPath(string? path, string root); + + /// + /// Gets the repository name. + /// + /// A path to the repository working directory. + /// + /// The remote repository name (without the .git suffix), or an empty string if it cannot be determined. + /// + string GetOriginRepositoryName(string? path); + + /// + /// Retrieves branch information (names and tracking status) for the given repository. + /// + /// A path to the repository working directory. + /// + /// A task producing an array of branches; returns an empty array when the repository is invalid or unavailable. + /// + Task GetBranchesNames(string? path); + + /// + /// Gets the current repository HEAD reference. + /// + /// A path to the repository working directory. + /// + /// A task producing a representing the HEAD, or if not available. + /// + Task GetRepositoryHead(string? path); + + /// + /// Checks out the specified branch. + /// + /// A path to the repository working directory. + /// The branch name to check out. + /// + /// A task producing if checkout succeeded; otherwise . + /// + /// + /// Implementations should prompt the user when there are conflicts or uncommitted changes. + /// + Task Checkout(string? repositoryPath, string? branch); + + /// + /// Creates a new local branch and optionally checks it out. + /// + /// A path to the repository working directory. + /// The currently active branch name. + /// A task representing the asynchronous operation. + /// + /// Implementations should involve UI for branch name selection and validation. + /// + Task CreateNewBranchAsync(string repositoryPath, string activeBranch); + + /// + /// Deletes a local branch. + /// + /// A path to the repository working directory. + /// The current branch; the implementation should not delete the active branch. + /// The branch name to delete. + /// A task representing the asynchronous operation. + Task DeleteBranchAsync(string? repositoryPath, string? activeBranch, string? branchToDelete); + + /// + /// Validates whether a branch name is valid and does not already exist within the repository. + /// + /// The proposed branch name. + /// A path to the repository working directory. + /// + /// if the name is acceptable for the repository; otherwise . + /// + bool ValidateBranchNameForRepository(string branchName, string repositoryPath); + + /// + /// Fetches updates from remotes. + /// + /// A path to the repository working directory. + /// A token used to cancel the operation. + /// + /// Implementations should raise when the fetch completes successfully. + /// + void FetchOrigin(string? repositoryPath, CancellationToken cancellationToken = default); + + /// + /// Pulls from the default remote. + /// + /// A path to the repository working directory. + /// A task representing the asynchronous operation. + Task PullOriginAsync(string? repositoryPath); + + /// + /// Pushes a branch to the default remote. + /// + /// A path to the repository working directory. + /// The branch name to push. + /// A task representing the asynchronous operation. + Task PushToOriginAsync(string? repositoryPath, string? branchName); + + /// + /// Initiates an authentication flow suitable for the configured remote. + /// + /// A task representing the asynchronous operation. + Task RequireGitAuthenticationAsync(); + + /// + /// Determines whether the given path is within a repository and returns the resolved repository root. + /// + /// The path to test. + /// When this method returns , contains the repository root path. + /// + /// if a repository was found; otherwise, . + /// + bool IsRepositoryEx(string path, out string repoRootPath); + + /// + /// Gets version control information for a filesystem item. + /// + /// The opened repository instance used to retrieve information. + /// The full path to the filesystem item. + /// Whether to compute status (working tree/index changes). + /// Whether to compute the last commit affecting the item. + /// A describing the item. + GitItemModel GetGitInformationForItem(Repository repository, string path, bool getStatus = true, bool getCommit = true); + + /// + /// Removes any stored credentials associated with the version control provider. + /// + void RemoveSavedCredentials(); + + /// + /// Gets any stored credentials associated with the version control provider. + /// + /// The stored credential (typically a token), or an empty string if none exists. + string GetSavedCredentials(); + + /// + /// Initialises a new repository at the specified location. + /// + /// The target directory path. + /// A task representing the asynchronous operation. + Task InitializeRepositoryAsync(string? path); + + /// + /// Parses a repository URL and returns normalised information. + /// + /// The input URL. + /// + /// A tuple containing the normalised repository URL and the repository name. + /// + /// + /// The set of recognised URLs is defined by the implementation (github, gitlab, etc.) + /// + (string RepoUrl, string RepoName) GetRepoInfo(string url); + + /// + /// Checks whether the provided URL is recognised as a repository URL. + /// + /// The URL to validate. + /// if the URL is valid; otherwise, . + bool IsValidRepoUrl(string url); + + /// + /// Clones a repository into the specified target directory. + /// + /// The repository URL to clone. + /// A display-friendly repository name. + /// The directory where the repository should be cloned. + /// A task representing the asynchronous operation. + Task CloneRepoAsync(string repoUrl, string repoName, string targetDirectory); + + /// + /// Gets a value indicating whether a version control operation is currently running. + /// + /// + /// while an operation is in progress; otherwise . + /// + bool IsExecutingGitAction { get; } + + event PropertyChangedEventHandler? IsExecutingGitActionChanged; + + /// + /// Raised when a fetch operation completes. + /// + event EventHandler? GitFetchCompleted; + } +} diff --git a/src/Files.App/Utils/Global/QuickAccessManager.cs b/src/Files.App/Utils/Global/QuickAccessManager.cs index deeabca3424f..6b2b1de36bd6 100644 --- a/src/Files.App/Utils/Global/QuickAccessManager.cs +++ b/src/Files.App/Utils/Global/QuickAccessManager.cs @@ -1,7 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using CommunityToolkit.WinUI.Helpers; using System.IO; namespace Files.App.Utils @@ -26,14 +25,27 @@ public QuickAccessManager() public void Initialize() { - PinnedItemsWatcher = new() + var automaticDestinationsPath = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Microsoft", "Windows", "Recent", "AutomaticDestinations"); + + // Only initialize FileSystemWatcher if the directory exists + // This handles cases where AppData is redirected to network locations that don't contain Windows system directories + if (Directory.Exists(automaticDestinationsPath)) { - Path = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Microsoft", "Windows", "Recent", "AutomaticDestinations"), - Filter = "f01b4d95cf55d32a.automaticDestinations-ms", - NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName - }; - - PinnedItemsWatcher.Changed += PinnedItemsWatcher_Changed; + PinnedItemsWatcher = new() + { + Path = automaticDestinationsPath, + Filter = "f01b4d95cf55d32a.automaticDestinations-ms", + NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName + }; + + PinnedItemsWatcher.Changed += PinnedItemsWatcher_Changed; + } + else + { + // If the directory doesn't exist (e.g., redirected AppData), skip FileSystemWatcher initialization + // The app will still function, but won't receive automatic updates when pinned items change externally + PinnedItemsWatcher = null; + } } private void PinnedItemsWatcher_Changed(object sender, FileSystemEventArgs e) @@ -42,11 +54,10 @@ private void PinnedItemsWatcher_Changed(object sender, FileSystemEventArgs e) public async Task InitializeAsync() { PinnedItemsModified += Model.LoadAsync; + await Model.LoadAsync(); - if (!Model.PinnedFolders.Contains(Constants.UserEnvironmentPaths.RecycleBinPath) && SystemInformation.Instance.IsFirstRun) + if (!Model.PinnedFolders.Contains(Constants.UserEnvironmentPaths.RecycleBinPath) && AppLifecycleHelper.IsFirstRun) await QuickAccessService.PinToSidebarAsync(Constants.UserEnvironmentPaths.RecycleBinPath); - - await Model.LoadAsync(); } } } diff --git a/src/Files.App/Utils/Global/WSLDistroManager.cs b/src/Files.App/Utils/Global/WSLDistroManager.cs index 63adc28a49c6..6c8d28332476 100644 --- a/src/Files.App/Utils/Global/WSLDistroManager.cs +++ b/src/Files.App/Utils/Global/WSLDistroManager.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; @@ -29,10 +29,10 @@ public static async Task UpdateDrivesAsync() try { // Check if WSL is installed - const string WslRegistryPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss"; + const string WslRegistryPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss\MSI"; using (var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(WslRegistryPath)) { - if (key != null && key.GetSubKeyNames().Length == 0) + if (key is null || key.GetValue("InstallLocation") is null) return; } diff --git a/src/Files.App/Utils/Global/WallpaperHelpers.cs b/src/Files.App/Utils/Global/WallpaperHelpers.cs deleted file mode 100644 index 73054947983f..000000000000 --- a/src/Files.App/Utils/Global/WallpaperHelpers.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Microsoft.UI.Xaml.Controls; -using Vanara.PInvoke; -using Windows.Foundation.Metadata; -using Windows.Storage; -using Windows.System.UserProfile; - -namespace Files.App.Utils -{ - public static class WallpaperHelpers - { - public static async Task SetAsBackgroundAsync(WallpaperType type, string filePath) - { - try - { - if (type == WallpaperType.Desktop) - { - // Set the desktop background - var wallpaper = (Shell32.IDesktopWallpaper)new Shell32.DesktopWallpaper(); - var monitorCount = wallpaper.GetMonitorDevicePathCount(); - - for (uint i = 0; i < monitorCount; i++) - { - wallpaper.GetMonitorDevicePathAt(i, out var monitorId); - wallpaper.SetWallpaper(monitorId, filePath); - } - } - else if (type == WallpaperType.LockScreen) - { - // Set the lockscreen background - IStorageFile sourceFile = await StorageFile.GetFileFromPathAsync(filePath); - await LockScreen.SetImageFileAsync(sourceFile); - } - } - catch (Exception ex) - { - ShowErrorPrompt(ex.Message); - } - } - - public static void SetSlideshow(string[] filePaths) - { - if (filePaths is null || !filePaths.Any()) - return; - - try - { - var idList = filePaths.Select(Shell32.IntILCreateFromPath).ToArray(); - Shell32.SHCreateShellItemArrayFromIDLists((uint)idList.Length, [.. idList], out var shellItemArray); - - // Set SlideShow - var wallpaper = (Shell32.IDesktopWallpaper)new Shell32.DesktopWallpaper(); - wallpaper.SetSlideshow(shellItemArray); - - // Set wallpaper to fill desktop. - wallpaper.SetPosition(Shell32.DESKTOP_WALLPAPER_POSITION.DWPOS_FILL); - } - catch (Exception ex) - { - ShowErrorPrompt(ex.Message); - } - } - - private static async void ShowErrorPrompt(string exception) - { - var errorDialog = new ContentDialog() - { - Title = "FailedToSetBackground".GetLocalizedResource(), - Content = exception, - PrimaryButtonText = "OK".GetLocalizedResource(), - }; - - if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) - errorDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; - - await errorDialog.TryShowAsync(); - } - } -} diff --git a/src/Files.App/Utils/Global/WindowsStorageDeviceWatcher.cs b/src/Files.App/Utils/Global/WindowsStorageDeviceWatcher.cs index b12fa0c474fa..dc813e6c2b31 100644 --- a/src/Files.App/Utils/Global/WindowsStorageDeviceWatcher.cs +++ b/src/Files.App/Utils/Global/WindowsStorageDeviceWatcher.cs @@ -1,11 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.App.Data.Items; -using Files.App.Helpers; -using Files.Core.Storage.Storables; using Microsoft.Extensions.Logging; -using System; using System.IO; using System.Runtime.InteropServices; using Windows.Devices.Enumeration; @@ -16,7 +12,7 @@ namespace Files.App.Utils { public sealed class WindowsStorageDeviceWatcher : IStorageDeviceWatcher { - public event EventHandler DeviceAdded; + public event EventHandler DeviceAdded; public event EventHandler DeviceRemoved; public event EventHandler EnumerationCompleted; public event EventHandler DeviceModified; @@ -56,6 +52,9 @@ private void Win32_OnDeviceRemoved(object? sender, DeviceEventArgs e) private async void Win32_OnDeviceAdded(object? sender, DeviceEventArgs e) { var driveAdded = new DriveInfo(e.DeviceId); + if (!driveAdded.IsReady) + return; + var rootAdded = await FilesystemTasks.Wrap(() => StorageFolder.GetFolderFromPathAsync(e.DeviceId).AsTask()); if (!rootAdded) { @@ -63,7 +62,7 @@ private async void Win32_OnDeviceAdded(object? sender, DeviceEventArgs e) + " failed at the StorageFolder initialization step. This device will be ignored."); return; } - + var type = DriveHelpers.GetDriveType(driveAdded); var label = DriveHelpers.GetExtendedDriveLabel(driveAdded); DriveItem driveItem = await DriveItem.CreateFromPropertiesAsync(rootAdded, e.DeviceId, label, type); @@ -96,12 +95,15 @@ private async void Watcher_Added(DeviceWatcher sender, DeviceInformation args) return; } - Data.Items.DriveType type; + Data.Items.DriveType type; string label; try { // Check if this drive is associated with a drive letter var driveAdded = new DriveInfo(root.Path); + if (!driveAdded.IsReady) + return; + type = DriveHelpers.GetDriveType(driveAdded); label = DriveHelpers.GetExtendedDriveLabel(driveAdded); } diff --git a/src/Files.App/Utils/Library/LibraryManager.cs b/src/Files.App/Utils/Library/LibraryManager.cs index 772c6479d1c3..385b1bc14300 100644 --- a/src/Files.App/Utils/Library/LibraryManager.cs +++ b/src/Files.App/Utils/Library/LibraryManager.cs @@ -1,8 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.Dialogs; -using Files.App.ViewModels.Dialogs; using Microsoft.Extensions.Logging; using Microsoft.UI.Xaml.Controls; using System.Collections.Specialized; @@ -14,7 +13,7 @@ namespace Files.App.Utils.Library { - public sealed class LibraryManager : IDisposable + public sealed partial class LibraryManager : IDisposable { public EventHandler? DataChanged; @@ -68,7 +67,7 @@ private void InitializeWatcher() /// List of library items public static async Task> ListUserLibraries() { - var libraries = await Win32Helper.StartSTATask(() => + var libraries = await STATask.Run(() => { try { @@ -91,7 +90,7 @@ public static async Task> ListUserLibraries() } return []; - }); + }, App.Logger); return libraries.Select(lib => new LibraryLocationItem(lib)).ToList(); } @@ -135,7 +134,7 @@ public async Task CreateNewLibrary(string name) if (string.IsNullOrWhiteSpace(name) || !CanCreateLibrary(name).result) return false; - var newLib = new LibraryLocationItem(await Win32Helper.StartSTATask(() => + var newLib = new LibraryLocationItem(await STATask.Run(() => { try { @@ -151,7 +150,7 @@ public async Task CreateNewLibrary(string name) } return Task.FromResult(null); - })); + }, App.Logger)); if (newLib is not null) { @@ -179,7 +178,7 @@ public async Task UpdateLibrary(string libraryPath, string // Nothing to update return null; - var item = await Win32Helper.StartSTATask(() => + var item = await STATask.Run(() => { try { @@ -232,7 +231,7 @@ public async Task UpdateLibrary(string libraryPath, string } return Task.FromResult(null); - }); + }, App.Logger); var newLib = item is not null ? new LibraryLocationItem(item) : null; if (newLib is not null) @@ -255,20 +254,20 @@ public async Task UpdateLibrary(string libraryPath, string { if (string.IsNullOrWhiteSpace(name)) { - return (false, "ErrorInputEmpty".GetLocalizedResource()); + return (false, Strings.ErrorInputEmpty.GetLocalizedResource()); } if (FilesystemHelpers.ContainsRestrictedCharacters(name)) { - return (false, "ErrorNameInputRestrictedCharacters".GetLocalizedResource()); + return (false, Strings.ErrorNameInputRestrictedCharacters.GetLocalizedResource()); } if (FilesystemHelpers.ContainsRestrictedFileName(name)) { - return (false, "ErrorNameInputRestricted".GetLocalizedResource()); + return (false, Strings.ErrorNameInputRestricted.GetLocalizedResource()); } if (Libraries.Any((item) => string.Equals(name, item.Text, StringComparison.OrdinalIgnoreCase) || string.Equals(name, Path.GetFileNameWithoutExtension(item.Path), StringComparison.OrdinalIgnoreCase))) { - return (false, "CreateLibraryErrorAlreadyExists".GetLocalizedResource()); + return (false, Strings.CreateLibraryErrorAlreadyExists.GetLocalizedResource()); } return (true, string.Empty); } @@ -277,10 +276,10 @@ public static async Task ShowRestoreDefaultLibrariesDialogAsync() { var dialog = new DynamicDialog(new DynamicDialogViewModel { - TitleText = "DialogRestoreLibrariesTitleText".GetLocalizedResource(), - SubtitleText = "DialogRestoreLibrariesSubtitleText".GetLocalizedResource(), - PrimaryButtonText = "Restore".GetLocalizedResource(), - CloseButtonText = "Cancel".GetLocalizedResource(), + TitleText = Strings.DialogRestoreLibrariesTitleText.GetLocalizedResource(), + SubtitleText = Strings.DialogRestoreLibrariesSubtitleText.GetLocalizedResource(), + PrimaryButtonText = Strings.Restore.GetLocalizedResource(), + CloseButtonText = Strings.Cancel.GetLocalizedResource(), PrimaryButtonAction = async (vm, e) => { await ContextMenu.InvokeVerb("restorelibraries", ShellLibraryItem.LibrariesPath); @@ -303,7 +302,7 @@ public static async Task ShowCreateNewLibraryDialogAsync() { var inputText = new TextBox { - PlaceholderText = "FolderWidgetCreateNewLibraryInputPlaceholderText".GetLocalizedResource() + PlaceholderText = Strings.FolderWidgetCreateNewLibraryInputPlaceholderText.GetLocalizedResource() }; var tipText = new TextBlock { @@ -328,10 +327,10 @@ public static async Task ShowCreateNewLibraryDialogAsync() } } }, - TitleText = "FolderWidgetCreateNewLibraryDialogTitleText".GetLocalizedResource(), - SubtitleText = "SideBarCreateNewLibrary/Text".GetLocalizedResource(), - PrimaryButtonText = "Create".GetLocalizedResource(), - CloseButtonText = "Cancel".GetLocalizedResource(), + TitleText = Strings.FolderWidgetCreateNewLibraryDialogTitleText.GetLocalizedResource(), + SubtitleText = Strings.SideBarCreateNewLibrary_Text.GetLocalizedResource(), + PrimaryButtonText = Strings.Create.GetLocalizedResource(), + CloseButtonText = Strings.Cancel.GetLocalizedResource(), PrimaryButtonAction = async (vm, e) => { var (result, reason) = App.LibraryManager.CanCreateLibrary(inputText.Text); @@ -383,7 +382,12 @@ private void OnLibraryChanged(WatcherChangeTypes changeType, string oldPath, str return; } - var library1 = ShellFolderExtensions.GetShellLibraryItem(library, newPath); + var library1 = SafetyExtensions.IgnoreExceptions(() => ShellFolderExtensions.GetShellLibraryItem(library, newPath)); + if (library1 is null) + { + App.Logger.LogWarning($"Failed to open library after {changeType}: {newPath}"); + return; + } string? path = oldPath; if (string.IsNullOrEmpty(oldPath)) diff --git a/src/Files.App/Utils/Logger/SentryLogger.cs b/src/Files.App/Utils/Logger/SentryLogger.cs index ce6ef90d7d2d..d03d44df03d6 100644 --- a/src/Files.App/Utils/Logger/SentryLogger.cs +++ b/src/Files.App/Utils/Logger/SentryLogger.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.Extensions.Logging; using Sentry; @@ -23,7 +23,9 @@ public bool IsEnabled(LogLevel logLevel) public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { // Unhandled exceptions are captured in AppLifecycleHelper.HandleAppUnhandledException - if (exception is null || exception.Data[Mechanism.HandledKey] is false) + if (exception is null || + exception.Data[Mechanism.HandledKey] is false || + logLevel <= LogLevel.Information) return; var generalSettingsService = Ioc.Default.GetRequiredService(); diff --git a/src/Files.App/Utils/Logger/SentryLoggerProvider.cs b/src/Files.App/Utils/Logger/SentryLoggerProvider.cs index 35123ff1e662..08f42213fb17 100644 --- a/src/Files.App/Utils/Logger/SentryLoggerProvider.cs +++ b/src/Files.App/Utils/Logger/SentryLoggerProvider.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.Extensions.Logging; namespace Files.App.Utils.Logger { - public sealed class SentryLoggerProvider : ILoggerProvider + public sealed partial class SentryLoggerProvider : ILoggerProvider { public ILogger CreateLogger(string categoryName) { diff --git a/src/Files.App/Utils/RecentItem/RecentItem.cs b/src/Files.App/Utils/RecentItem/RecentItem.cs deleted file mode 100644 index 42ad32922cc4..000000000000 --- a/src/Files.App/Utils/RecentItem/RecentItem.cs +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Microsoft.UI.Xaml.Media.Imaging; - -namespace Files.App.Utils.RecentItem -{ - public sealed class RecentItem : WidgetCardItem, IEquatable - { - private BitmapImage _fileImg; - public BitmapImage FileImg - { - get => _fileImg; - set => SetProperty(ref _fileImg, value); - } - public string LinkPath { get; set; } // path of shortcut item (this is unique) - public string RecentPath { get; set; } // path to target item - public string Name { get; set; } - public DateTime LastModified { get; set; } - public byte[] PIDL { get; set; } - public override string Path => RecentPath; - - public RecentItem() - { - - } - - /// - /// Create a RecentItem instance from a link path. - /// This is usually needed if a shortcut is deleted -- the metadata is lost (i.e. the target item). - /// - /// The location that shortcut lives/lived in - public RecentItem(string linkPath) : base() - { - LinkPath = linkPath; - } - - /// - /// Create a RecentItem from a ShellLinkItem (usually from shortcuts in `Windows\Recent`) - /// - public RecentItem(ShellLinkItem linkItem, bool showFileExtension) : base() - { - LinkPath = linkItem.FilePath; - RecentPath = linkItem.TargetPath; - Name = showFileExtension ? linkItem.FileName : NameOrPathWithoutExtension(linkItem.FileName); - LastModified = linkItem.ModifiedDate; - PIDL = linkItem.PIDL; - } - - /// - /// Create a RecentItem from a ShellFileItem (usually from enumerating Quick Access directly). - /// - /// The shell file item - public RecentItem(ShellFileItem fileItem, bool showFileExtension) : base() - { - LinkPath = ShellStorageFolder.IsShellPath(fileItem.FilePath) ? fileItem.RecyclePath : fileItem.FilePath; // use true path on disk for shell items - RecentPath = LinkPath; // intentionally the same - Name = showFileExtension ? fileItem.FileName : NameOrPathWithoutExtension(fileItem.FileName); - LastModified = fileItem.ModifiedDate; - PIDL = fileItem.PIDL; - } - - public async Task LoadRecentItemIconAsync() - { - var result = await FileThumbnailHelper.GetIconAsync( - RecentPath, - Constants.ShellIconSizes.Small, - false, - IconOptions.UseCurrentScale); - - var bitmapImage = await result.ToBitmapAsync(); - if (bitmapImage is not null) - FileImg = bitmapImage; - } - - /// - /// Test equality for generic collection methods such as Remove(...) - /// - public bool Equals(RecentItem other) - { - if (other is null) - { - return false; - } - - // do not include LastModified or anything else here; otherwise, Remove(...) will fail since we lose metadata on deletion! - // when constructing a RecentItem from a deleted link, the only thing we have is the LinkPath (where the link use to be) - return LinkPath == other.LinkPath && - RecentPath == other.RecentPath; - } - - public override int GetHashCode() => (LinkPath, RecentPath).GetHashCode(); - public override bool Equals(object? o) => o is RecentItem other && Equals(other); - - /** - * Strips a name from an extension while aware of some edge cases. - * - * example.min.js => example.min - * example.js => example - * .gitignore => .gitignore - */ - private static string NameOrPathWithoutExtension(string nameOrPath) - { - string strippedExtension = System.IO.Path.GetFileNameWithoutExtension(nameOrPath); - return string.IsNullOrEmpty(strippedExtension) ? System.IO.Path.GetFileName(nameOrPath) : strippedExtension; - } - } -} diff --git a/src/Files.App/Utils/RecentItem/RecentItems.cs b/src/Files.App/Utils/RecentItem/RecentItems.cs deleted file mode 100644 index a3b67a5a54b3..000000000000 --- a/src/Files.App/Utils/RecentItem/RecentItems.cs +++ /dev/null @@ -1,310 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Microsoft.Extensions.Logging; -using Microsoft.Win32; -using System.Collections.Specialized; -using System.IO; -using Vanara.PInvoke; -using Vanara.Windows.Shell; - -namespace Files.App.Utils.RecentItem -{ - public sealed class RecentItems : IDisposable - { - private const string QuickAccessGuid = "::{679f85cb-0220-4080-b29b-5540cc05aab6}"; - - public EventHandler? RecentFilesChanged; - public EventHandler? RecentFoldersChanged; - - // recent files - private readonly List recentFiles = []; - public IReadOnlyList RecentFiles // already sorted - { - get - { - lock (recentFiles) - { - return recentFiles.ToList().AsReadOnly(); - } - } - } - - // recent folders - private readonly List recentFolders = []; - public IReadOnlyList RecentFolders // already sorted - { - get - { - lock (recentFolders) - { - return recentFolders.ToList().AsReadOnly(); - } - } - } - - private readonly IUserSettingsService UserSettingsService; - - private bool ShowFileExtensions => UserSettingsService.FoldersSettingsService.ShowFileExtensions; - - - public RecentItems(IUserSettingsService userSettingsService) - { - RecentItemsManager.Default.RecentItemsChanged += OnRecentItemsChangedAsync; - UserSettingsService = userSettingsService; - } - - private async void OnRecentItemsChangedAsync(object? sender, EventArgs e) - { - await UpdateRecentFilesAsync(); - } - - /// - /// Refetch recent files to `recentFiles`. - /// - public async Task UpdateRecentFilesAsync() - { - // enumerate with fulltrust process - List enumeratedFiles = await ListRecentFilesAsync(); - if (enumeratedFiles is not null) - { - var recentFilesSnapshot = RecentFiles; - - lock (recentFiles) - { - recentFiles.Clear(); - recentFiles.AddRange(enumeratedFiles); - // do not sort here, enumeration order *is* the correct order since we get it from Quick Access - } - - var changedActionEventArgs = GetChangedActionEventArgs(recentFilesSnapshot, enumeratedFiles); - RecentFilesChanged?.Invoke(this, changedActionEventArgs); - } - } - - /// - /// Refetch recent folders to `recentFolders`. - /// - public async Task UpdateRecentFoldersAsync() - { - var enumeratedFolders = await Task.Run(ListRecentFoldersAsync); // run off the UI thread - if (enumeratedFolders is not null) - { - lock (recentFolders) - { - recentFolders.Clear(); - recentFolders.AddRange(enumeratedFolders); - - // shortcut modifications in `Windows\Recent` consist of a delete + add operation; - // thus, last modify date is reset and we can sort off it - recentFolders.Sort((x, y) => y.LastModified.CompareTo(x.LastModified)); - } - - RecentFoldersChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - } - } - - /// - /// Enumerate recently accessed files via `Quick Access`. - /// - public async Task> ListRecentFilesAsync() - { - // Since the maximum number of recent files is 20, we set the count to 20 to avoid loading of unnecessary shell items. - return (await Win32Helper.GetShellFolderAsync(QuickAccessGuid, false, true, 0, 20)).Enumerate - .Where(link => !link.IsFolder) - .Select(link => new RecentItem(link, ShowFileExtensions)).ToList(); - } - - /// - /// Enumerate recently accessed folders via `Windows\Recent`. - /// - public async Task> ListRecentFoldersAsync() - { - var excludeMask = FileAttributes.Hidden; - var linkFilePaths = Directory.EnumerateFiles(Constants.UserEnvironmentPaths.RecentItemsPath).Where(f => (new FileInfo(f).Attributes & excludeMask) == 0); - - Task GetRecentItemFromLink(string linkPath) - { - return Task.Run(() => - { - try - { - using var link = new ShellLink(linkPath, LinkResolution.NoUIWithMsgPump, default, TimeSpan.FromMilliseconds(100)); - - if (!string.IsNullOrEmpty(link.TargetPath) && link.Target.IsFolder) - { - var shellLinkItem = ShellFolderExtensions.GetShellLinkItem(link); - return new RecentItem(shellLinkItem, ShowFileExtensions); - } - } - catch (FileNotFoundException) - { - // occurs when shortcut or shortcut target is deleted and accessed (link.Target) - // consequently, we shouldn't include the item as a recent item - } - catch (Exception ex) - { - // other error (usually a COMException) - } - - return null; - }); - } - - var recentItems = await Task.WhenAll(linkFilePaths.Select(GetRecentItemFromLink)); - return recentItems.OfType().ToList(); - } - - /// - /// Adds a shortcut to `Windows\Recent`. The path can be to a file or folder. - /// It will update to `recentFiles` or `recentFolders` respectively. - /// - /// Path to a file or folder - /// Whether the action was successfully handled or not - public bool AddToRecentItems(string path) - { - try - { - Shell32.SHAddToRecentDocs(Shell32.SHARD.SHARD_PATHW, path); - return true; - } - catch (Exception ex) - { - App.Logger.LogWarning(ex, ex.Message); - return false; - } - } - - /// - /// Clears both `recentFiles` and `recentFolders`. - /// This will also clear the Recent Files (and its jumplist) in File Explorer. - /// - /// Whether the action was successfully handled or not - public bool ClearRecentItems() - { - try - { - Shell32.SHAddToRecentDocs(Shell32.SHARD.SHARD_PIDL, (string)null); - return true; - } - catch (Exception ex) - { - App.Logger.LogWarning(ex, ex.Message); - return false; - } - } - - /// - /// Unpin (or remove) a file from `recentFiles`. - /// This will also unpin the item from the Recent Files in File Explorer. - /// - /// Whether the action was successfully handled or not - public Task UnpinFromRecentFiles(RecentItem item) - { - return SafetyExtensions.IgnoreExceptions(() => Task.Run(async () => - { - using var pidl = new Shell32.PIDL(item.PIDL); - using var shellItem = ShellItem.Open(pidl); - using var cMenu = await ContextMenu.GetContextMenuForFiles(new[] { shellItem }, Shell32.CMF.CMF_NORMAL); - if (cMenu is not null) - return await cMenu.InvokeVerb("remove"); - return false; - })); - } - - private NotifyCollectionChangedEventArgs GetChangedActionEventArgs(IReadOnlyList oldItems, IList newItems) - { - // a single item was added - if (newItems.Count == oldItems.Count + 1) - { - var differences = newItems.Except(oldItems); - if (differences.Take(2).Count() == 1) - { - return new(NotifyCollectionChangedAction.Add, newItems.First()); - } - } - // a single item was removed - else if (newItems.Count == oldItems.Count - 1) - { - var differences = oldItems.Except(newItems); - if (differences.Take(2).Count() == 1) - { - for (int i = 0; i < oldItems.Count; i++) - { - if (i >= newItems.Count || !newItems[i].Equals(oldItems[i])) - { - return new(NotifyCollectionChangedAction.Remove, oldItems[i], index: i); - } - } - } - } - // a single item was moved - else if (newItems.Count == oldItems.Count) - { - var differences = oldItems.Except(newItems); - // desync due to skipped/batched calls, reset the list - if (differences.Any()) - { - return new(NotifyCollectionChangedAction.Reset); - } - - // first diff from reversed is the designated item - for (int i = oldItems.Count - 1; i >= 0; i--) - { - if (!oldItems[i].Equals(newItems[i])) - { - return new(NotifyCollectionChangedAction.Move, oldItems[i], index: 0, oldIndex: i); - } - } - } - - return new(NotifyCollectionChangedAction.Reset); - } - - public bool CheckIsRecentFilesEnabled() - { - using var subkey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer"); - using var advSubkey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced"); - using var userPolicySubkey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer"); - using var sysPolicySubkey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer"); - - if (subkey is not null) - { - // quick access: show recent files option - bool showRecentValue = Convert.ToBoolean(subkey.GetValue("ShowRecent", true)); // 1 by default - if (!showRecentValue) - { - return false; - } - } - - if (advSubkey is not null) - { - // settings: personalization > start > show recently opened items - bool startTrackDocsValue = Convert.ToBoolean(advSubkey.GetValue("Start_TrackDocs", true)); // 1 by default - if (!startTrackDocsValue) - { - return false; - } - } - - // for users in group policies - var policySubkey = userPolicySubkey ?? sysPolicySubkey; - if (policySubkey is not null) - { - bool noRecentDocsHistoryValue = Convert.ToBoolean(policySubkey.GetValue("NoRecentDocsHistory", false)); // 0 by default - if (noRecentDocsHistoryValue) - { - return false; - } - } - - return true; - } - - public void Dispose() - { - RecentItemsManager.Default.RecentItemsChanged -= OnRecentItemsChangedAsync; - } - } -} \ No newline at end of file diff --git a/src/Files.App/Utils/RecentItem/RecentItemsManager.cs b/src/Files.App/Utils/RecentItem/RecentItemsManager.cs deleted file mode 100644 index 8e5e4faf25a8..000000000000 --- a/src/Files.App/Utils/RecentItem/RecentItemsManager.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System.IO; - -namespace Files.App.Utils.RecentItem -{ - public sealed class RecentItemsManager - { - private static readonly Lazy lazy = new(() => new RecentItemsManager()); - private static readonly string recentItemsPath = Environment.GetFolderPath(Environment.SpecialFolder.Recent); - private static readonly string automaticDestinationsPath = Path.Combine(recentItemsPath, "AutomaticDestinations"); - private const string QuickAccessJumpListFileName = "5f7b5f1e01b83767.automaticDestinations-ms"; - private DateTime quickAccessLastReadTime = DateTime.MinValue; - private FileSystemWatcher? quickAccessJumpListWatcher; - - public event EventHandler? RecentItemsChanged; - - public static RecentItemsManager Default - { - get - { - return lazy.Value; - } - } - - private RecentItemsManager() - { - Initialize(); - } - - private void Initialize() - { - StartQuickAccessJumpListWatcher(); - } - - private void StartQuickAccessJumpListWatcher() - { - if (quickAccessJumpListWatcher is not null) - { - return; - } - - quickAccessJumpListWatcher = new FileSystemWatcher - { - Path = automaticDestinationsPath, - Filter = QuickAccessJumpListFileName, - NotifyFilter = NotifyFilters.DirectoryName | NotifyFilters.FileName | NotifyFilters.LastWrite, - }; - quickAccessJumpListWatcher.Changed += QuickAccessJumpList_Changed; - quickAccessJumpListWatcher.Deleted += QuickAccessJumpList_Changed; - quickAccessJumpListWatcher.EnableRaisingEvents = true; - } - - private void QuickAccessJumpList_Changed(object sender, FileSystemEventArgs e) - { - System.Diagnostics.Debug.WriteLine($"{nameof(QuickAccessJumpList_Changed)}: {e.ChangeType}, {e.FullPath}"); - - // skip if multiple events occurred for singular change - var lastWriteTime = File.GetLastWriteTime(e.FullPath); - if (quickAccessLastReadTime >= lastWriteTime) - { - return; - } - else - { - quickAccessLastReadTime = lastWriteTime; - } - - RecentItemsChanged?.Invoke(this, e); - } - - private void Unregister() - { - quickAccessJumpListWatcher?.Dispose(); - } - - ~RecentItemsManager() - { - Unregister(); - } - } -} diff --git a/src/Files.App/Utils/RecycleBin/RecycleBinHelpers.cs b/src/Files.App/Utils/RecycleBin/RecycleBinHelpers.cs deleted file mode 100644 index 4f6df171c2c6..000000000000 --- a/src/Files.App/Utils/RecycleBin/RecycleBinHelpers.cs +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Microsoft.UI.Xaml.Controls; -using Vanara.PInvoke; -using Windows.Foundation.Metadata; -using Windows.Storage; - -namespace Files.App.Utils.RecycleBin -{ - public static class RecycleBinHelpers - { - private static readonly StatusCenterViewModel _statusCenterViewModel = Ioc.Default.GetRequiredService(); - - private static readonly IUserSettingsService userSettingsService = Ioc.Default.GetRequiredService(); - - public static async Task> EnumerateRecycleBin() - { - return (await Win32Helper.GetShellFolderAsync(Constants.UserEnvironmentPaths.RecycleBinPath, false, true, 0, int.MaxValue)).Enumerate; - } - - public static ulong GetSize() - { - return (ulong)Win32Helper.QueryRecycleBin().BinSize; - } - - public static async Task IsRecycleBinItem(IStorageItem item) - { - List recycleBinItems = await EnumerateRecycleBin(); - return recycleBinItems.Any((shellItem) => shellItem.RecyclePath == item.Path); - } - - public static async Task IsRecycleBinItem(string path) - { - List recycleBinItems = await EnumerateRecycleBin(); - return recycleBinItems.Any((shellItem) => shellItem.RecyclePath == path); - } - - public static bool IsPathUnderRecycleBin(string path) - { - return !string.IsNullOrWhiteSpace(path) && RegexHelpers.RecycleBinPath().IsMatch(path); - } - - public static async Task EmptyRecycleBinAsync() - { - // Display confirmation dialog - var ConfirmEmptyBinDialog = new ContentDialog() - { - Title = "ConfirmEmptyBinDialogTitle".GetLocalizedResource(), - Content = "ConfirmEmptyBinDialogContent".GetLocalizedResource(), - PrimaryButtonText = "Yes".GetLocalizedResource(), - SecondaryButtonText = "Cancel".GetLocalizedResource(), - DefaultButton = ContentDialogButton.Primary - }; - - if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) - ConfirmEmptyBinDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; - - // If the operation is approved by the user - if (userSettingsService.FoldersSettingsService.DeleteConfirmationPolicy is DeleteConfirmationPolicies.Never || - await ConfirmEmptyBinDialog.TryShowAsync() == ContentDialogResult.Primary) - { - - var banner = StatusCenterHelper.AddCard_EmptyRecycleBin(ReturnResult.InProgress); - - bool bResult = await Task.Run(() => Shell32.SHEmptyRecycleBin(IntPtr.Zero, null, Shell32.SHERB.SHERB_NOCONFIRMATION | Shell32.SHERB.SHERB_NOPROGRESSUI).Succeeded); - - _statusCenterViewModel.RemoveItem(banner); - - if (bResult) - StatusCenterHelper.AddCard_EmptyRecycleBin(ReturnResult.Success); - else - StatusCenterHelper.AddCard_EmptyRecycleBin(ReturnResult.Failed); - } - } - - public static async Task RestoreRecycleBinAsync() - { - var confirmEmptyBinDialog = new ContentDialog() - { - Title = "ConfirmRestoreBinDialogTitle".GetLocalizedResource(), - Content = "ConfirmRestoreBinDialogContent".GetLocalizedResource(), - PrimaryButtonText = "Yes".GetLocalizedResource(), - SecondaryButtonText = "Cancel".GetLocalizedResource(), - DefaultButton = ContentDialogButton.Primary - }; - - if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) - confirmEmptyBinDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; - - ContentDialogResult result = await confirmEmptyBinDialog.TryShowAsync(); - - if (result == ContentDialogResult.Primary) - { - try - { - Vanara.Windows.Shell.RecycleBin.RestoreAll(); - } - catch (Exception) - { - var errorDialog = new ContentDialog() - { - Title = "FailedToRestore".GetLocalizedResource(), - PrimaryButtonText = "OK".GetLocalizedResource(), - }; - - if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) - errorDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; - - await errorDialog.TryShowAsync(); - } - } - } - - public static async Task RestoreSelectionRecycleBinAsync(IShellPage associatedInstance) - { - var items = associatedInstance.SlimContentPage.SelectedItems; - if (items == null) - return; - var ConfirmEmptyBinDialog = new ContentDialog() - { - Title = "ConfirmRestoreSelectionBinDialogTitle".GetLocalizedResource(), - - Content = string.Format("ConfirmRestoreSelectionBinDialogContent".GetLocalizedResource(), items.Count), - PrimaryButtonText = "Yes".GetLocalizedResource(), - SecondaryButtonText = "Cancel".GetLocalizedResource(), - DefaultButton = ContentDialogButton.Primary - }; - - if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) - ConfirmEmptyBinDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; - - ContentDialogResult result = await ConfirmEmptyBinDialog.TryShowAsync(); - - if (result == ContentDialogResult.Primary) - await RestoreItemAsync(associatedInstance); - } - - public static async Task HasRecycleBin(string? path) - { - if (string.IsNullOrEmpty(path) || path.StartsWith(@"\\?\", StringComparison.Ordinal)) - return false; - - var result = await FileOperationsHelpers.TestRecycleAsync(path.Split('|')); - - return result.Item1 &= result.Item2 is not null && result.Item2.Items.All(x => x.Succeeded); - } - - public static bool RecycleBinHasItems() - { - return Win32Helper.QueryRecycleBin().NumItems > 0; - } - - public static async Task RestoreItemAsync(IShellPage associatedInstance) - { - var selected = associatedInstance.SlimContentPage.SelectedItems; - if (selected == null) - return; - var items = selected.ToList().Where(x => x is RecycleBinItem).Select((item) => new - { - Source = StorageHelpers.FromPathAndType( - item.ItemPath, - item.PrimaryItemAttribute == StorageItemTypes.File ? FilesystemItemType.File : FilesystemItemType.Directory), - Dest = ((RecycleBinItem)item).ItemOriginalPath - }); - await associatedInstance.FilesystemHelpers.RestoreItemsFromTrashAsync(items.Select(x => x.Source), items.Select(x => x.Dest), true); - } - - public static async Task DeleteItemAsync(IShellPage associatedInstance) - { - var selected = associatedInstance.SlimContentPage.SelectedItems; - if (selected == null) - return; - var items = selected.ToList().Select((item) => StorageHelpers.FromPathAndType( - item.ItemPath, - item.PrimaryItemAttribute == StorageItemTypes.File ? FilesystemItemType.File : FilesystemItemType.Directory)); - await associatedInstance.FilesystemHelpers.DeleteItemsAsync(items, userSettingsService.FoldersSettingsService.DeleteConfirmationPolicy, false, true); - } - } -} diff --git a/src/Files.App/Utils/RecycleBin/RecycleBinManager.cs b/src/Files.App/Utils/RecycleBin/RecycleBinManager.cs deleted file mode 100644 index 8b834a19747a..000000000000 --- a/src/Files.App/Utils/RecycleBin/RecycleBinManager.cs +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System.Security.Principal; - -namespace Files.App.Utils.RecycleBin -{ - /// - /// Provides a utility to handle Windows Recycle Bin. - /// - public sealed class RecycleBinManager - { - private static readonly Lazy lazy = new(() => new RecycleBinManager()); - - private List? binWatchers; - - public event SystemIO.FileSystemEventHandler? RecycleBinItemCreated; - - public event SystemIO.FileSystemEventHandler? RecycleBinItemDeleted; - - public event SystemIO.FileSystemEventHandler? RecycleBinItemRenamed; - - public event SystemIO.FileSystemEventHandler? RecycleBinRefreshRequested; - - public static RecycleBinManager Default - => lazy.Value; - - private RecycleBinManager() - { - Initialize(); - } - - private void Initialize() - { - // Create shell COM object and get recycle bin folder - StartRecycleBinWatcher(); - } - - private void StartRecycleBinWatcher() - { - // NOTE: SHChangeNotifyRegister only works if recycle bin is open in explorer - // Create file system watcher to monitor recycle bin folder(s) - binWatchers = []; - - var sid = WindowsIdentity.GetCurrent().User.ToString(); - - foreach (var drive in SystemIO.DriveInfo.GetDrives()) - { - var recyclePath = SystemIO.Path.Combine(drive.Name, "$RECYCLE.BIN", sid); - - if (drive.DriveType == SystemIO.DriveType.Network || !SystemIO.Directory.Exists(recyclePath)) - continue; - - SafetyExtensions.IgnoreExceptions(() => - { - SystemIO.FileSystemWatcher watcher = new() - { - Path = recyclePath, - Filter = "*.*", - NotifyFilter = SystemIO.NotifyFilters.LastWrite | SystemIO.NotifyFilters.FileName | SystemIO.NotifyFilters.DirectoryName - }; - - watcher.Created += RecycleBinWatcher_Changed; - watcher.Deleted += RecycleBinWatcher_Changed; - watcher.EnableRaisingEvents = true; - - binWatchers.Add(watcher); - }); - } - } - - private void RecycleBinWatcher_Changed(object sender, SystemIO.FileSystemEventArgs e) - { - Debug.WriteLine($"Recycle bin event: {e.ChangeType}, {e.FullPath}"); - - if (e.Name.StartsWith("$I", StringComparison.Ordinal)) - { - // Recycle bin also stores a file starting with $I for each item - return; - } - - switch (e.ChangeType) - { - case SystemIO.WatcherChangeTypes.Created: - RecycleBinItemCreated?.Invoke(this, e); - break; - case SystemIO.WatcherChangeTypes.Deleted: - RecycleBinItemDeleted?.Invoke(this, e); - break; - case SystemIO.WatcherChangeTypes.Renamed: - RecycleBinItemRenamed?.Invoke(this, e); - break; - default: - RecycleBinRefreshRequested?.Invoke(this, e); - break; - } - } - - private void Unregister() - { - if (binWatchers is not null) - { - foreach (var watcher in binWatchers) - watcher.Dispose(); - } - } - - ~RecycleBinManager() - { - Unregister(); - } - } -} diff --git a/src/Files.App/Utils/Serialization/BaseJsonSettings.cs b/src/Files.App/Utils/Serialization/BaseJsonSettings.cs index 0e337cdc3218..215952c8f0ec 100644 --- a/src/Files.App/Utils/Serialization/BaseJsonSettings.cs +++ b/src/Files.App/Utils/Serialization/BaseJsonSettings.cs @@ -1,7 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using System; using System.Runtime.CompilerServices; namespace Files.App.Utils.Serialization diff --git a/src/Files.App/Utils/Serialization/BaseObservableJsonSettings.cs b/src/Files.App/Utils/Serialization/BaseObservableJsonSettings.cs index 7dafbb355416..39ae970163fa 100644 --- a/src/Files.App/Utils/Serialization/BaseObservableJsonSettings.cs +++ b/src/Files.App/Utils/Serialization/BaseObservableJsonSettings.cs @@ -1,7 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using System.ComponentModel; using System.Runtime.CompilerServices; namespace Files.App.Utils.Serialization diff --git a/src/Files.App/Utils/Serialization/IJsonSettingsDatabase.cs b/src/Files.App/Utils/Serialization/IJsonSettingsDatabase.cs index b7ad12681b7a..ab4a49e1889e 100644 --- a/src/Files.App/Utils/Serialization/IJsonSettingsDatabase.cs +++ b/src/Files.App/Utils/Serialization/IJsonSettingsDatabase.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.Serialization { diff --git a/src/Files.App/Utils/Serialization/IJsonSettingsSerializer.cs b/src/Files.App/Utils/Serialization/IJsonSettingsSerializer.cs index f6b7b96fc258..04d273c60559 100644 --- a/src/Files.App/Utils/Serialization/IJsonSettingsSerializer.cs +++ b/src/Files.App/Utils/Serialization/IJsonSettingsSerializer.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.Serialization { diff --git a/src/Files.App/Utils/Serialization/ISettingsSerializer.cs b/src/Files.App/Utils/Serialization/ISettingsSerializer.cs index ef4319dfed18..39a508353a45 100644 --- a/src/Files.App/Utils/Serialization/ISettingsSerializer.cs +++ b/src/Files.App/Utils/Serialization/ISettingsSerializer.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.Serialization { diff --git a/src/Files.App/Utils/Serialization/ISettingsSharingContext.cs b/src/Files.App/Utils/Serialization/ISettingsSharingContext.cs index a2a62032800c..1d181613c289 100644 --- a/src/Files.App/Utils/Serialization/ISettingsSharingContext.cs +++ b/src/Files.App/Utils/Serialization/ISettingsSharingContext.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.Serialization { diff --git a/src/Files.App/Utils/Serialization/Implementation/CachingJsonSettingsDatabase.cs b/src/Files.App/Utils/Serialization/Implementation/CachingJsonSettingsDatabase.cs index 68916d24d79a..f005bf8b6631 100644 --- a/src/Files.App/Utils/Serialization/Implementation/CachingJsonSettingsDatabase.cs +++ b/src/Files.App/Utils/Serialization/Implementation/CachingJsonSettingsDatabase.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.Serialization.Implementation { diff --git a/src/Files.App/Utils/Serialization/Implementation/DefaultJsonSettingsDatabase.cs b/src/Files.App/Utils/Serialization/Implementation/DefaultJsonSettingsDatabase.cs index 5cde342fbb5c..cfd190a1857d 100644 --- a/src/Files.App/Utils/Serialization/Implementation/DefaultJsonSettingsDatabase.cs +++ b/src/Files.App/Utils/Serialization/Implementation/DefaultJsonSettingsDatabase.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Collections.Concurrent; using System.Text.Json; @@ -119,7 +119,16 @@ public virtual bool ImportSettings(object? import) { if (obj is JsonElement jElem) { - return jElem.Deserialize(); + try + { + return jElem.Deserialize(); + } + catch (JsonException) + { + // Deserialization failed (e.g., incompatible type in settings file) + // Return null to fall back to the default value + return default; + } } return (TValue?)obj; diff --git a/src/Files.App/Utils/Serialization/Implementation/DefaultJsonSettingsSerializer.cs b/src/Files.App/Utils/Serialization/Implementation/DefaultJsonSettingsSerializer.cs index c43bf238b267..7965e6cbf534 100644 --- a/src/Files.App/Utils/Serialization/Implementation/DefaultJsonSettingsSerializer.cs +++ b/src/Files.App/Utils/Serialization/Implementation/DefaultJsonSettingsSerializer.cs @@ -1,7 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System.Text.Json; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.Serialization.Implementation { diff --git a/src/Files.App/Utils/Serialization/Implementation/DefaultSettingsSerializer.cs b/src/Files.App/Utils/Serialization/Implementation/DefaultSettingsSerializer.cs index fe4abf2ee5ab..db37b20cd7f5 100644 --- a/src/Files.App/Utils/Serialization/Implementation/DefaultSettingsSerializer.cs +++ b/src/Files.App/Utils/Serialization/Implementation/DefaultSettingsSerializer.cs @@ -1,8 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.IO; using Windows.Win32; +using Windows.Win32.Storage.FileSystem; using static Files.App.Helpers.Win32Helper; using static Files.App.Helpers.Win32PInvoke; @@ -16,7 +17,7 @@ public bool CreateFile(string path) { PInvoke.CreateDirectoryFromApp(Path.GetDirectoryName(path), null); - var hFile = CreateFileFromApp(path, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero, OPEN_ALWAYS, (uint)File_Attributes.BackupSemantics, IntPtr.Zero); + var hFile = CreateFileFromApp(path, (uint)FILE_ACCESS_RIGHTS.FILE_GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero, OPEN_ALWAYS, (uint)File_Attributes.BackupSemantics, IntPtr.Zero); if (hFile.IsHandleInvalid()) { return false; @@ -35,14 +36,14 @@ public bool CreateFile(string path) /// public string ReadFromFile() { - _ = _filePath ?? throw new ArgumentNullException(nameof(_filePath)); + ArgumentNullException.ThrowIfNull(_filePath); return ReadStringFromFile(_filePath); } public bool WriteToFile(string? text) { - _ = _filePath ?? throw new ArgumentNullException(nameof(_filePath)); + ArgumentNullException.ThrowIfNull(_filePath); return WriteStringToFile(_filePath, text); } diff --git a/src/Files.App/Utils/Shell/ContextMenu.cs b/src/Files.App/Utils/Shell/ContextMenu.cs index 01354324a24e..54cb5eaab4a5 100644 --- a/src/Files.App/Utils/Shell/ContextMenu.cs +++ b/src/Files.App/Utils/Shell/ContextMenu.cs @@ -1,23 +1,25 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Drawing; using System.Runtime.InteropServices; using Vanara.InteropServices; using Vanara.PInvoke; using Vanara.Windows.Shell; +using Windows.Win32; +using Windows.Win32.UI.WindowsAndMessaging; namespace Files.App.Utils.Shell { /// /// Provides a helper for Win32 context menu. /// - public class ContextMenu : Win32ContextMenu, IDisposable + public partial class ContextMenu : Win32ContextMenu, IDisposable { private Shell32.IContextMenu _cMenu; - + private User32.SafeHMENU _hMenu; - + private readonly ThreadWithMessageQueue _owningThread; private readonly Func? _itemFilter; @@ -43,7 +45,7 @@ private ContextMenu(Shell32.IContextMenu cMenu, User32.SafeHMENU hMenu, IEnumera public async static Task InvokeVerb(string verb, params string[] filePaths) { - using var cMenu = await GetContextMenuForFiles(filePaths, Shell32.CMF.CMF_DEFAULTONLY); + using var cMenu = await GetContextMenuForFiles(filePaths, PInvoke.CMF_DEFAULTONLY); return cMenu is not null && await cMenu.InvokeVerb(verb); } @@ -83,7 +85,7 @@ public async Task InvokeVerb(string? verb) return false; } - public async Task InvokeItem(int itemID) + public async Task InvokeItem(int itemID, string? workingDirectory = null) { if (itemID < 0) return false; @@ -98,6 +100,8 @@ public async Task InvokeItem(int itemID) }; pici.cbSize = (uint)Marshal.SizeOf(pici); + if (workingDirectory is not null) + pici.lpDirectoryW = workingDirectory; await _owningThread.PostMethod(() => _cMenu.InvokeCommand(pici)); Win32Helper.BringToForeground(currentWindows); @@ -112,7 +116,7 @@ public async Task InvokeItem(int itemID) return false; } - public async static Task GetContextMenuForFiles(string[] filePathList, Shell32.CMF flags, Func? itemFilter = null) + public async static Task GetContextMenuForFiles(string[] filePathList, uint flags, Func? itemFilter = null) { var owningThread = new ThreadWithMessageQueue(); @@ -140,14 +144,14 @@ public async Task InvokeItem(int itemID) }); } - public async static Task GetContextMenuForFiles(ShellItem[] shellItems, Shell32.CMF flags, Func? itemFilter = null) + public async static Task GetContextMenuForFiles(ShellItem[] shellItems, uint flags, Func? itemFilter = null) { var owningThread = new ThreadWithMessageQueue(); return await owningThread.PostMethod(() => GetContextMenuForFiles(shellItems, flags, owningThread, itemFilter)); } - private static ContextMenu? GetContextMenuForFiles(ShellItem[] shellItems, Shell32.CMF flags, ThreadWithMessageQueue owningThread, Func? itemFilter = null) + private static ContextMenu? GetContextMenuForFiles(ShellItem[] shellItems, uint flags, ThreadWithMessageQueue owningThread, Func? itemFilter = null) { if (!shellItems.Any()) return null; @@ -159,7 +163,7 @@ public async Task InvokeItem(int itemID) Shell32.IContextMenu menu = sf.GetChildrenUIObjects(default, shellItems); var hMenu = User32.CreatePopupMenu(); - menu.QueryContextMenu(hMenu, 0, 1, 0x7FFF, flags); + menu.QueryContextMenu(hMenu, 0, 1, 0x7FFF, (Shell32.CMF)flags); var contextMenu = new ContextMenu(menu, hMenu, shellItems.Select(x => x.ParsingName), owningThread, itemFilter); contextMenu.EnumMenuItems(hMenu, contextMenu.Items); @@ -174,10 +178,10 @@ public async Task InvokeItem(int itemID) public static async Task WarmUpQueryContextMenuAsync() { - using var cMenu = await GetContextMenuForFiles(new string[] { $@"{Environment.GetEnvironmentVariable("SystemDrive")}\" }, Shell32.CMF.CMF_NORMAL); + using var cMenu = await GetContextMenuForFiles(new string[] { $@"{Constants.UserEnvironmentPaths.SystemDrivePath}\" }, PInvoke.CMF_NORMAL); } - private void EnumMenuItems(HMENU hMenu, List menuItemsResult, bool loadSubenus = false) + private void EnumMenuItems(Vanara.PInvoke.HMENU hMenu, List menuItemsResult, bool loadSubenus = false) { var itemCount = User32.GetMenuItemCount(hMenu); @@ -211,12 +215,12 @@ private void EnumMenuItems(HMENU hMenu, List menuItemsResu continue; } - menuItem.Type = (MenuItemType)menuItemInfo.fType; + menuItem.Type = (MENU_ITEM_TYPE)menuItemInfo.fType; // wID - idCmdFirst menuItem.ID = (int)(menuItemInfo.wID - 1); - if (menuItem.Type == MenuItemType.MFT_STRING) + if (menuItem.Type == MENU_ITEM_TYPE.MFT_STRING) { Debug.WriteLine("Item {0} ({1}): {2}", index, menuItemInfo.wID, menuItemInfo.dwTypeData); @@ -244,7 +248,7 @@ private void EnumMenuItems(HMENU hMenu, List menuItemsResu } } - if (menuItemInfo.hSubMenu != HMENU.NULL) + if (menuItemInfo.hSubMenu != Vanara.PInvoke.HMENU.NULL) { Debug.WriteLine("Item {0}: has submenu", index); var subItems = new List(); diff --git a/src/Files.App/Utils/Shell/ContextMenuItem.cs b/src/Files.App/Utils/Shell/ContextMenuItem.cs index 6247a4545bf2..28878c5091e1 100644 --- a/src/Files.App/Utils/Shell/ContextMenuItem.cs +++ b/src/Files.App/Utils/Shell/ContextMenuItem.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.Shell { /// /// Represents an item for Win32 context menu. /// - public class ContextMenuItem : Win32ContextMenuItem, IDisposable + public partial class ContextMenuItem : Win32ContextMenuItem, IDisposable { public void Dispose() { diff --git a/src/Files.App/Utils/Shell/Disposable.cs b/src/Files.App/Utils/Shell/Disposable.cs index 970e26308617..bd9ab725a276 100644 --- a/src/Files.App/Utils/Shell/Disposable.cs +++ b/src/Files.App/Utils/Shell/Disposable.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.Shell { diff --git a/src/Files.App/Utils/Shell/FileAssociationHelpers.cs b/src/Files.App/Utils/Shell/FileAssociationHelpers.cs new file mode 100644 index 000000000000..036d973df821 --- /dev/null +++ b/src/Files.App/Utils/Shell/FileAssociationHelpers.cs @@ -0,0 +1,109 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.UI.Shell; + +namespace Files.App.Utils.Shell +{ + public static class FileAssociationHelpers + { + // Known identifiers used by classic and AppX registrations for Microsoft Photos. + private static readonly string[] PhotosAssociationMarkers = + [ + "Microsoft.Windows.Photos", + "ms-photos", + "AppX43hnxtbyyps62jhe9sqpdzxn1790zetc" + ]; + + // PROGID and APPID are enough for modern default-app detection and cheaper than probing command/exe every time. + private static readonly ASSOCSTR[] PhotosAssociationSources = + [ + ASSOCSTR.ASSOCSTR_PROGID, + ASSOCSTR.ASSOCSTR_APPID, + ]; + + /// + /// Returns true when the specified extension is currently associated with Microsoft Photos. + /// + /// File extension with or without leading dot (e.g. ".jpg" or "jpg"). + public static bool IsMicrosoftPhotosDefaultAssociation(string? fileExtension) + { + if (string.IsNullOrWhiteSpace(fileExtension)) + return false; + + string normalizedExtension = fileExtension.StartsWith(".", StringComparison.Ordinal) + ? fileExtension + : $".{fileExtension}"; + + return IsMicrosoftPhotosDefaultAssociationCore(normalizedExtension); + } + + private static bool IsMicrosoftPhotosDefaultAssociationCore(string fileExtension) + { + + foreach (var source in PhotosAssociationSources) + { + if (TryGetAssociationString(fileExtension, source, out string value) && IsPhotosAssociationValue(value)) + return true; + } + + return false; + } + + private static bool IsPhotosAssociationValue(string associationValue) + => PhotosAssociationMarkers.Any(marker => associationValue.Contains(marker, StringComparison.OrdinalIgnoreCase)); + + private static unsafe bool TryGetAssociationString(string fileExtension, ASSOCSTR association, out string value) + { + value = string.Empty; + + try + { + fixed (char* pszAssoc = fileExtension) + { + PWSTR pwszAssoc = new(pszAssoc); + uint cchOutput = 0; + + // First call retrieves the required buffer length. + _ = PInvoke.AssocQueryString( + ASSOCF.ASSOCF_INIT_IGNOREUNKNOWN, + association, + pwszAssoc, + default, + default, + &cchOutput); + + if (cchOutput <= 1) + return false; + + char[] outputBuffer = new char[cchOutput]; + fixed (char* pszOutput = outputBuffer) + { + PWSTR pwszOutput = new(pszOutput); + + // Second call fills the buffer with the resolved association string. + var result = PInvoke.AssocQueryString( + ASSOCF.ASSOCF_INIT_IGNOREUNKNOWN, + association, + pwszAssoc, + default, + pwszOutput, + &cchOutput); + + if (result.Failed) + return false; + + value = pwszOutput.ToString(); + return !string.IsNullOrWhiteSpace(value); + } + } + } + catch + { + return false; + } + } + } +} diff --git a/src/Files.App/Utils/Shell/ItemStreamHelper.cs b/src/Files.App/Utils/Shell/ItemStreamHelper.cs index 523845e21d8c..114c071cc655 100644 --- a/src/Files.App/Utils/Shell/ItemStreamHelper.cs +++ b/src/Files.App/Utils/Shell/ItemStreamHelper.cs @@ -1,6 +1,8 @@ -using System; +// Copyright (c) Files Community +// Licensed under the MIT License. + using System.Runtime.InteropServices; -using Vanara.PInvoke; +using Windows.Win32.System.Com; namespace Files.App.Utils.Shell { @@ -20,12 +22,15 @@ public static IntPtr IShellItemFromPath(string path) public static IntPtr IStreamFromPath(string path) { - IntPtr pstm; - var hr = Win32PInvoke.SHCreateStreamOnFileEx(path, + var hr = Win32PInvoke.SHCreateStreamOnFileEx( + path, STGM.STGM_READ | STGM.STGM_FAILIFTHERE | STGM.STGM_SHARE_DENY_NONE, - 0, 0, IntPtr.Zero, out pstm); + 0, 0, + IntPtr.Zero, out var pstm); + if ((int)hr < 0) return IntPtr.Zero; + return pstm; } diff --git a/src/Files.App/Utils/Shell/LaunchHelper.cs b/src/Files.App/Utils/Shell/LaunchHelper.cs index a47797b7f2c9..634b326a2f4c 100644 --- a/src/Files.App/Utils/Shell/LaunchHelper.cs +++ b/src/Files.App/Utils/Shell/LaunchHelper.cs @@ -1,11 +1,13 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.Shared.Helpers; using Microsoft.Extensions.Logging; using System.IO; using Vanara.PInvoke; using Vanara.Windows.Shell; +using Windows.Win32; +using Windows.Win32.UI.Shell; namespace Files.App.Utils.Shell { @@ -14,14 +16,15 @@ namespace Files.App.Utils.Shell /// public static class LaunchHelper { - public static void LaunchSettings(string page) + public unsafe static void LaunchSettings(string page) { - var appActiveManager = new Shell32.IApplicationActivationManager(); + using ComPtr pApplicationActivationManager = default; + pApplicationActivationManager.CoCreateInstance(CLSID.CLSID_ApplicationActivationManager); - appActiveManager.ActivateApplication( + pApplicationActivationManager.Get()->ActivateApplication( "windows.immersivecontrolpanel_cw5n1h2txyewy!microsoft.windows.immersivecontrolpanel", page, - Shell32.ACTIVATEOPTIONS.AO_NONE, + ACTIVATEOPTIONS.AO_NONE, out _); } @@ -36,7 +39,7 @@ public static Task RunCompatibilityTroubleshooterAsync(string filePath) try { - File.WriteAllText(compatibilityTroubleshooterAnswerFile, string.Format("CompatTab{0}", filePath)); + File.WriteAllText(compatibilityTroubleshooterAnswerFile, $"CompatTab{filePath}"); } catch (IOException) { @@ -44,7 +47,7 @@ public static Task RunCompatibilityTroubleshooterAsync(string filePath) SafetyExtensions.IgnoreExceptions(() => { compatibilityTroubleshooterAnswerFile = Path.Combine(Path.GetTempPath(), "CompatibilityTroubleshooterAnswerFile1.xml"); - File.WriteAllText(compatibilityTroubleshooterAnswerFile, string.Format("CompatTab{0}", filePath)); + File.WriteAllText(compatibilityTroubleshooterAnswerFile, $"CompatTab{filePath}"); }); } @@ -97,6 +100,17 @@ private static async Task HandleApplicationLaunch(string application, stri process.StartInfo.Arguments = arguments; // Refresh env variables for the child process + foreach (DictionaryEntry ent in Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Machine)) + { + string key = (string)ent.Key; + + // Skip USERNAME to avoid issues where files were executed as SYSTEM user (#12139) + if (string.Equals(key, "USERNAME", StringComparison.OrdinalIgnoreCase)) + continue; + + process.StartInfo.EnvironmentVariables[key] = (string)ent.Value; + } + foreach (DictionaryEntry ent in Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User)) process.StartInfo.EnvironmentVariables[(string)ent.Key] = (string)ent.Value; @@ -132,13 +146,13 @@ private static async Task HandleApplicationLaunch(string application, stri catch (Win32Exception ex) when (ex.NativeErrorCode == 50) { // ShellExecute return code 50 (ERROR_NOT_SUPPORTED) for some exes (#15179) - return Win32Helper.RunPowershellCommand($"\"{application}\"", false); + return Win32Helper.RunPowershellCommand($"\"{application}\"", PowerShellExecutionOptions.Hidden); } catch (Win32Exception) { try { - var opened = await Win32Helper.StartSTATask(async () => + var opened = await STATask.Run(async () => { var split = application.Split('|').Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => GetMtpPath(x)); if (split.Count() == 1) @@ -152,7 +166,7 @@ private static async Task HandleApplicationLaunch(string application, stri var groups = split.GroupBy(x => new { Dir = Path.GetDirectoryName(x), - Prog = Win32Helper.GetFileAssociationAsync(x).Result ?? Path.GetExtension(x) + Prog = Win32Helper.GetDefaultFileAssociationAsync(x).Result ?? Path.GetExtension(x) }); foreach (var group in groups) @@ -160,7 +174,7 @@ private static async Task HandleApplicationLaunch(string application, stri if (!group.Any()) continue; - using var cMenu = await ContextMenu.GetContextMenuForFiles(group.ToArray(), Shell32.CMF.CMF_DEFAULTONLY); + using var cMenu = await ContextMenu.GetContextMenuForFiles(group.ToArray(), PInvoke.CMF_DEFAULTONLY); if (cMenu is not null) await cMenu.InvokeVerb(Shell32.CMDSTR_OPEN); @@ -168,21 +182,21 @@ private static async Task HandleApplicationLaunch(string application, stri } return true; - }); + }, App.Logger); if (!opened) { if (application.StartsWith(@"\\SHELL\", StringComparison.Ordinal)) { - opened = await Win32Helper.StartSTATask(async () => + opened = await STATask.Run(async () => { - using var cMenu = await ContextMenu.GetContextMenuForFiles(new[] { application }, Shell32.CMF.CMF_DEFAULTONLY); + using var cMenu = await ContextMenu.GetContextMenuForFiles(new[] { application }, PInvoke.CMF_DEFAULTONLY); if (cMenu is not null) await cMenu.InvokeItem(cMenu.Items.FirstOrDefault()?.ID ?? -1); return true; - }); + }, App.Logger); } } @@ -201,8 +215,8 @@ private static async Task HandleApplicationLaunch(string application, stri if (!hFileSrc.IsInvalid && !hFileDst.IsInvalid) { // Copy ADS to temp folder and open - using (var inStream = new FileStream(hFileSrc.DangerousGetHandle(), FileAccess.Read)) - using (var outStream = new FileStream(hFileDst.DangerousGetHandle(), FileAccess.Write)) + await using (var inStream = new FileStream(hFileSrc.DangerousGetHandle(), FileAccess.Read)) + await using (var outStream = new FileStream(hFileDst.DangerousGetHandle(), FileAccess.Write)) { await inStream.CopyToAsync(outStream); await outStream.FlushAsync(); diff --git a/src/Files.App/Utils/Shell/PreviewHandler.cs b/src/Files.App/Utils/Shell/PreviewHandler.cs index 6cb5f4001798..abb49b7d518a 100644 --- a/src/Files.App/Utils/Shell/PreviewHandler.cs +++ b/src/Files.App/Utils/Shell/PreviewHandler.cs @@ -1,17 +1,30 @@ -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; -using Vanara.PInvoke; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; using Windows.UI; +using Windows.Win32.Foundation; +using Windows.Win32.System.Com; namespace Files.App.Utils.Shell { /// /// Credits: https://github.com/GeeLaw/PreviewHost/ /// - public sealed class PreviewHandler : IDisposable + public sealed partial class PreviewHandler : IDisposable { + const int S_OK = 0; + const int S_FALSE = 1; + const int E_FAIL = unchecked((int)0x80004005); + const int E_SERVER_EXEC_FAILURE = unchecked((int)0x80080005); + + [StructLayout(LayoutKind.Sequential)] + public struct RECT(int left, int top, int right, int bottom) + { + public int Left = left; + public int Top = top; + public int Right = right; + public int Bottom = bottom; + } + #region IPreviewHandlerFrame support [StructLayout(LayoutKind.Sequential)] @@ -21,16 +34,17 @@ public struct PreviewHandlerFrameInfo public uint AcceleratorEntryCount; } - [ComImport, Guid("fec87aaf-35f9-447a-adb7-20234491401a"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - public interface IPreviewHandlerFrame + [GeneratedComInterface, Guid("fec87aaf-35f9-447a-adb7-20234491401a"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public partial interface IPreviewHandlerFrame { [PreserveSig] - HRESULT GetWindowContext(out PreviewHandlerFrameInfo pinfo); + int GetWindowContext(out PreviewHandlerFrameInfo pinfo); [PreserveSig] - HRESULT TranslateAccelerator(ref MSG pmsg); + int TranslateAccelerator(nint pmsg); } - public sealed class PreviewHandlerFrame : IPreviewHandlerFrame, IDisposable + [GeneratedComClass] + public sealed partial class PreviewHandlerFrame : IPreviewHandlerFrame, IDisposable { bool disposed; nint hwnd; @@ -47,20 +61,20 @@ public void Dispose() disposed = true; } - public HRESULT GetWindowContext(out PreviewHandlerFrameInfo pinfo) + public int GetWindowContext(out PreviewHandlerFrameInfo pinfo) { pinfo.AcceleratorTableHandle = IntPtr.Zero; pinfo.AcceleratorEntryCount = 0; if (disposed) - return HRESULT.E_FAIL; - return HRESULT.S_OK; + return E_FAIL; + return S_OK; } - public HRESULT TranslateAccelerator(ref MSG pmsg) + public int TranslateAccelerator(nint pmsg) { if (disposed) - return HRESULT.E_FAIL; - return HRESULT.S_FALSE; + return E_FAIL; + return S_FALSE; } } @@ -68,33 +82,33 @@ public HRESULT TranslateAccelerator(ref MSG pmsg) #region IPreviewHandler major interfaces - [ComImport, Guid("8895b1c6-b41f-4c1c-a562-0d564250836f"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - interface IPreviewHandler + [GeneratedComInterface, Guid("8895b1c6-b41f-4c1c-a562-0d564250836f"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal partial interface IPreviewHandler { [PreserveSig] - HRESULT SetWindow(IntPtr hwnd, ref RECT prc); + int SetWindow(nint hwnd, in RECT prc); [PreserveSig] - HRESULT SetRect(ref RECT prc); + int SetRect(in RECT prc); [PreserveSig] - HRESULT DoPreview(); + int DoPreview(); [PreserveSig] - HRESULT Unload(); + int Unload(); [PreserveSig] - HRESULT SetFocus(); + int SetFocus(); [PreserveSig] - HRESULT QueryFocus(out IntPtr phwnd); + int QueryFocus(out nint phwnd); // TranslateAccelerator is not used here. } - [ComImport, Guid("196bf9a5-b346-4ef0-aa1e-5dcdb76768b1"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - interface IPreviewHandlerVisuals + [GeneratedComInterface, Guid("196bf9a5-b346-4ef0-aa1e-5dcdb76768b1"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal partial interface IPreviewHandlerVisuals { [PreserveSig] - HRESULT SetBackgroundColor(uint color); + int SetBackgroundColor(uint color); [PreserveSig] - HRESULT SetFont(ref LOGFONT plf); + int SetFont(nint plf); [PreserveSig] - HRESULT SetTextColor(uint color); + int SetTextColor(uint color); } static uint ColorRefFromColor(Color color) @@ -102,11 +116,11 @@ static uint ColorRefFromColor(Color color) return (((uint)color.B) << 16) | (((uint)color.G) << 8) | ((uint)color.R); } - [ComImport, Guid("fc4801a3-2ba9-11cf-a229-00aa003d7352"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - interface IObjectWithSite + [GeneratedComInterface, Guid("fc4801a3-2ba9-11cf-a229-00aa003d7352"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal partial interface IObjectWithSite { [PreserveSig] - HRESULT SetSite([In, MarshalAs(UnmanagedType.IUnknown)] object pUnkSite); + int SetSite(nint pUnkSite); // GetSite is not used. } @@ -115,11 +129,11 @@ interface IObjectWithSite bool disposed; bool init; bool shown; - PreviewHandlerFrame comSite; + PreviewHandlerFrame? comSite; nint hwnd; - IPreviewHandler previewHandler; - IPreviewHandlerVisuals visuals; - IntPtr pPreviewHandler; + IPreviewHandler? previewHandler; + IPreviewHandlerVisuals? visuals; + readonly ComWrappers comWrappers = new StrategyBasedComWrappers(); public PreviewHandler(Guid clsid, nint frame) { @@ -135,12 +149,7 @@ public PreviewHandler(Guid clsid, nint frame) } catch { - if (previewHandler != null) - Marshal.ReleaseComObject(previewHandler); previewHandler = null; - if (pPreviewHandler != IntPtr.Zero) - Marshal.Release(pPreviewHandler); - pPreviewHandler = IntPtr.Zero; comSite.Dispose(); comSite = null; throw; @@ -160,65 +169,81 @@ void SetupHandler(Guid clsid) // If we use Activator.CreateInstance(Type.GetTypeFromCLSID(...)), // CLR will allow in-process server, which defeats isolation and // creates strange bugs. - HRESULT hr = Win32PInvoke.CoCreateInstance(ref clsid, IntPtr.Zero, Win32PInvoke.ClassContext.LocalServer, ref iid, out pph); + int hr = Win32PInvoke.CoCreateInstance(ref clsid, IntPtr.Zero, Win32PInvoke.ClassContext.LocalServer, ref iid, out pph); // See https://blogs.msdn.microsoft.com/adioltean/2005/06/24/when-cocreateinstance-returns-0x80080005-co_e_server_exec_failure/ // CO_E_SERVER_EXEC_FAILURE also tends to happen when debugging in Visual Studio. // Moreover, to create the instance in a server at low integrity level, we need // to use another thread with low mandatory label. We keep it simple by creating // a same-integrity object. - //if (hr == HRESULT.CO_E_SERVER_EXEC_FAILURE) - // hr = CoCreateInstance(ref clsid, IntPtr.Zero, ClassContext.LocalServer, ref iid, out pph); - if ((int)hr < 0) - throw new COMException(cannotCreate, (int)hr); - pPreviewHandler = pph; - var previewHandlerObject = Marshal.GetUniqueObjectForIUnknown(pph); + if (hr == E_SERVER_EXEC_FAILURE) + hr = Win32PInvoke.CoCreateInstance(ref clsid, IntPtr.Zero, Win32PInvoke.ClassContext.LocalServer, ref iid, out pph); + if (hr < 0) + throw new COMException(cannotCreate, hr); + var previewHandlerObject = comWrappers.GetOrCreateObjectForComInstance(pph, CreateObjectFlags.UniqueInstance); previewHandler = previewHandlerObject as IPreviewHandler; + if (previewHandler == null) { - Marshal.ReleaseComObject(previewHandlerObject); throw new COMException(cannotCreate); } var objectWithSite = previewHandlerObject as IObjectWithSite; if (objectWithSite == null) throw new COMException(cannotCast); - hr = objectWithSite.SetSite(comSite); - if ((int)hr < 0) - throw new COMException(cannotSetSite, (int)hr); + hr = objectWithSite.SetSite(comWrappers.GetOrCreateComInterfaceForObject(comSite, CreateComInterfaceFlags.None)); + if (hr < 0) + throw new COMException(cannotSetSite, hr); visuals = previewHandlerObject as IPreviewHandlerVisuals; } #region Initialization interfaces - [ComImport, Guid("b824b49d-22ac-4161-ac8a-9916e8fa3f7f"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - interface IInitializeWithStream + [GeneratedComInterface, Guid("0000000c-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public partial interface IStream + { + // ISequentialStream portion + void Read([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1), Out] byte[] pv, int cb, nint pcbRead); + void Write([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] pv, int cb, nint pcbWritten); + + // IStream portion + void Seek(long dlibMove, int dwOrigin, nint plibNewPosition); + void SetSize(long libNewSize); + void CopyTo(IStream pstm, long cb, nint pcbRead, nint pcbWritten); + void Commit(int grfCommitFlags); + void Revert(); + void LockRegion(long libOffset, long cb, int dwLockType); + void UnlockRegion(long libOffset, long cb, int dwLockType); + } + + [GeneratedComInterface, Guid("b824b49d-22ac-4161-ac8a-9916e8fa3f7f"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal partial interface IInitializeWithStream { [PreserveSig] - HRESULT Initialize(IStream psi, STGM grfMode); + int Initialize(IStream psi, STGM grfMode); } - [ComImport, Guid("b824b49d-22ac-4161-ac8a-9916e8fa3f7f"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - interface IInitializeWithStreamNative + [GeneratedComInterface, Guid("b824b49d-22ac-4161-ac8a-9916e8fa3f7f"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal partial interface IInitializeWithStreamNative { [PreserveSig] - HRESULT Initialize(IntPtr psi, STGM grfMode); + int Initialize(IntPtr psi, STGM grfMode); } static readonly Guid IInitializeWithStreamIid = Guid.ParseExact("b824b49d-22ac-4161-ac8a-9916e8fa3f7f", "d"); - [ComImport, Guid("b7d14566-0509-4cce-a71f-0a554233bd9b"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - interface IInitializeWithFile + [GeneratedComInterface, Guid("b7d14566-0509-4cce-a71f-0a554233bd9b"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal partial interface IInitializeWithFile { [PreserveSig] - HRESULT Initialize([MarshalAs(UnmanagedType.LPWStr)] string pszFilePath, STGM grfMode); + int Initialize([MarshalAs(UnmanagedType.LPWStr)] string pszFilePath, STGM grfMode); } static readonly Guid IInitializeWithFileIid = Guid.ParseExact("b7d14566-0509-4cce-a71f-0a554233bd9b", "d"); - [ComImport, Guid("7f73be3f-fb79-493c-a6c7-7ee14e245841"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - interface IInitializeWithItem + [GeneratedComInterface, Guid("7f73be3f-fb79-493c-a6c7-7ee14e245841"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal partial interface IInitializeWithItem { [PreserveSig] - HRESULT Initialize(IntPtr psi, STGM grfMode); + int Initialize(IntPtr psi, STGM grfMode); } static readonly Guid IInitializeWithItemIid = Guid.ParseExact("7f73be3f-fb79-493c-a6c7-7ee14e245841", "d"); @@ -241,10 +266,10 @@ public bool InitWithStream(IStream stream, STGM mode) if (iws == null) return false; var hr = iws.Initialize(stream, mode); - if (hr == HRESULT.E_NOTIMPL) + if (hr == (int)HRESULT.E_NOTIMPL) return false; - if ((int)hr < 0) - throw new COMException("IInitializeWithStream.Initialize failed.", (int)hr); + if (hr < 0) + throw new COMException("IInitializeWithStream.Initialize failed.", hr); init = true; return true; } @@ -267,10 +292,10 @@ public bool InitWithStream(IntPtr pStream, STGM mode) if (iws == null) return false; var hr = iws.Initialize(pStream, mode); - if (hr == HRESULT.E_NOTIMPL) + if (hr == (int)HRESULT.E_NOTIMPL) return false; - if ((int)hr < 0) - throw new COMException("IInitializeWithStream.Initialize failed.", (int)hr); + if (hr < 0) + throw new COMException("IInitializeWithStream.Initialize failed.", hr); init = true; return true; } @@ -293,10 +318,10 @@ public bool InitWithItem(IntPtr psi, STGM mode) if (iwi == null) return false; var hr = iwi.Initialize(psi, mode); - if (hr == HRESULT.E_NOTIMPL) + if (hr == (int)HRESULT.E_NOTIMPL) return false; - if ((int)hr < 0) - throw new COMException("IInitializeWithItem.Initialize failed.", (int)hr); + if (hr < 0) + throw new COMException("IInitializeWithItem.Initialize failed.", hr); init = true; return true; } @@ -319,10 +344,10 @@ public bool InitWithFile(string path, STGM mode) if (iwf == null) return false; var hr = iwf.Initialize(path, mode); - if (hr == HRESULT.E_NOTIMPL) + if (hr == (int)HRESULT.E_NOTIMPL) return false; - if ((int)hr < 0) - throw new COMException("IInitializeWithFile.Initialize failed.", (int)hr); + if (hr < 0) + throw new COMException("IInitializeWithFile.Initialize failed.", hr); init = true; return true; } @@ -401,7 +426,7 @@ public bool ResetWindow() if (!init) return false; var hr = previewHandler.SetWindow(hwnd, new()); - return (int)hr >= 0; + return hr >= 0; } /// @@ -414,7 +439,7 @@ public bool ResetBounds(RECT previewerBounds) if (!init) return false; var hr = previewHandler.SetRect(previewerBounds); - return (int)hr >= 0; + return hr >= 0; } /// @@ -425,7 +450,7 @@ public bool ResetBounds(RECT previewerBounds) public bool SetBackground(Color color) { var hr = visuals?.SetBackgroundColor(ColorRefFromColor(color)); - return hr.HasValue && (int)hr.Value >= 0; + return hr.HasValue && hr.Value >= 0; } /// @@ -436,7 +461,7 @@ public bool SetBackground(Color color) public bool SetForeground(Color color) { var hr = visuals?.SetTextColor(ColorRefFromColor(color)); - return hr.HasValue && (int)hr.Value >= 0; + return hr.HasValue && hr.Value >= 0; } /// @@ -444,10 +469,10 @@ public bool SetForeground(Color color) /// /// The LogFontW reference. /// Whether the call succeeds. - public bool SetFont(ref LOGFONT font) + public bool SetFont(nint font) { - var hr = visuals?.SetFont(ref font); - return hr.HasValue && (int)hr.Value >= 0; + var hr = visuals?.SetFont(font); + return hr.HasValue && hr.Value >= 0; } /// @@ -461,7 +486,7 @@ public void DoPreview() return; EnsureNotShown(); ResetWindow(); - previewHandler.DoPreview(); + previewHandler?.DoPreview(); shown = true; } @@ -475,35 +500,27 @@ public void Focus() if (!init) return; EnsureShown(); - previewHandler.SetFocus(); + previewHandler?.SetFocus(); } /// /// Tells the preview handler to query focus. /// /// The focused window. - public IntPtr QueryFocus() + public nint QueryFocus() { EnsureNotDisposed(); //EnsureInitialized(); if (!init) return IntPtr.Zero; EnsureShown(); - IntPtr result; + nint result; var hr = previewHandler.QueryFocus(out result); - if ((int)hr < 0) + if (hr < 0) return IntPtr.Zero; return result; } - /// - /// Unloads the preview and disposes the object. This method is idempotent. - /// - public void UnloadPreview() - { - Dispose(true); - } - void EnsureNotDisposed() { if (disposed) @@ -536,44 +553,25 @@ void EnsureNotShown() #region IDisposable pattern - void Dispose(bool disposing) + ~PreviewHandler() + { + Dispose(); + } + + public void Dispose() { if (disposed) return; disposed = true; init = false; - if (disposing) - { - previewHandler.Unload(); - comSite.Dispose(); - Marshal.ReleaseComObject(previewHandler); - } - else - { - // We're in the finalizer. - // Field previewHandler might have been finalized at this point. - // Get a new RCW. - var phObject = Marshal.GetUniqueObjectForIUnknown(pPreviewHandler); - var ph = phObject as IPreviewHandler; - if (ph != null) - ph.Unload(); - Marshal.ReleaseComObject(phObject); - } - Marshal.Release(pPreviewHandler); - } - ~PreviewHandler() - { - Dispose(false); - } + previewHandler?.Unload(); + comSite?.Dispose(); - void IDisposable.Dispose() - { - Dispose(true); GC.SuppressFinalize(this); } #endregion } -} +} \ No newline at end of file diff --git a/src/Files.App/Utils/Shell/ShellFileOperations2.cs b/src/Files.App/Utils/Shell/ShellFileOperations2.cs index 47c1bcf65f20..05fba030f96a 100644 --- a/src/Files.App/Utils/Shell/ShellFileOperations2.cs +++ b/src/Files.App/Utils/Shell/ShellFileOperations2.cs @@ -8,10 +8,10 @@ namespace Vanara.Windows.Shell; /// Queued and static file operations using the Shell. /// /// https://github.com/dahall/Vanara/blob/master/Windows.Shell.Common/ShellFileOperations/ShellFileOperations.cs -public class ShellFileOperations2 : IDisposable +public partial class ShellFileOperations2 : IDisposable { private const OperationFlags defaultOptions = OperationFlags.AllowUndo | OperationFlags.NoConfirmMkDir; - private bool disposedValue = false; + private int disposedValue = 0; private IFileOperation op; private OperationFlags opFlags = defaultOptions; private HWND owner; @@ -569,20 +569,19 @@ public void QueueRenameOperation(IEnumerable sourceItems, string newN /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { - if (!disposedValue) + if (Interlocked.CompareExchange(ref disposedValue, 1, 0) == 0) { if (disposing) { // Dispose managed state (managed objects). } - if (sink != null) + if (sink != null && op != null) { op.Unadvise(sinkCookie); } op = null; - disposedValue = true; } } @@ -601,25 +600,25 @@ private sealed class OpSink : IFileOperationProgressSink public HRESULT PostCopyItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName, HRESULT hrCopy, IShellItem psiNewlyCreated) => CallChkErr(() => parent.PostCopyItem?.Invoke(parent, new ShellFileOpEventArgs(dwFlags, psiItem, psiDestinationFolder, psiNewlyCreated, pszNewName, hrCopy))); - public HRESULT PostDeleteItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem, HRESULT hrDelete, IShellItem psiNewlyCreated) => + public HRESULT PostDeleteItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem, HRESULT hrDelete, IShellItem? psiNewlyCreated) => CallChkErr(() => parent.PostDeleteItem?.Invoke(parent, new ShellFileOpEventArgs(dwFlags, psiItem, null, psiNewlyCreated, null, hrDelete))); public HRESULT PostMoveItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName, HRESULT hrMove, IShellItem psiNewlyCreated) => CallChkErr(() => parent.PostMoveItem?.Invoke(parent, new ShellFileOpEventArgs(dwFlags, psiItem, psiDestinationFolder, psiNewlyCreated, pszNewName, hrMove))); - public HRESULT PostNewItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName, [MarshalAs(UnmanagedType.LPWStr)] string pszTemplateName, uint dwFileAttributes, HRESULT hrNew, IShellItem psiNewItem) => + public HRESULT PostNewItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName, [MarshalAs(UnmanagedType.LPWStr)] string? pszTemplateName, uint dwFileAttributes, HRESULT hrNew, IShellItem psiNewItem) => CallChkErr(() => parent.PostNewItem?.Invoke(parent, new ShellFileNewOpEventArgs(dwFlags, null, psiDestinationFolder, psiNewItem, pszNewName, hrNew, pszTemplateName, dwFileAttributes))); public HRESULT PostRenameItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName, HRESULT hrRename, IShellItem psiNewlyCreated) => CallChkErr(() => parent.PostRenameItem?.Invoke(parent, new ShellFileOpEventArgs(dwFlags, psiItem, null, psiNewlyCreated, pszNewName, hrRename))); - public HRESULT PreCopyItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName) => + public HRESULT PreCopyItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string? pszNewName) => CallChkErr(() => parent.PreCopyItem?.Invoke(parent, new ShellFileOpEventArgs(dwFlags, psiItem, psiDestinationFolder, null, pszNewName))); public HRESULT PreDeleteItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem) => CallChkErr(() => parent.PreDeleteItem?.Invoke(parent, new ShellFileOpEventArgs(dwFlags, psiItem))); - public HRESULT PreMoveItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName) => + public HRESULT PreMoveItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string? pszNewName) => CallChkErr(() => parent.PreMoveItem?.Invoke(parent, new ShellFileOpEventArgs(dwFlags, psiItem, psiDestinationFolder, null, pszNewName))); public HRESULT PreNewItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName) => @@ -661,11 +660,11 @@ internal ShellFileNewOpEventArgs(TRANSFER_SOURCE_FLAGS flags, IShellItem source, /// Gets the name of the template. /// The name of the template. - public string TemplateName { get; protected set; } + public string TemplateName { get; private set; } /// Gets the file attributes. /// The file attributes. - public System.IO.FileAttributes FileAttributes { get; protected set; } + public System.IO.FileAttributes FileAttributes { get; private set; } } /// diff --git a/src/Files.App/Utils/Shell/ShellFolderExtensions.cs b/src/Files.App/Utils/Shell/ShellFolderExtensions.cs index 4a468d660872..d9c8dbdadbd8 100644 --- a/src/Files.App/Utils/Shell/ShellFolderExtensions.cs +++ b/src/Files.App/Utils/Shell/ShellFolderExtensions.cs @@ -1,7 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using System; using System.IO; using Vanara.PInvoke; using Vanara.Windows.Shell; @@ -72,7 +71,7 @@ public static ShellFileItem GetShellFileItem(ShellItem folderItem) "::{031E4825-7B94-4DC3-B131-E946B44C8DD5}\\Videos.library-ms" => ShellHelpers.GetLibraryFullPathFromShell(parsingPath), // Use PIDL as path // Replace "/" with "_" to avoid confusion with path separator - _ => $@"\\SHELL\{string.Join("\\", folderItem.PIDL.Select(x => x.GetBytes()).Select(x => Convert.ToBase64String(x, 0, x.Length).Replace("/", "_")))}" + _ => $@"\\SHELL\{string.Join("\\", folderItem.PIDL.Select(x => x.GetBytes()).Select(x => Convert.ToBase64String(x, 0, x.Length).Replace('/', '_')))}" }; } @@ -127,6 +126,7 @@ public static ShellLinkItem GetShellLinkItem(ShellLink linkItem) { IsFolder = !string.IsNullOrEmpty(linkItem.TargetPath) && linkItem.Target.IsFolder, RunAsAdmin = linkItem.RunAsAdministrator, + ShowWindowCommand = (Windows.Win32.UI.WindowsAndMessaging.SHOW_WINDOW_CMD)linkItem.ShowState, Arguments = linkItem.Arguments, WorkingDirectory = Environment.ExpandEnvironmentVariables(linkItem.WorkingDirectory), TargetPath = Environment.ExpandEnvironmentVariables(linkItem.TargetPath) @@ -149,7 +149,7 @@ public static bool GetStringAsPIDL(string pathOrPIDL, out Shell32.PIDL pPIDL) { pPIDL = pathOrPIDL.Replace(@"\\SHELL\", "", StringComparison.Ordinal) // Avoid confusion with path separator - .Replace("_", "/") + .Replace('_', '/') .Split('\\', StringSplitOptions.RemoveEmptyEntries) .Select(pathSegment => new Shell32.PIDL(Convert.FromBase64String(pathSegment))) .Aggregate(Shell32.PIDL.Combine); diff --git a/src/Files.App/Utils/Shell/ShellHelpers.cs b/src/Files.App/Utils/Shell/ShellHelpers.cs index 99b92caf3847..2f3dc7f107d6 100644 --- a/src/Files.App/Utils/Shell/ShellHelpers.cs +++ b/src/Files.App/Utils/Shell/ShellHelpers.cs @@ -1,9 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.App.Extensions; -using Files.Shared; -using System; using System.IO; namespace Files.App.Utils.Shell @@ -28,10 +25,12 @@ public static string GetShellNameFromPath(string shPath) { return shPath switch { - "Home" => "Home".GetLocalizedResource(), - Constants.UserEnvironmentPaths.RecycleBinPath => "RecycleBin".GetLocalizedResource(), - Constants.UserEnvironmentPaths.NetworkFolderPath => "Network".GetLocalizedResource(), - Constants.UserEnvironmentPaths.MyComputerPath => "ThisPC".GetLocalizedResource(), + "Home" => Strings.Home.GetLocalizedResource(), + "ReleaseNotes" => Strings.ReleaseNotes.GetLocalizedResource(), + "Settings" => Strings.Settings.GetLocalizedResource(), + Constants.UserEnvironmentPaths.RecycleBinPath => Strings.RecycleBin.GetLocalizedResource(), + Constants.UserEnvironmentPaths.NetworkFolderPath => Strings.Network.GetLocalizedResource(), + Constants.UserEnvironmentPaths.MyComputerPath => Strings.ThisPC.GetLocalizedResource(), _ => shPath }; } diff --git a/src/Files.App/Utils/Shell/ShellLibraryEx.cs b/src/Files.App/Utils/Shell/ShellLibraryEx.cs index 975dd1e6b1bc..0ef9ed8872f3 100644 --- a/src/Files.App/Utils/Shell/ShellLibraryEx.cs +++ b/src/Files.App/Utils/Shell/ShellLibraryEx.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Windows.Forms; using Vanara.Extensions; @@ -11,7 +11,7 @@ namespace Files.App.Utils.Shell /// /// Represents an encapsulated item for shell library. /// - public sealed class ShellLibraryEx : ShellFolder + public sealed partial class ShellLibraryEx : ShellFolder { //private const string ext = ".library-ms"; diff --git a/src/Files.App/Utils/Shell/ShellLibraryFolders.cs b/src/Files.App/Utils/Shell/ShellLibraryFolders.cs index b7f49066eb1c..ee33126d09c3 100644 --- a/src/Files.App/Utils/Shell/ShellLibraryFolders.cs +++ b/src/Files.App/Utils/Shell/ShellLibraryFolders.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Vanara.PInvoke; using Vanara.Windows.Shell; @@ -11,7 +11,7 @@ namespace Files.App.Utils.Shell /// /// /// - public sealed class ShellLibraryFolders : ShellItemArray, ICollection + public sealed partial class ShellLibraryFolders : ShellItemArray, ICollection { private Shell32.IShellLibrary _lib; @@ -38,8 +38,7 @@ bool ICollection.IsReadOnly /// location public void Add(ShellItem location) { - if (location is null) - throw new ArgumentNullException(nameof(location)); + ArgumentNullException.ThrowIfNull(location); _lib.AddFolder(location.IShellItem); } @@ -52,8 +51,7 @@ public void Add(ShellItem location) /// location public bool Remove(ShellItem location) { - if (location is null) - throw new ArgumentNullException(nameof(location)); + ArgumentNullException.ThrowIfNull(location); try { diff --git a/src/Files.App/Utils/Shell/ShellNewMenuHelper.cs b/src/Files.App/Utils/Shell/ShellNewMenuHelper.cs index 1e1673a344c9..0e49e2332c3f 100644 --- a/src/Files.App/Utils/Shell/ShellNewMenuHelper.cs +++ b/src/Files.App/Utils/Shell/ShellNewMenuHelper.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.Win32; using System.IO; @@ -69,7 +69,8 @@ private static async Task GetShellNewRegistryEntries(RegistryKey } } } - catch { + catch + { // Ignore exceptions when the registry is inaccessible to avoid freezes } @@ -115,7 +116,7 @@ private static async Task CreateShellNewEntry(string extension, s var folder = await SafetyExtensions.IgnoreExceptions(() => ApplicationData.Current.LocalFolder.CreateFolderAsync("extensions", CreationCollisionOption.OpenIfExists).AsTask()); var sampleFile = folder is not null ? await SafetyExtensions.IgnoreExceptions(() => folder.CreateFileAsync("file" + extension, CreationCollisionOption.OpenIfExists).AsTask()) : null; - var displayType = sampleFile is not null ? sampleFile.DisplayType : string.Format("{0} {1}", "file", extension); + var displayType = sampleFile is not null ? sampleFile.DisplayType : $"file {extension}"; var thumbnail = sampleFile is not null ? await SafetyExtensions.IgnoreExceptions(() => sampleFile.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode.ListView, 24, Windows.Storage.FileProperties.ThumbnailOptions.UseCurrentScale).AsTask()) : null; string iconString = null; @@ -124,7 +125,7 @@ private static async Task CreateShellNewEntry(string extension, s { var readStream = thumbnail.AsStreamForRead(); var bitmapData = new byte[readStream.Length]; - await readStream.ReadAsync(bitmapData, 0, bitmapData.Length); + await readStream.ReadExactlyAsync(bitmapData, 0, bitmapData.Length); iconString = Convert.ToBase64String(bitmapData, 0, bitmapData.Length); } diff --git a/src/Files.App/Utils/Shell/ThreadWithMessageQueue.cs b/src/Files.App/Utils/Shell/ThreadWithMessageQueue.cs index e5894fb692cd..755fb4c3229d 100644 --- a/src/Files.App/Utils/Shell/ThreadWithMessageQueue.cs +++ b/src/Files.App/Utils/Shell/ThreadWithMessageQueue.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Collections.Concurrent; namespace Files.App.Utils.Shell { - public sealed class ThreadWithMessageQueue : Disposable + public sealed partial class ThreadWithMessageQueue : Disposable { private readonly BlockingCollection messageQueue; diff --git a/src/Files.App/Utils/Signatures/DigitalSignaturesUtil.cs b/src/Files.App/Utils/Signatures/DigitalSignaturesUtil.cs new file mode 100644 index 000000000000..f4bb020f4b0c --- /dev/null +++ b/src/Files.App/Utils/Signatures/DigitalSignaturesUtil.cs @@ -0,0 +1,993 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.Security.Cryptography; +using Windows.Win32.Security.WinTrust; +using static Files.App.Helpers.Win32PInvoke; + +namespace Files.App.Utils.Signatures +{ + public static class DigitalSignaturesUtil + { + // OIDs + private const string szOID_NESTED_SIGNATURE = "1.3.6.1.4.1.311.2.4.1"; + private const string szOID_RSA_counterSign = "1.2.840.113549.1.9.6"; + private const string szOID_RSA_signingTime = "1.2.840.113549.1.9.5"; + private const string szOID_RFC3161_counterSign = "1.3.6.1.4.1.311.3.3.1"; + private const string szOID_OIWSEC_sha1 = "1.3.14.3.2.26"; + private const string szOID_RSA_MD5 = "1.2.840.113549.2.5"; + private const string szOID_NIST_sha256 = "2.16.840.1.101.3.4.2.1"; + + // Flags + private const uint CERT_NAME_SIMPLE_DISPLAY_TYPE = 4; + private const uint CERT_SYSTEM_STORE_CURRENT_USER = 0x00010000; + private const uint PKCS_7_ASN_ENCODING = 0x00010000; + private const uint CRYPT_ASN_ENCODING = 0x00000001; + private const CERT_QUERY_ENCODING_TYPE ENCODING = + CERT_QUERY_ENCODING_TYPE.X509_ASN_ENCODING | CERT_QUERY_ENCODING_TYPE.PKCS_7_ASN_ENCODING; + + private const uint CMSG_SIGNER_INFO_PARAM = 6; + + // Version numbers + private const uint CERT_V1 = 0; + private const uint CERT_V2 = 1; + private const uint CERT_V3 = 2; + + private static readonly byte[] SG_ProtoCoded = [ + 0x30, 0x82 + ]; + + private static readonly byte[] SG_SignedData = [ + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 + ]; + + private static readonly IDateTimeFormatter formatter = Ioc.Default.GetRequiredService(); + + public static void LoadItemSignatures( + string filePath, + ObservableCollection signatures, + HWND hWnd, + CancellationToken ct) + { + var signChain = new List(); + GetSignerCertificateInfo(filePath, signChain, ct); + + foreach (var signNode in signChain) + { + if (signNode.CertChain.Count == 0) + continue; + + var signatureInfo = new SignatureInfoItem(filePath, signNode.Index, hWnd, signNode.CertChain) + { + Version = signNode.Version, + IssuedBy = signNode.CertChain[0].IssuedBy, + IssuedTo = signNode.CertChain[0].IssuedTo, + ValidFromTimestamp = signNode.CertChain[0].ValidFrom, + ValidToTimestamp = signNode.CertChain[0].ValidTo, + VerifiedTimestamp = signNode.CounterSign.TimeStamp, + Verified = signNode.IsValid, + }; + signatures.Add(signatureInfo); + } + } + + public unsafe static void DisplaySignerInfoDialog(string filePath, HWND hwndParent, int index) + { + if (string.IsNullOrEmpty(filePath)) + return; + + void* hAuthCryptMsg = null; + var signHandle = new SignDataHandle(); + var signDataChain = new List(); + + try + { + var result = TryGetSignerInfo( + filePath, + out hAuthCryptMsg, + out signHandle.hCertStoreHandle, + out signHandle.pSignerInfo, + out signHandle.dwObjSize + ); + if (!result || signHandle.pSignerInfo is null) + return; + + signDataChain.Add(signHandle); + GetNestedSignerInfo(ref signHandle, signDataChain); + if (index >= signDataChain.Count) + return; + + signHandle = signDataChain[index]; + var issuer = signHandle.pSignerInfo->Issuer; + var pCertContext = PInvoke.CertFindCertificateInStore( + signHandle.hCertStoreHandle, + ENCODING, + 0, + CERT_FIND_FLAGS.CERT_FIND_ISSUER_NAME, + &issuer, + null + ); + if (pCertContext is null) + return; + + var viewInfo = new CRYPTUI_VIEWSIGNERINFO_STRUCT + { + dwSize = (uint)Marshal.SizeOf(), + hwndParent = hwndParent, + dwFlags = 0, + szTitle = (PCSTR)null, + pSignerInfo = signHandle.pSignerInfo, + hMsg = hAuthCryptMsg, + pszOID = (PCSTR)null, + dwReserved = null, + cStores = 1, + rghStores = (HCERTSTORE*)NativeMemory.Alloc((uint)sizeof(void*)), + cPropPages = 0, + rgPropPages = null + }; + *(viewInfo.rghStores) = signHandle.hCertStoreHandle; + + result = CryptUIDlgViewSignerInfo(&viewInfo); + + PInvoke.CertFreeCertificateContext(pCertContext); + } + finally + { + // Since signDataChain contains nested signatures, + // you must release them starting from the last one. + for (int i = signDataChain.Count - 1; i >= 0; i--) + { + if (signDataChain[i].pSignerInfo is not null) + NativeMemory.Free(signDataChain[i].pSignerInfo); + + if (!signDataChain[i].hCertStoreHandle.IsNull) + PInvoke.CertCloseStore(signDataChain[i].hCertStoreHandle, 0); + } + + if (hAuthCryptMsg is not null) + PInvoke.CryptMsgClose(hAuthCryptMsg); + } + } + + private unsafe static bool GetSignerSignatureInfo( + HCERTSTORE hSystemStore, + HCERTSTORE hCertStore, + CERT_CONTEXT* pOrigContext, + ref CERT_CONTEXT* pCurrContext, + SignNodeInfo signNode) + { + var pCertInfo = pCurrContext->pCertInfo; + var certNode = new CertNodeInfoItem(); + + (_, certNode.Version) = CalculateSignVersion(pCertInfo->dwVersion); + GetStringFromCertContext(pCurrContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, certNode); + GetStringFromCertContext(pCurrContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 1, certNode); + + var pft = &(pCertInfo->NotBefore); + certNode.ValidFrom = TimeToString(pft); + pft = &(pCertInfo->NotAfter); + certNode.ValidTo = TimeToString(pft); + + signNode.CertChain.Add(certNode); + + pCurrContext = PInvoke.CertFindCertificateInStore( + hCertStore, + ENCODING, + 0, + CERT_FIND_FLAGS.CERT_FIND_SUBJECT_NAME, + &(pCertInfo->Issuer), + null + ); + + if (pCurrContext is null) + { + pCurrContext = PInvoke.CertFindCertificateInStore( + hSystemStore, + ENCODING, + 0, + CERT_FIND_FLAGS.CERT_FIND_SUBJECT_NAME, + &(pCertInfo->Issuer), + null + ); + } + + if (pCurrContext is null) + return false; + + var result = PInvoke.CertComparePublicKeyInfo( + ENCODING, + &pCurrContext->pCertInfo->SubjectPublicKeyInfo, + &pOrigContext->pCertInfo->SubjectPublicKeyInfo + ); + + return !result; + } + + private unsafe static bool GetSignerCertificateInfo(string fileName, List signChain, CancellationToken ct) + { + var succeded = false; + var authSignData = new SignDataHandle() { dwObjSize = 0, hCertStoreHandle = HCERTSTORE.Null, pSignerInfo = null }; + var signDataChain = new List(); + signChain.Clear(); + + var cert_store_prov_system = (PCSTR)(byte*)10; + HCERTSTORE hSystemStore; + fixed (char* pRoot = "Root") + { + hSystemStore = PInvoke.CertOpenStore( + cert_store_prov_system, + ENCODING, + HCRYPTPROV_LEGACY.Null, + (CERT_OPEN_STORE_FLAGS)CERT_SYSTEM_STORE_CURRENT_USER, + (void*)pRoot + ); + } + if (hSystemStore == IntPtr.Zero) + return false; + + void* hAuthCryptMsg = null; + var result = TryGetSignerInfo( + fileName, + out hAuthCryptMsg, + out authSignData.hCertStoreHandle, + out authSignData.pSignerInfo, + out authSignData.dwObjSize + ); + + if (hAuthCryptMsg is not null) + { + PInvoke.CryptMsgClose(hAuthCryptMsg); + hAuthCryptMsg = null; + } + + if (!result) + { + if (authSignData.hCertStoreHandle != IntPtr.Zero) + PInvoke.CertCloseStore(authSignData.hCertStoreHandle, 0); + + PInvoke.CertCloseStore(hSystemStore, 0); + return false; + } + + signDataChain.Add(authSignData); + GetNestedSignerInfo(ref authSignData, signDataChain); + + for (var i = 0; i < signDataChain.Count; i++) + { + if (ct.IsCancellationRequested) + { + PInvoke.CertCloseStore(hSystemStore, 0); + return false; + } + + CERT_CONTEXT* pCurrContext = null; + CMSG_SIGNER_INFO* pCounterSigner = null; + var signNode = new SignNodeInfo(); + + GetCounterSignerInfo(signDataChain[i].pSignerInfo, &pCounterSigner); + if (pCounterSigner is not null) + GetCounterSignerData(pCounterSigner, signNode.CounterSign); + else + GetGeneralizedTimeStamp(signDataChain[i].pSignerInfo, signNode.CounterSign); + + var pszObjId = signDataChain[i].pSignerInfo->HashAlgorithm.pszObjId; + var szObjId = new string((sbyte*)(byte*)pszObjId); + CalculateDigestAlgorithm(szObjId, signNode); + (_, signNode.Version) = CalculateSignVersion(signDataChain[i].pSignerInfo->dwVersion); + + + var pIssuer = &(signDataChain[i].pSignerInfo->Issuer); + pCurrContext = PInvoke.CertFindCertificateInStore( + signDataChain[i].hCertStoreHandle, + ENCODING, + 0, + CERT_FIND_FLAGS.CERT_FIND_ISSUER_NAME, + pIssuer, + null + ); + + result = pCurrContext is not null; + while (result) + { + var pOrigContext = pCurrContext; + result = GetSignerSignatureInfo( + hSystemStore, + signDataChain[i].hCertStoreHandle, + pOrigContext, + ref pCurrContext, + signNode + ); + PInvoke.CertFreeCertificateContext(pOrigContext); + } + + if (pCurrContext is not null) + PInvoke.CertFreeCertificateContext(pCurrContext); + + if (pCounterSigner is not null) + NativeMemory.Free(pCounterSigner); + + if (signDataChain[i].pSignerInfo is not null) + NativeMemory.Free(signDataChain[i].pSignerInfo); + + if (!signDataChain[i].hCertStoreHandle.IsNull) + PInvoke.CertCloseStore(signDataChain[i].hCertStoreHandle, 0); + + succeded = true; + signNode.IsValid = VerifyySignature(fileName); + signNode.Index = i; + signChain.Add(signNode); + } + + PInvoke.CertCloseStore(hSystemStore, 0); + return succeded; + } + + private unsafe static bool VerifyySignature(string certPath) + { + int res = 1; + var sFileInfo = (uint)Marshal.SizeOf(); + var sData = (uint)Marshal.SizeOf(); + var actionGuid = new Guid("{00AAC56B-CD44-11D0-8CC2-00C04FC295EE}"); + + fixed (char* pCertPath = certPath) + { + var fileInfo = new WINTRUST_FILE_INFO + { + cbStruct = sFileInfo, + pcwszFilePath = (PCWSTR)pCertPath, + hFile = (HANDLE)null, + pgKnownSubject = null + }; + + var wintrustData = new WINTRUST_DATA + { + cbStruct = sData, + pPolicyCallbackData = null, + pSIPClientData = null, + dwUIChoice = WINTRUST_DATA_UICHOICE.WTD_UI_NONE, + fdwRevocationChecks = 0, // No revocation checking + dwUnionChoice = WINTRUST_DATA_UNION_CHOICE.WTD_CHOICE_FILE, + dwStateAction = WINTRUST_DATA_STATE_ACTION.WTD_STATEACTION_VERIFY, + hWVTStateData = (HANDLE)null, + pwszURLReference = null, + dwUIContext = 0, + Anonymous = new WINTRUST_DATA._Anonymous_e__Union + { + pFile = &fileInfo, + }, + }; + + res = PInvoke.WinVerifyTrust((HWND)null, ref actionGuid, &wintrustData); + + // Release hWVTStateData + wintrustData.dwStateAction = WINTRUST_DATA_STATE_ACTION.WTD_STATEACTION_CLOSE; + PInvoke.WinVerifyTrust((HWND)null, ref actionGuid, &wintrustData); + } + + return res == 0; + } + + private unsafe static bool TryGetSignerInfo( + string fileName, + out void* hMsg, + out HCERTSTORE hCertStore, + out CMSG_SIGNER_INFO* pSignerInfo, + out uint signerSize, + uint index = 0) + { + CERT_QUERY_ENCODING_TYPE encoding = 0; + CERT_QUERY_CONTENT_TYPE dummy = 0; + CERT_QUERY_FORMAT_TYPE dummy2 = 0; + void* pDummy = null; + BOOL result = false; + + HCERTSTORE hCertStoreTmp = HCERTSTORE.Null; + void* hMsgTmp = null; + + fixed (char* pFileName = fileName) + { + result = PInvoke.CryptQueryObject( + CERT_QUERY_OBJECT_TYPE.CERT_QUERY_OBJECT_FILE, + pFileName, + CERT_QUERY_CONTENT_TYPE_FLAGS.CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED, + CERT_QUERY_FORMAT_TYPE_FLAGS.CERT_QUERY_FORMAT_FLAG_BINARY, + 0, + &encoding, + &dummy, + &dummy2, + &hCertStoreTmp, + &hMsgTmp, + &pDummy + ); + } + + hCertStore = hCertStoreTmp; + hMsg = hMsgTmp; + pSignerInfo = null; + signerSize = 0; + + if (!result) + return false; + + var vpSignerInfo = (void*)pSignerInfo; + result = CustomCryptMsgGetParam( + hMsg, + CMSG_SIGNER_INFO_PARAM, + index, + ref vpSignerInfo, + ref signerSize + ); + pSignerInfo = (CMSG_SIGNER_INFO*)vpSignerInfo; + + return result; + } + + private unsafe static bool GetCounterSignerInfo( + CMSG_SIGNER_INFO* pSignerInfo, + CMSG_SIGNER_INFO** pTargetSigner) + { + uint objSize = 0; + if (pSignerInfo is null || pTargetSigner is null) + return false; + + try + { + *pTargetSigner = null; + CRYPT_ATTRIBUTE* attr = null; + var res = TryGetUnauthAttr(pSignerInfo, szOID_RSA_counterSign, ref attr); + if (!res || attr is null) + return false; + + var pkcs7_signer_info = (PCSTR)(byte*)500; + var result = PInvoke.CryptDecodeObject( + ENCODING, + pkcs7_signer_info, + attr->rgValue[0].pbData, + attr->rgValue[0].cbData, + 0, + null, + &objSize + ); + if (!result) + return false; + + *pTargetSigner = (CMSG_SIGNER_INFO*)NativeMemory.Alloc(objSize); + if (*pTargetSigner is null) + return false; + + result = PInvoke.CryptDecodeObject( + ENCODING, + pkcs7_signer_info, + attr->rgValue[0].pbData, + attr->rgValue[0].cbData, + 0, + *pTargetSigner, + &objSize + ); + if (!result) + return false; + } + finally + { + } + + return true; + } + + private unsafe static bool GetCounterSignerData(CMSG_SIGNER_INFO* pSignerInfo, SignCounterSign counterSign) + { + CRYPT_ATTRIBUTE* attr = null; + var res = TryGetAuthAttr(pSignerInfo, szOID_RSA_signingTime, ref attr); + if (!res || attr is null) + return false; + + var data = (uint)Marshal.SizeOf(); + var ft = (System.Runtime.InteropServices.ComTypes.FILETIME*)NativeMemory.Alloc(data); + try + { + var pkcs_utc_time = (PCSTR)(byte*)17; + var result = PInvoke.CryptDecodeObject( + ENCODING, + pkcs_utc_time, + attr->rgValue[0].pbData, + attr->rgValue[0].cbData, + 0, + ft, + &data + ); + if (!result) + return false; + + PInvoke.FileTimeToLocalFileTime(*ft, out var lft); + PInvoke.FileTimeToSystemTime(lft, out var st); + counterSign.TimeStamp = TimeToString(null, &st); + + return true; + } + finally + { + NativeMemory.Free(ft); + } + } + + private unsafe static bool ParseDERFindType( + int typeSearch, + byte* pbSignature, + uint size, + ref uint positionFound, + ref uint lengthFound) + { + uint position = 0; + uint sizeFound = 0; + uint bytesParsed = 0; + var iType = 0; + var iClass = 0; + positionFound = 0; + lengthFound = 0; + if (pbSignature is null) + return false; + + while (size > position) + { + if (!SafeToReadNBytes(size, position, 2)) + return false; + + ParseDERType(pbSignature[position], ref iType, ref iClass); + switch (iType) + { + case 0x05: // Null + ++position; + if (pbSignature[position] != 0x00) + return false; + + ++position; + break; + + case 0x06: // Object Identifier + ++position; + if (!SafeToReadNBytes(size - position, 1, pbSignature[position])) + return false; + + position += 1u + pbSignature[position]; + break; + + case 0x00: // ? + case 0x01: // Boolean + case 0x02: // Integer + case 0x03: // Bit String + case 0x04: // Octet String + case 0x0A: // enumerated + case 0x0C: // UTF8string + case 0x13: // printable string + case 0x14: // T61 string + case 0x16: // IA5String + case 0x17: // UTC time + case 0x18: // Generalized time + case 0x1E: // BMPstring + ++position; + if (!ParseDERSize( + pbSignature + position, + size - position, + ref sizeFound, + ref bytesParsed)) + { + return false; + } + + position += bytesParsed; + if (!SafeToReadNBytes(size - position, 0, sizeFound)) + return false; + + if (typeSearch == iType) + { + positionFound = position; + lengthFound = sizeFound; + + return true; + } + + position += sizeFound; + break; + + case 0x20: // context specific + case 0x21: // context specific + case 0x23: // context specific + case 0x24: // context specific + case 0x30: // sequence + case 0x31: // set + position++; + if (!ParseDERSize( + pbSignature + position, + size - position, + ref sizeFound, + ref bytesParsed)) + { + return false; + } + + position += bytesParsed; + break; + + case 0x22: // ? + position += 2; + break; + + default: + return false; + } + } + + return false; + } + + private unsafe static bool GetGeneralizedTimeStamp( + CMSG_SIGNER_INFO* pSignerInfo, + SignCounterSign counter) + { + uint positionFound = 0; + uint lengthFound = 0; + CRYPT_ATTRIBUTE* attr = null; + var res = TryGetUnauthAttr(pSignerInfo, szOID_RFC3161_counterSign, ref attr); + if (!res || attr is null) + return false; + + var result = ParseDERFindType( + 0x04, + attr->rgValue[0].pbData, + attr->rgValue[0].cbData, + ref positionFound, + ref lengthFound); + if (!result) + return false; + + // Counter Signer Timstamp + var pbOctetString = attr->rgValue[0].pbData + positionFound; + counter.TimeStamp = GetTimeStampFromDER(pbOctetString, lengthFound, ref positionFound); + + return true; + } + + private unsafe static string GetTimeStampFromDER(byte* pbOctetString, uint lengthFound, ref uint positionFound) + { + var result = ParseDERFindType( + 0x18, + pbOctetString, + lengthFound, + ref positionFound, + ref lengthFound + ); + if (!result) + return string.Empty; + + var st = new Windows.Win32.Foundation.SYSTEMTIME(); + var buffer = new string((sbyte*)(pbOctetString + positionFound)); + + _ = ushort.TryParse(buffer.AsSpan(0, 4), out st.wYear); + _ = ushort.TryParse(buffer.AsSpan(4, 2), out st.wMonth); + _ = ushort.TryParse(buffer.AsSpan(6, 2), out st.wDay); + _ = ushort.TryParse(buffer.AsSpan(8, 2), out st.wHour); + _ = ushort.TryParse(buffer.AsSpan(10, 2), out st.wMinute); + _ = ushort.TryParse(buffer.AsSpan(12, 2), out st.wSecond); + _ = ushort.TryParse(buffer.AsSpan(15, 3), out st.wMilliseconds); + + PInvoke.SystemTimeToFileTime(st, out var fft); + PInvoke.FileTimeToLocalFileTime(fft, out var lft); + PInvoke.FileTimeToSystemTime(lft, out var lst); + var timestamp = TimeToString(null, &lst); + + return timestamp; + } + + private unsafe static bool GetStringFromCertContext(CERT_CONTEXT* pCertContext, uint dwType, uint flag, CertNodeInfoItem info) + { + var data = PInvoke.CertGetNameString(pCertContext, dwType, flag, null, (PWSTR)null, 0); + if (data == 0) + { + PInvoke.CertFreeCertificateContext(pCertContext); + return false; + } + + var pszTempName = (PWSTR)NativeMemory.Alloc(data * sizeof(char)); + if (pszTempName.Value is null) + { + PInvoke.CertFreeCertificateContext(pCertContext); + NativeMemory.Free(pszTempName); + return false; + } + + data = PInvoke.CertGetNameString(pCertContext, dwType, flag, null, pszTempName, data); + if (data == 0) + { + NativeMemory.Free(pszTempName); + return false; + } + + var name = pszTempName.AsSpan().ToString(); + NativeMemory.Free(pszTempName); + if (flag == 0) + info.IssuedTo = StripString(name); + else + info.IssuedBy = StripString(name); + + return true; + } + + private unsafe static bool TryGetUnauthAttr(CMSG_SIGNER_INFO* pSignerInfo, string oid, ref CRYPT_ATTRIBUTE* attr) + { + int n = 0; + attr = null; + for (; n < pSignerInfo->UnauthAttrs.cAttr; n++) + { + attr = &pSignerInfo->UnauthAttrs.rgAttr[n]; + var objId = new string((sbyte*)(byte*)attr->pszObjId); + if (objId == oid) + break; + } + + return n < pSignerInfo->UnauthAttrs.cAttr; + } + + private unsafe static bool TryGetAuthAttr(CMSG_SIGNER_INFO* pSignerInfo, string oid, ref CRYPT_ATTRIBUTE* attr) + { + int n = 0; + attr = null; + for (; n < pSignerInfo->AuthAttrs.cAttr; n++) + { + attr = &pSignerInfo->AuthAttrs.rgAttr[n]; + var objId = new string((sbyte*)(byte*)attr->pszObjId); + if (objId == oid) + break; + } + + return n < pSignerInfo->AuthAttrs.cAttr; + } + + private unsafe static bool GetNestedSignerInfo(ref SignDataHandle AuthSignData, List NestedChain) + { + var succeded = false; + void* hNestedMsg = null; + if (AuthSignData.pSignerInfo is null) + return false; + + try + { + CRYPT_ATTRIBUTE* attr = null; + var res = TryGetUnauthAttr(AuthSignData.pSignerInfo, szOID_NESTED_SIGNATURE, ref attr); + if (!res || attr is null) + return false; + + var cbCurrData = attr->rgValue[0].cbData; + var pbCurrData = attr->rgValue[0].pbData; + var upperBound = AuthSignData.pSignerInfo + AuthSignData.dwObjSize; + while (pbCurrData > AuthSignData.pSignerInfo && pbCurrData < upperBound) + { + var nestedHandle = new SignDataHandle() { dwObjSize = 0, pSignerInfo = null, hCertStoreHandle = HCERTSTORE.Null }; + if (!Memcmp(pbCurrData, SG_ProtoCoded) || + !Memcmp(pbCurrData + 6, SG_SignedData)) + { + break; + } + + hNestedMsg = PInvoke.CryptMsgOpenToDecode( + PKCS_7_ASN_ENCODING | CRYPT_ASN_ENCODING, + 0, + 0, + HCRYPTPROV_LEGACY.Null, + null, + null + ); + if (hNestedMsg is null) + return false; + + cbCurrData = XCHWordLitend(*(ushort*)(pbCurrData + 2)) + 4u; + var pbNextData = pbCurrData; + pbNextData += EightByteAlign(cbCurrData, (long)pbCurrData); + var result = PInvoke.CryptMsgUpdate(hNestedMsg, pbCurrData, cbCurrData, true); + pbCurrData = pbNextData; + if (!result) + continue; + + var pSignerInfo = (void*)nestedHandle.pSignerInfo; + result = CustomCryptMsgGetParam( + hNestedMsg, + CMSG_SIGNER_INFO_PARAM, + 0, + ref pSignerInfo, + ref nestedHandle.dwObjSize + ); + nestedHandle.pSignerInfo = (CMSG_SIGNER_INFO*)pSignerInfo; + if (!result) + continue; + + var cert_store_prov_msg = (PCSTR)(byte*)1; + nestedHandle.hCertStoreHandle = PInvoke.CertOpenStore( + cert_store_prov_msg, + ENCODING, + HCRYPTPROV_LEGACY.Null, + 0, + hNestedMsg + ); + + succeded = true; + NestedChain.Add(nestedHandle); + } + } + finally + { + if (hNestedMsg is not null) + PInvoke.CryptMsgClose(hNestedMsg); + } + + return succeded; + } + + private unsafe static bool CustomCryptMsgGetParam( + void* hCryptMsg, + uint paramType, + uint index, + ref void* pParam, + ref uint outSize) + { + bool result; + uint size = 0; + + result = PInvoke.CryptMsgGetParam( + hCryptMsg, + paramType, + index, + null, + &size + ); + if (!result) + return false; + + pParam = NativeMemory.Alloc(size); + if (pParam is null) + return false; + + result = PInvoke.CryptMsgGetParam( + hCryptMsg, + paramType, + index, + pParam, + &size + ); + if (!result) + return false; + + outSize = size; + return true; + } + + private static ushort XCHWordLitend(uint num) + => (ushort)(((((ushort)num) & 0xFF00) >> 8) | (((ushort)num) & 0x00FF) << 8); + + private static long EightByteAlign(long offset, long b) + => ((offset + b + 7) & 0xFFFFFFF8L) - (b & 0xFFFFFFF8L); + + private unsafe static bool Memcmp(byte* ptr1, byte[] arr) + { + for (var i = 0; i < arr.Length; i++) + { + if (ptr1[i] != arr[i]) + return false; + } + + return true; + } + + private static (bool, string) CalculateSignVersion(uint versionNumber) + { + var res = versionNumber switch + { + CERT_V1 => "V1", + CERT_V2 => "V2", + CERT_V3 => "V3", + _ => "Unknown", + }; + return (true, res); + } + + private static bool CalculateDigestAlgorithm(string pszObjId, SignNodeInfo info) + { + if (string.IsNullOrWhiteSpace(pszObjId)) + info.DigestAlgorithm = "Unknown"; + else if (pszObjId == szOID_OIWSEC_sha1) + info.DigestAlgorithm = "SHA1"; + else if (pszObjId == szOID_RSA_MD5) + info.DigestAlgorithm = "MD5"; + else if (pszObjId == szOID_NIST_sha256) + info.DigestAlgorithm = "SHA256"; + else + info.DigestAlgorithm = StripString(pszObjId); + + return true; + } + + private static bool SafeToReadNBytes(uint size, uint start, uint requestSize) + => size - start >= requestSize; + + private static void ParseDERType(byte bIn, ref int iType, ref int iClass) + { + iType = bIn & 0x3F; + iClass = bIn >> 6; + } + + private unsafe static uint ReadNumberFromNBytes(byte* pbSignature, uint start, uint requestSize) + { + uint number = 0; + for (var i = 0; i < requestSize; i++) + number = number * 0x100 + pbSignature[start + i]; + + return number; + } + + private unsafe static bool ParseDERSize(byte* pbSignature, uint size, ref uint sizeFound, ref uint bytesParsed) + { + if (pbSignature[0] > 0x80 && !SafeToReadNBytes(size, 1, pbSignature[0] - 0x80u)) + return false; + + if (pbSignature[0] <= 0x80) + { + sizeFound = pbSignature[0]; + bytesParsed = 1; + } + else + { + sizeFound = ReadNumberFromNBytes(pbSignature, 1, pbSignature[0] - 0x80u); + bytesParsed = pbSignature[0] - 0x80u + 1; + } + + return true; + } + + private static string StripString(string? str) + { + return str? + .Replace("\t", "")? + .Replace("\n", "")? + .Replace("\r", "")? + .Replace(((char)0).ToString(), "") ?? string.Empty; + } + + private unsafe static string TimeToString( + System.Runtime.InteropServices.ComTypes.FILETIME* pftIn, + Windows.Win32.Foundation.SYSTEMTIME* pstIn = null) + { + if (pstIn is null) + { + if (pftIn is null) + return string.Empty; + + PInvoke.FileTimeToSystemTime(*pftIn, out var sysTime); + pstIn = &sysTime; + } + + var date = new DateTime( + pstIn->wYear, pstIn->wMonth, pstIn->wDay, + pstIn->wHour, pstIn->wMinute, pstIn->wSecond + ); + + return formatter.ToLongLabel(date); + } + + class SignCounterSign + { + public string TimeStamp { get; set; } = string.Empty; + } + + class SignNodeInfo + { + public bool IsValid { get; set; } = false; + public string DigestAlgorithm { get; set; } = string.Empty; + public string Version { get; set; } = string.Empty; + public int Index { get; set; } = 0; + public SignCounterSign CounterSign { get; set; } = new(); + public List CertChain { get; set; } = []; + } + } +} diff --git a/src/Files.App/Utils/StatusCenter/StatusCenterHelper.cs b/src/Files.App/Utils/StatusCenter/StatusCenterHelper.cs index 1998e77ea64b..8605bdd12415 100644 --- a/src/Files.App/Utils/StatusCenter/StatusCenterHelper.cs +++ b/src/Files.App/Utils/StatusCenter/StatusCenterHelper.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.StatusCenter { @@ -474,6 +474,131 @@ public static StatusCenterItem AddCard_EmptyRecycleBin( } } + public static StatusCenterItem AddCard_GitClone( + IEnumerable repoName, + IEnumerable destination, + ReturnResult returnStatus, + long itemsCount = 0, + long totalSize = 0) + { + var destinationDir = PathNormalization.GetParentDir(destination.FirstOrDefault()); + + if (returnStatus == ReturnResult.Cancelled) + { + return _statusCenterViewModel.AddItem( + "StatusCenter_GitCloneCanceled_Header", + "StatusCenter_GitCloneCanceled_SubHeader", + ReturnResult.Cancelled, + FileOperationType.GitClone, + repoName, + destination, + false, + itemsCount, + totalSize); + } + else if (returnStatus == ReturnResult.InProgress) + { + return _statusCenterViewModel.AddItem( + "StatusCenter_GitCloneInProgress_Header", + "StatusCenter_GitCloneInProgress_SubHeader", + ReturnResult.InProgress, + FileOperationType.GitClone, + repoName, + destination, + true, + itemsCount, + totalSize, + new CancellationTokenSource()); + } + else if (returnStatus == ReturnResult.Success) + { + return _statusCenterViewModel.AddItem( + "StatusCenter_GitCloneComplete_Header", + "StatusCenter_GitCloneComplete_SubHeader", + ReturnResult.Success, + FileOperationType.GitClone, + repoName, + destination, + false, + itemsCount, + totalSize); + } + else + { + return _statusCenterViewModel.AddItem( + "StatusCenter_GitCloneFailed_Header", + "StatusCenter_GitCloneFailed_SubHeader", + ReturnResult.Failed, + FileOperationType.GitClone, + repoName, + destination, + false, + itemsCount, + totalSize); + } + } + + public static StatusCenterItem AddCard_InstallFont( + IEnumerable source, + ReturnResult returnStatus, + long itemsCount = 0, + long totalSize = 0) + { + if (returnStatus == ReturnResult.Cancelled) + { + return _statusCenterViewModel.AddItem( + "StatusCenter_InstallFontCanceled_Header", + "StatusCenter_InstallFontCanceled_SubHeader", + ReturnResult.Cancelled, + FileOperationType.InstallFont, + source, + string.Empty.CreateEnumerable(), + false, + itemsCount, + totalSize); + } + else if (returnStatus == ReturnResult.InProgress) + { + return _statusCenterViewModel.AddItem( + "StatusCenter_InstallFontInProgress_Header", + "StatusCenter_InstallFontInProgress_SubHeader", + ReturnResult.InProgress, + FileOperationType.InstallFont, + source, + string.Empty.CreateEnumerable(), + false, + itemsCount, + totalSize, + new CancellationTokenSource()); + } + else if (returnStatus == ReturnResult.Success) + { + return _statusCenterViewModel.AddItem( + "StatusCenter_InstallFontComplete_Header", + "StatusCenter_InstallFontComplete_SubHeader", + ReturnResult.Success, + FileOperationType.InstallFont, + source, + string.Empty.CreateEnumerable(), + false, + itemsCount, + totalSize); + } + else + { + return _statusCenterViewModel.AddItem( + "StatusCenter_InstallFontFailed_Header", + "StatusCenter_InstallFontFailed_SubHeader", + ReturnResult.Failed, + FileOperationType.InstallFont, + source, + string.Empty.CreateEnumerable(), + false, + itemsCount, + totalSize); + } + } + public static StatusCenterItem AddCard_Prepare() { return _statusCenterViewModel.AddItem( @@ -486,7 +611,7 @@ public static StatusCenterItem AddCard_Prepare() false); } - public static void UpdateCardStrings(StatusCenterItem card) + public static void UpdateCardStrings(StatusCenterItem card, StatusCenterItemProgressModel? progressValue = null) { // Aren't used for now string sourcePath = string.Empty; @@ -515,166 +640,119 @@ public static void UpdateCardStrings(StatusCenterItem card) destinationDirName = destinationPath.Split('\\').Last(); } - string headerString = string.IsNullOrWhiteSpace(card.HeaderStringResource) ? string.Empty : card.HeaderStringResource.GetLocalizedResource(); - string subHeaderString = string.IsNullOrWhiteSpace(card.SubHeaderStringResource) ? string.Empty : card.SubHeaderStringResource.GetLocalizedResource(); - // Update string resources switch (card.Operation) { case FileOperationType.Copy: { - if (headerString is not null) - { - card.Header = card.FileSystemOperationReturnResult switch - { - ReturnResult.Cancelled => string.Format(headerString, card.TotalItemsCount, destinationDirName), - ReturnResult.Success => string.Format(headerString, card.TotalItemsCount, destinationDirName), - ReturnResult.Failed => string.Format(headerString, card.TotalItemsCount, destinationDirName), - ReturnResult.InProgress => string.Format(headerString, card.TotalItemsCount, destinationDirName), - _ => string.Format(headerString, card.TotalItemsCount, destinationDirName), - }; - } - if (subHeaderString is not null) - { - card.SubHeader = card.FileSystemOperationReturnResult switch - { - ReturnResult.Cancelled => string.Format(subHeaderString, card.TotalItemsCount, sourcePath, destinationPath), - ReturnResult.Success => string.Format(subHeaderString, card.TotalItemsCount, sourcePath, destinationPath), - ReturnResult.Failed => string.Format(subHeaderString, card.TotalItemsCount, sourcePath, destinationPath), - ReturnResult.InProgress => string.Format(subHeaderString, card.TotalItemsCount, sourcePath, destinationPath), - _ => string.Format(subHeaderString, card.TotalItemsCount, sourcePath, destinationPath), - }; - } + string headerResource = card.HeaderStringResource; + + if (card.IsDiscovering && card.TotalItemsCount > 0 && card.IsInProgress) + headerResource = "StatusCenter_CopyDiscovery_Header"; + + if (string.IsNullOrWhiteSpace(headerResource)) + card.Header = string.Empty; + else if (headerResource == "StatusCenter_CopyDiscovery_Header") + card.Header = headerResource.GetLocalizedFormatResource(card.TotalItemsCount); + else + card.Header = headerResource.GetLocalizedFormatResource(card.TotalItemsCount, destinationDirName); + + string subHeaderString = string.IsNullOrWhiteSpace(card.SubHeaderStringResource) ? string.Empty + : card.SubHeaderStringResource.GetLocalizedFormatResource(card.TotalItemsCount, sourcePath, destinationPath); + card.SubHeader = subHeaderString; break; } case FileOperationType.Move: { - if (headerString is not null) - { - card.Header = card.FileSystemOperationReturnResult switch - { - ReturnResult.Cancelled => string.Format(headerString, card.TotalItemsCount, destinationDirName), - ReturnResult.Success => string.Format(headerString, card.TotalItemsCount, destinationDirName), - ReturnResult.Failed => string.Format(headerString, card.TotalItemsCount, destinationDirName), - ReturnResult.InProgress => string.Format(headerString, card.TotalItemsCount, destinationDirName), - _ => string.Format(headerString, card.TotalItemsCount, destinationDirName), - }; - } - if (subHeaderString is not null) - { - card.SubHeader = card.FileSystemOperationReturnResult switch - { - ReturnResult.Cancelled => string.Format(subHeaderString, card.TotalItemsCount, sourcePath, destinationPath), - ReturnResult.Success => string.Format(subHeaderString, card.TotalItemsCount, sourcePath, destinationPath), - ReturnResult.Failed => string.Format(subHeaderString, card.TotalItemsCount, sourcePath, destinationPath), - ReturnResult.InProgress => string.Format(subHeaderString, card.TotalItemsCount, sourcePath, destinationPath), - _ => string.Format(subHeaderString, card.TotalItemsCount, sourcePath, destinationPath), - }; - } + string headerResource = card.HeaderStringResource; + + if (card.IsDiscovering && card.TotalItemsCount > 0 && card.IsInProgress) + headerResource = "StatusCenter_CopyDiscovery_Header"; + + if (string.IsNullOrWhiteSpace(headerResource)) + card.Header = string.Empty; + else if (headerResource == "StatusCenter_CopyDiscovery_Header") + card.Header = headerResource.GetLocalizedFormatResource(card.TotalItemsCount); + else + card.Header = headerResource.GetLocalizedFormatResource(card.TotalItemsCount, destinationDirName); + + string subHeaderString = string.IsNullOrWhiteSpace(card.SubHeaderStringResource) ? string.Empty + : card.SubHeaderStringResource.GetLocalizedFormatResource(card.TotalItemsCount, sourcePath, destinationPath); + card.SubHeader = subHeaderString; break; } case FileOperationType.Delete: { - if (headerString is not null) - { - card.Header = card.FileSystemOperationReturnResult switch - { - ReturnResult.Cancelled => string.Format(headerString, card.TotalItemsCount, sourceDirName), - ReturnResult.Success => string.Format(headerString, card.TotalItemsCount, sourceDirName), - ReturnResult.Failed => string.Format(headerString, card.TotalItemsCount, sourceDirName), - ReturnResult.InProgress => string.Format(headerString, card.TotalItemsCount, sourceDirName), - _ => string.Format(headerString, card.TotalItemsCount, sourceDirName), - }; - } - if (subHeaderString is not null) - { - card.SubHeader = card.FileSystemOperationReturnResult switch - { - ReturnResult.Cancelled => string.Format(subHeaderString, card.TotalItemsCount, sourcePath), - ReturnResult.Success => string.Format(subHeaderString, card.TotalItemsCount, sourcePath), - ReturnResult.Failed => string.Format(subHeaderString, card.TotalItemsCount, sourcePath), - ReturnResult.InProgress => string.Format(subHeaderString, card.TotalItemsCount, sourcePath), - _ => string.Format(subHeaderString, card.TotalItemsCount, sourcePath), - }; - } + string headerResource = card.HeaderStringResource; + + if (card.IsDiscovering && card.TotalItemsCount > 0 && card.IsInProgress) + headerResource = "StatusCenter_CopyDiscovery_Header"; + + if (string.IsNullOrWhiteSpace(headerResource)) + card.Header = string.Empty; + else if (headerResource == "StatusCenter_CopyDiscovery_Header") + card.Header = headerResource.GetLocalizedFormatResource(card.TotalItemsCount); + else + card.Header = headerResource.GetLocalizedFormatResource(card.TotalItemsCount, sourceDirName); + + string subHeaderString = string.IsNullOrWhiteSpace(card.SubHeaderStringResource) ? string.Empty + : card.SubHeaderStringResource.GetLocalizedFormatResource(card.TotalItemsCount, sourcePath); + card.SubHeader = subHeaderString; break; } case FileOperationType.Recycle: { - if (headerString is not null) - { - card.Header = card.FileSystemOperationReturnResult switch - { - ReturnResult.Cancelled => string.Format(headerString, card.TotalItemsCount, sourceDirName), - ReturnResult.Success => string.Format(headerString, card.TotalItemsCount, sourceDirName), - ReturnResult.Failed => string.Format(headerString, card.TotalItemsCount, sourceDirName), - ReturnResult.InProgress => string.Format(headerString, card.TotalItemsCount, sourceDirName), - _ => string.Format(headerString, card.TotalItemsCount, sourceDirName), - }; - } - if (subHeaderString is not null) - { - card.SubHeader = card.FileSystemOperationReturnResult switch - { - ReturnResult.Cancelled => string.Format(subHeaderString, card.TotalItemsCount, sourcePath), - ReturnResult.Success => string.Format(subHeaderString, card.TotalItemsCount, sourcePath), - ReturnResult.Failed => string.Format(subHeaderString, card.TotalItemsCount, sourcePath), - ReturnResult.InProgress => string.Format(subHeaderString, card.TotalItemsCount, sourcePath), - _ => string.Format(subHeaderString, card.TotalItemsCount, sourcePath), - }; - } + string headerString = string.IsNullOrWhiteSpace(card.HeaderStringResource) ? string.Empty + : card.HeaderStringResource.GetLocalizedFormatResource(card.TotalItemsCount, sourceDirName); + card.Header = headerString; + + string subHeaderString = string.IsNullOrWhiteSpace(card.SubHeaderStringResource) ? string.Empty + : card.SubHeaderStringResource.GetLocalizedFormatResource(card.TotalItemsCount, sourcePath); + card.SubHeader = subHeaderString; break; } case FileOperationType.Extract: { - if (headerString is not null) - { - card.Header = card.FileSystemOperationReturnResult switch - { - ReturnResult.Cancelled => string.Format(headerString, sourceFileName, destinationDirName), - ReturnResult.Success => string.Format(headerString, sourceFileName, destinationDirName), - ReturnResult.Failed => string.Format(headerString, sourceFileName, destinationDirName), - ReturnResult.InProgress => string.Format(headerString, sourceFileName, destinationDirName), - _ => string.Format(headerString, sourceFileName, destinationDirName), - }; - } - if (subHeaderString is not null) - { - card.SubHeader = card.FileSystemOperationReturnResult switch - { - ReturnResult.Cancelled => string.Format(subHeaderString, sourceFileName, sourcePath, destinationPath), - ReturnResult.Success => string.Format(subHeaderString, sourceFileName, sourcePath, destinationPath), - ReturnResult.Failed => string.Format(subHeaderString, sourceFileName, sourcePath, destinationPath), - ReturnResult.InProgress => string.Format(subHeaderString, sourceFileName, sourcePath, destinationPath), - _ => string.Format(subHeaderString, sourceFileName, sourcePath, destinationPath), - }; - } + string headerString = string.IsNullOrWhiteSpace(card.HeaderStringResource) ? string.Empty + : card.HeaderStringResource.GetLocalizedFormatResource(sourceFileName, destinationDirName); + card.Header = headerString; + + string subHeaderString = string.IsNullOrWhiteSpace(card.SubHeaderStringResource) ? string.Empty + : card.SubHeaderStringResource.GetLocalizedFormatResource(sourceFileName, sourcePath, destinationPath); + card.SubHeader = subHeaderString; break; } case FileOperationType.Compressed: { - if (headerString is not null) - { - card.Header = card.FileSystemOperationReturnResult switch - { - ReturnResult.Cancelled => string.Format(headerString, card.TotalItemsCount, destinationDirName), - ReturnResult.Success => string.Format(headerString, card.TotalItemsCount, destinationDirName), - ReturnResult.Failed => string.Format(headerString, card.TotalItemsCount, destinationDirName), - ReturnResult.InProgress => string.Format(headerString, card.TotalItemsCount, destinationDirName), - _ => string.Format(headerString, card.TotalItemsCount, destinationDirName), - }; - } - if (subHeaderString is not null) - { - card.SubHeader = card.FileSystemOperationReturnResult switch - { - ReturnResult.Cancelled => string.Format(subHeaderString, card.TotalItemsCount, sourcePath, destinationPath), - ReturnResult.Success => string.Format(subHeaderString, card.TotalItemsCount, sourcePath, destinationPath), - ReturnResult.Failed => string.Format(subHeaderString, card.TotalItemsCount, sourcePath, destinationPath), - ReturnResult.InProgress => string.Format(subHeaderString, card.TotalItemsCount, sourcePath, destinationPath), - _ => string.Format(subHeaderString, card.TotalItemsCount, sourcePath, destinationPath), - }; - } + string headerString = string.IsNullOrWhiteSpace(card.HeaderStringResource) ? string.Empty + : card.HeaderStringResource.GetLocalizedFormatResource(card.TotalItemsCount, destinationDirName); + card.Header = headerString; + + string subHeaderString = string.IsNullOrWhiteSpace(card.SubHeaderStringResource) ? string.Empty + : card.SubHeaderStringResource.GetLocalizedFormatResource(card.TotalItemsCount, sourcePath, destinationPath); + card.SubHeader = subHeaderString; + break; + } + case FileOperationType.GitClone: + { + string headerString = string.IsNullOrWhiteSpace(card.HeaderStringResource) ? string.Empty + : card.HeaderStringResource.GetLocalizedFormatResource(sourcePath, destinationDirName); + card.Header = headerString; + + string subHeaderString = string.IsNullOrWhiteSpace(card.SubHeaderStringResource) ? string.Empty + : card.SubHeaderStringResource.GetLocalizedFormatResource(card.TotalItemsCount, sourcePath, destinationPath); + card.SubHeader = subHeaderString; + break; + } + case FileOperationType.InstallFont: + { + string headerString = string.IsNullOrWhiteSpace(card.HeaderStringResource) ? string.Empty + : card.HeaderStringResource.GetLocalizedFormatResource(card.TotalItemsCount); + card.Header = headerString; + + string subHeaderString = string.IsNullOrWhiteSpace(card.SubHeaderStringResource) ? string.Empty + : card.SubHeaderStringResource.GetLocalizedFormatResource(card.TotalItemsCount, sourcePath); + card.SubHeader = subHeaderString; break; } } diff --git a/src/Files.App/Utils/StatusCenter/StatusCenterItem.cs b/src/Files.App/Utils/StatusCenter/StatusCenterItem.cs index 7a259f10793d..b1e063b890f3 100644 --- a/src/Files.App/Utils/StatusCenter/StatusCenterItem.cs +++ b/src/Files.App/Utils/StatusCenter/StatusCenterItem.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using System.Windows.Input; using Microsoft.UI.Xaml.Media; using System.Numerics; +using System.Windows.Input; namespace Files.App.Utils.StatusCenter { @@ -12,7 +12,7 @@ namespace Files.App.Utils.StatusCenter ///
/// Handles all operation's functionality and UI. ///
- public sealed class StatusCenterItem : ObservableObject + public sealed partial class StatusCenterItem : ObservableObject { private readonly StatusCenterViewModel _viewModel = Ioc.Default.GetRequiredService(); @@ -147,6 +147,8 @@ public StatusCenterItemProgressModel Progress public bool IsInProgress { get; private set; } + public bool IsDiscovering { get; private set; } = true; + public IEnumerable? Source { get; private set; } public IEnumerable? Destination { get; private set; } @@ -199,7 +201,7 @@ public StatusCenterItem( AnimatedIconState = "NormalOff"; SpeedGraphValues = []; CancelCommand = new RelayCommand(ExecuteCancelCommand); - Message = "ProcessingItems".GetLocalizedResource(); + Message = Strings.DiscoveringItems.GetLocalizedResource(); Source = source; Destination = destination; @@ -229,6 +231,8 @@ public StatusCenterItem( FileOperationType.Delete => StatusCenterItemIconKind.Delete, FileOperationType.Recycle => StatusCenterItemIconKind.Recycle, FileOperationType.Compressed => StatusCenterItemIconKind.Compress, + FileOperationType.GitClone => StatusCenterItemIconKind.GitClone, + FileOperationType.InstallFont => StatusCenterItemIconKind.InstallFont, _ => StatusCenterItemIconKind.Delete, }; @@ -261,6 +265,8 @@ public StatusCenterItem( FileOperationType.Delete => StatusCenterItemIconKind.Delete, FileOperationType.Recycle => StatusCenterItemIconKind.Recycle, FileOperationType.Compressed => StatusCenterItemIconKind.Compress, + FileOperationType.GitClone => StatusCenterItemIconKind.GitClone, + FileOperationType.InstallFont => StatusCenterItemIconKind.InstallFont, _ => StatusCenterItemIconKind.Delete, }; @@ -292,21 +298,20 @@ private void ReportProgress(StatusCenterItemProgressModel value) if (Operation == FileOperationType.Recycle || Operation == FileOperationType.Delete || - Operation == FileOperationType.Compressed) + Operation == FileOperationType.Compressed || + Operation == FileOperationType.GitClone) { - Message = - $"{string.Format( - "StatusCenter_ProcessedItems_Header".GetLocalizedResource(), - value.ProcessedItemsCount, - value.ItemsCount)}"; + Message = string.Format( + Strings.StatusCenter_ProcessedItems_Header.GetLocalizedFormatResource(value.ProcessedItemsCount, value.ItemsCount), + value.ProcessedItemsCount, + value.ItemsCount); } else { - Message = - $"{string.Format( - "StatusCenter_ProcessedSize_Header".GetLocalizedResource(), - value.ProcessedSize.ToSizeString(), - value.TotalSize.ToSizeString())}"; + Message = string.Format( + Strings.StatusCenter_ProcessedSize_Header.GetLocalizedResource(), + value.ProcessedSize.ToSizeString(), + value.TotalSize.ToSizeString()); } } @@ -322,8 +327,14 @@ private void ReportProgress(StatusCenterItemProgressModel value) if (TotalSize < value.TotalSize) TotalSize = value.TotalSize; + if (value.EnumerationCompleted && IsDiscovering) + { + IsDiscovering = false; + Message = Strings.ProcessingItems.GetLocalizedResource(); + } + // Update UI for strings - StatusCenterHelper.UpdateCardStrings(this); + StatusCenterHelper.UpdateCardStrings(this, value); OnPropertyChanged(nameof(HeaderTooltip)); // Graph item point @@ -377,7 +388,7 @@ private void ReportProgress(StatusCenterItemProgressModel value) SpeedGraphValues?.Add(point); // Add percentage to the header - if (!IsIndeterminateProgress) + if (!IsIndeterminateProgress && value.EnumerationCompleted) Header = $"{Header} ({ProgressPercentage}%)"; // Update UI of the address bar @@ -393,7 +404,7 @@ public void ExecuteCancelCommand() IsCancelable = false; IsExpanded = false; IsSpeedAndProgressAvailable = false; - Header = $"{"Canceling".GetLocalizedResource()} - {Header}"; + Header = $"{Strings.Canceling.GetLocalizedResource()} - {Header}"; } } } diff --git a/src/Files.App/Utils/StatusCenter/StatusCenterItemProgressModel.cs b/src/Files.App/Utils/StatusCenter/StatusCenterItemProgressModel.cs index fa52196930b5..e4ced2c56529 100644 --- a/src/Files.App/Utils/StatusCenter/StatusCenterItemProgressModel.cs +++ b/src/Files.App/Utils/StatusCenter/StatusCenterItemProgressModel.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Collections.Concurrent; using System.Runtime.CompilerServices; @@ -14,7 +14,7 @@ namespace Files.App.Utils.StatusCenter ///
/// Therefore, the storage operation classes can portably instance this class and update progress from everywhere with the same instance. /// - public sealed class StatusCenterItemProgressModel : INotifyPropertyChanged + public sealed partial class StatusCenterItemProgressModel : INotifyPropertyChanged { private readonly IProgress? _progress; diff --git a/src/Files.App/Utils/Storage/Collection/BlockingListEnumerator.cs b/src/Files.App/Utils/Storage/Collection/BlockingListEnumerator.cs index ff19fc939ce2..3c85449fc585 100644 --- a/src/Files.App/Utils/Storage/Collection/BlockingListEnumerator.cs +++ b/src/Files.App/Utils/Storage/Collection/BlockingListEnumerator.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.Storage { diff --git a/src/Files.App/Utils/Storage/Collection/BulkConcurrentObservableCollection.cs b/src/Files.App/Utils/Storage/Collection/BulkConcurrentObservableCollection.cs index 4bba7f55eccd..528f1e9a0b46 100644 --- a/src/Files.App/Utils/Storage/Collection/BulkConcurrentObservableCollection.cs +++ b/src/Files.App/Utils/Storage/Collection/BulkConcurrentObservableCollection.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Collections.Specialized; diff --git a/src/Files.App/Utils/Storage/Collection/ConcurrentCollection.cs b/src/Files.App/Utils/Storage/Collection/ConcurrentCollection.cs index 7ba91e7d946d..ba52779a1a66 100644 --- a/src/Files.App/Utils/Storage/Collection/ConcurrentCollection.cs +++ b/src/Files.App/Utils/Storage/Collection/ConcurrentCollection.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Helpers { @@ -8,7 +8,7 @@ namespace Files.App.Helpers public sealed class ConcurrentCollection : ICollection, IList, ICollection, IList { private readonly object syncRoot = new object(); - + private readonly List collection = []; public int Count diff --git a/src/Files.App/Utils/Storage/Collection/GroupedCollection.cs b/src/Files.App/Utils/Storage/Collection/GroupedCollection.cs index bbe0a03d593a..9466d24ced31 100644 --- a/src/Files.App/Utils/Storage/Collection/GroupedCollection.cs +++ b/src/Files.App/Utils/Storage/Collection/GroupedCollection.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.Storage { @@ -43,8 +43,8 @@ private void GroupedCollection_PropertyChanged(object sender, PropertyChangedEve { Model.CountText = string.Format( Count > 1 - ? "GroupItemsCount_Plural".GetLocalizedResource() - : "GroupItemsCount_Singular".GetLocalizedResource(), + ? Strings.GroupItemsCount_Plural.GetLocalizedResource() + : Strings.GroupItemsCount_Singular.GetLocalizedResource(), Count); } } @@ -66,7 +66,7 @@ public void InitializeExtendedGroupHeaderInfoAsync() public override void BeginBulkOperation() { base.BeginBulkOperation(); - + Model.PausePropertyChangedNotifications(); } diff --git a/src/Files.App/Utils/Storage/Collection/GroupedHeaderViewModel.cs b/src/Files.App/Utils/Storage/Collection/GroupedHeaderViewModel.cs index fe069cac1f39..dff592522bef 100644 --- a/src/Files.App/Utils/Storage/Collection/GroupedHeaderViewModel.cs +++ b/src/Files.App/Utils/Storage/Collection/GroupedHeaderViewModel.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Media; using System.Runtime.CompilerServices; namespace Files.App.Utils.Storage { - public sealed class GroupedHeaderViewModel : ObservableObject + public sealed partial class GroupedHeaderViewModel : ObservableObject { public string Key { get; set; } public bool Initialized { get; set; } diff --git a/src/Files.App/Utils/Storage/Collection/GroupingHelper.cs b/src/Files.App/Utils/Storage/Collection/GroupingHelper.cs index 141826b374b3..ef91971c8cf6 100644 --- a/src/Files.App/Utils/Storage/Collection/GroupingHelper.cs +++ b/src/Files.App/Utils/Storage/Collection/GroupingHelper.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.Storage; @@ -92,7 +92,7 @@ public static (Action>, Action item.Name; - public static Func? GetSortFunc(SortOption directorySortOption) + public static Func GetSortFunc(SortOption directorySortOption) { return directorySortOption switch { @@ -24,7 +24,7 @@ private static object OrderByNameFunc(ListedItem item) SortOption.Path => item => item.ItemPath, SortOption.OriginalFolder => item => (item as RecycleBinItem)?.ItemOriginalFolder, SortOption.DateDeleted => item => (item as RecycleBinItem)?.ItemDateDeletedReal, - _ => null, + _ => item => item.Name, }; } diff --git a/src/Files.App/Utils/Storage/Enumerators/UniversalStorageEnumerator.cs b/src/Files.App/Utils/Storage/Enumerators/UniversalStorageEnumerator.cs index f4438f851471..05ccd045528d 100644 --- a/src/Files.App/Utils/Storage/Enumerators/UniversalStorageEnumerator.cs +++ b/src/Files.App/Utils/Storage/Enumerators/UniversalStorageEnumerator.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.Shared.Helpers; using Microsoft.Extensions.Logging; @@ -11,6 +11,8 @@ namespace Files.App.Utils.Storage { public static class UniversalStorageEnumerator { + private static readonly IIconCacheService iconCacheService = Ioc.Default.GetRequiredService(); + public static async Task> ListEntries( BaseStorageFolder rootFolder, StorageFolderWithPath currentStorageFolder, @@ -80,9 +82,11 @@ ex is FileNotFoundException || var folder = await AddFolderAsync(item.AsBaseStorageFolder(), currentStorageFolder, cancellationToken); if (folder is not null) { + folder.PreloadedIconData = await iconCacheService.GetIconAsync(folder.ItemPath, null, true); + if (defaultIconPairs?.ContainsKey(string.Empty) ?? false) folder.FileImage = defaultIconPairs[string.Empty]; - + tempList.Add(folder); } } @@ -91,14 +95,16 @@ ex is FileNotFoundException || var fileEntry = await AddFileAsync(item.AsBaseStorageFile(), currentStorageFolder, cancellationToken); if (fileEntry is not null) { + fileEntry.PreloadedIconData = await iconCacheService.GetIconAsync(fileEntry.ItemPath, fileEntry.FileExtension, false); + if (defaultIconPairs is not null) { if (!string.IsNullOrEmpty(fileEntry.FileExtension)) { var lowercaseExtension = fileEntry.FileExtension.ToLowerInvariant(); - - if (defaultIconPairs.ContainsKey(lowercaseExtension)) - fileEntry.FileImage = defaultIconPairs[lowercaseExtension]; + + if (defaultIconPairs.TryGetValue(lowercaseExtension, out BitmapImage? image)) + fileEntry.FileImage = image; } } @@ -193,7 +199,8 @@ public static async Task AddFolderAsync( TargetPath = linkFolder.TargetPath, Arguments = linkFolder.Arguments, WorkingDirectory = linkFolder.WorkingDirectory, - RunAsAdmin = linkFolder.RunAsAdmin + RunAsAdmin = linkFolder.RunAsAdmin, + ShowWindowCommand = linkFolder.ShowWindowCommand }; } else if (folder is BinStorageFolder binFolder) @@ -293,6 +300,7 @@ public static async Task AddFileAsync( Arguments = linkFile.Arguments, WorkingDirectory = linkFile.WorkingDirectory, RunAsAdmin = linkFile.RunAsAdmin, + ShowWindowCommand = linkFile.ShowWindowCommand, IsUrl = isUrl, }; } @@ -337,8 +345,6 @@ public static async Task AddFileAsync( }; } } - - return null; } } } diff --git a/src/Files.App/Utils/Storage/Enumerators/Win32StorageEnumerator.cs b/src/Files.App/Utils/Storage/Enumerators/Win32StorageEnumerator.cs index 610af9ab439b..af29a244ec1d 100644 --- a/src/Files.App/Utils/Storage/Enumerators/Win32StorageEnumerator.cs +++ b/src/Files.App/Utils/Storage/Enumerators/Win32StorageEnumerator.cs @@ -1,12 +1,10 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.Services.SizeProvider; using Files.Shared.Helpers; using System.IO; -using Vanara.PInvoke; using Windows.Storage; -using static Files.App.Helpers.Win32Helper; using FileAttributes = System.IO.FileAttributes; namespace Files.App.Utils.Storage @@ -16,7 +14,9 @@ public static class Win32StorageEnumerator private static readonly ISizeProvider folderSizeProvider = Ioc.Default.GetService(); private static readonly IStorageCacheService fileListCache = Ioc.Default.GetRequiredService(); - private static readonly string folderTypeTextLocalized = "Folder".GetLocalizedResource(); + private static readonly string folderTypeTextLocalized = Strings.Folder.GetLocalizedResource(); + + private static readonly IIconCacheService iconCacheService = Ioc.Default.GetRequiredService(); public static async Task> ListEntries( string path, @@ -51,6 +51,7 @@ Func, Task> intermediateAction var file = await GetFile(findData, path, isGitRepo, cancellationToken); if (file is not null) { + file.PreloadedIconData = await iconCacheService.GetIconAsync(file.ItemPath, file.FileExtension, false); tempList.Add(file); ++count; @@ -67,6 +68,7 @@ Func, Task> intermediateAction var folder = await GetFolder(findData, path, isGitRepo, cancellationToken); if (folder is not null) { + folder.PreloadedIconData = await iconCacheService.GetIconAsync(folder.ItemPath, null, true); tempList.Add(folder); ++count; @@ -113,7 +115,7 @@ private static IEnumerable EnumAdsForPath(string itemPath, ListedIte public static ListedItem GetAlternateStream((string Name, long Size) ads, ListedItem main) { - string itemType = "File".GetLocalizedResource(); + string itemType = Strings.File.GetLocalizedResource(); string itemFileExtension = null; if (ads.Name.Contains('.')) @@ -251,7 +253,7 @@ CancellationToken cancellationToken long itemSizeBytes = findData.GetSize(); var itemSize = itemSizeBytes.ToSizeString(); - string itemType = "File".GetLocalizedResource(); + string itemType = Strings.File.GetLocalizedResource(); string itemFileExtension = null; if (findData.cFileName.Contains('.')) @@ -276,26 +278,50 @@ CancellationToken cancellationToken if (isSymlink) { var targetPath = Win32Helper.ParseSymLink(itemPath); - - return new ShortcutItem(null) + if (isGitRepo) { - PrimaryItemAttribute = StorageItemTypes.File, - FileExtension = itemFileExtension, - IsHiddenItem = isHidden, - Opacity = opacity, - FileImage = null, - LoadFileIcon = itemThumbnailImgVis, - ItemNameRaw = itemName, - ItemDateModifiedReal = itemModifiedDate, - ItemDateAccessedReal = itemLastAccessDate, - ItemDateCreatedReal = itemCreatedDate, - ItemType = "Shortcut".GetLocalizedResource(), - ItemPath = itemPath, - FileSize = itemSize, - FileSizeBytes = itemSizeBytes, - TargetPath = targetPath, - IsSymLink = true - }; + return new GitShortcutItem() + { + PrimaryItemAttribute = StorageItemTypes.File, + FileExtension = itemFileExtension, + IsHiddenItem = isHidden, + Opacity = opacity, + FileImage = null, + LoadFileIcon = itemThumbnailImgVis, + ItemNameRaw = itemName, + ItemDateModifiedReal = itemModifiedDate, + ItemDateAccessedReal = itemLastAccessDate, + ItemDateCreatedReal = itemCreatedDate, + ItemType = Strings.Shortcut.GetLocalizedResource(), + ItemPath = itemPath, + FileSize = itemSize, + FileSizeBytes = itemSizeBytes, + TargetPath = targetPath, + IsSymLink = true, + }; + } + else + { + return new ShortcutItem(null) + { + PrimaryItemAttribute = StorageItemTypes.File, + FileExtension = itemFileExtension, + IsHiddenItem = isHidden, + Opacity = opacity, + FileImage = null, + LoadFileIcon = itemThumbnailImgVis, + ItemNameRaw = itemName, + ItemDateModifiedReal = itemModifiedDate, + ItemDateAccessedReal = itemLastAccessDate, + ItemDateCreatedReal = itemCreatedDate, + ItemType = Strings.Shortcut.GetLocalizedResource(), + ItemPath = itemPath, + FileSize = itemSize, + FileSizeBytes = itemSizeBytes, + TargetPath = targetPath, + IsSymLink = true + }; + } } else if (FileExtensionHelpers.IsShortcutOrUrlFile(findData.cFileName)) { @@ -305,28 +331,58 @@ CancellationToken cancellationToken if (shInfo is null) return null; - return new ShortcutItem(null) + if (isGitRepo) { - PrimaryItemAttribute = shInfo.IsFolder ? StorageItemTypes.Folder : StorageItemTypes.File, - FileExtension = itemFileExtension, - IsHiddenItem = isHidden, - Opacity = opacity, - FileImage = null, - LoadFileIcon = !shInfo.IsFolder && itemThumbnailImgVis, - ItemNameRaw = itemName, - ItemDateModifiedReal = itemModifiedDate, - ItemDateAccessedReal = itemLastAccessDate, - ItemDateCreatedReal = itemCreatedDate, - ItemType = isUrl ? "ShortcutWebLinkFileType".GetLocalizedResource() : "Shortcut".GetLocalizedResource(), - ItemPath = itemPath, - FileSize = itemSize, - FileSizeBytes = itemSizeBytes, - TargetPath = shInfo.TargetPath, - Arguments = shInfo.Arguments, - WorkingDirectory = shInfo.WorkingDirectory, - RunAsAdmin = shInfo.RunAsAdmin, - IsUrl = isUrl, - }; + return new GitShortcutItem() + { + PrimaryItemAttribute = shInfo.IsFolder ? StorageItemTypes.Folder : StorageItemTypes.File, + FileExtension = itemFileExtension, + IsHiddenItem = isHidden, + Opacity = opacity, + FileImage = null, + LoadFileIcon = !shInfo.IsFolder && itemThumbnailImgVis, + ItemNameRaw = itemName, + ItemDateModifiedReal = itemModifiedDate, + ItemDateAccessedReal = itemLastAccessDate, + ItemDateCreatedReal = itemCreatedDate, + ItemType = isUrl ? Strings.ShortcutWebLinkFileType.GetLocalizedResource() : Strings.Shortcut.GetLocalizedResource(), + ItemPath = itemPath, + FileSize = itemSize, + FileSizeBytes = itemSizeBytes, + TargetPath = shInfo.TargetPath, + Arguments = shInfo.Arguments, + WorkingDirectory = shInfo.WorkingDirectory, + RunAsAdmin = shInfo.RunAsAdmin, + ShowWindowCommand = shInfo.ShowWindowCommand, + IsUrl = isUrl, + }; + } + else + { + return new ShortcutItem(null) + { + PrimaryItemAttribute = shInfo.IsFolder ? StorageItemTypes.Folder : StorageItemTypes.File, + FileExtension = itemFileExtension, + IsHiddenItem = isHidden, + Opacity = opacity, + FileImage = null, + LoadFileIcon = !shInfo.IsFolder && itemThumbnailImgVis, + ItemNameRaw = itemName, + ItemDateModifiedReal = itemModifiedDate, + ItemDateAccessedReal = itemLastAccessDate, + ItemDateCreatedReal = itemCreatedDate, + ItemType = isUrl ? Strings.ShortcutWebLinkFileType.GetLocalizedResource() : Strings.Shortcut.GetLocalizedResource(), + ItemPath = itemPath, + FileSize = itemSize, + FileSizeBytes = itemSizeBytes, + TargetPath = shInfo.TargetPath, + Arguments = shInfo.Arguments, + WorkingDirectory = shInfo.WorkingDirectory, + RunAsAdmin = shInfo.RunAsAdmin, + ShowWindowCommand = shInfo.ShowWindowCommand, + IsUrl = isUrl, + }; + } } else if (App.LibraryManager.TryGetLibrary(itemPath, out LibraryLocationItem library)) { @@ -400,8 +456,6 @@ CancellationToken cancellationToken }; } } - - return null; } } } diff --git a/src/Files.App/Utils/Storage/Helpers/DeviceManager.cs b/src/Files.App/Utils/Storage/Helpers/DeviceManager.cs index 587c899794a1..26e75267e583 100644 --- a/src/Files.App/Utils/Storage/Helpers/DeviceManager.cs +++ b/src/Files.App/Utils/Storage/Helpers/DeviceManager.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.Management.Infrastructure; diff --git a/src/Files.App/Utils/Storage/Helpers/DriveHelpers.cs b/src/Files.App/Utils/Storage/Helpers/DriveHelpers.cs index 430257365c22..ead6b80b88fe 100644 --- a/src/Files.App/Utils/Storage/Helpers/DriveHelpers.cs +++ b/src/Files.App/Utils/Storage/Helpers/DriveHelpers.cs @@ -1,22 +1,22 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. +using DiscUtils.Udf; using Microsoft.Management.Infrastructure; -using System.IO; using Windows.Devices.Enumeration; using Windows.Devices.Portable; using Windows.Storage; using Windows.Storage.FileProperties; -using DiscUtils.Udf; +using Windows.Win32; +using Windows.Win32.Foundation; namespace Files.App.Utils.Storage { public static class DriveHelpers { - public static Task EjectDeviceAsync(string path) + public static async void EjectDeviceAsync(string path) { - var removableDevice = new RemovableDevice(path); - return removableDevice.EjectAsync(); + await ContextMenu.InvokeVerb("eject", path); } public static string GetVolumeId(string driveName) @@ -42,26 +42,23 @@ public static async Task CheckEmptyDrive(string? drivePath) return false; var ejectButton = await DialogDisplayHelper.ShowDialogAsync( - "InsertDiscDialog/Title".GetLocalizedResource(), - string.Format("InsertDiscDialog/Text".GetLocalizedResource(), matchingDrive.Path), - "InsertDiscDialog/OpenDriveButton".GetLocalizedResource(), - "Close".GetLocalizedResource()); + Strings.InsertDiscDialog_Title.GetLocalizedResource(), + string.Format(Strings.InsertDiscDialog_Text.GetLocalizedResource(), matchingDrive.Path), + Strings.InsertDiscDialog_OpenDriveButton.GetLocalizedResource(), + Strings.Close.GetLocalizedResource()); if (ejectButton) - { - var result = await EjectDeviceAsync(matchingDrive.Path); - await UIHelpers.ShowDeviceEjectResultAsync(matchingDrive.Type, result); - } + EjectDeviceAsync(matchingDrive.Path); return true; } public static async Task GetRootFromPathAsync(string devicePath) { - if (!Path.IsPathRooted(devicePath)) + if (!SystemIO.Path.IsPathRooted(devicePath)) return null; var drivesViewModel = Ioc.Default.GetRequiredService(); - var rootPath = Path.GetPathRoot(devicePath); + var rootPath = SystemIO.Path.GetPathRoot(devicePath); if (devicePath.StartsWith(@"\\?\", StringComparison.Ordinal)) // USB device { // Check among already discovered drives @@ -95,10 +92,13 @@ public static async Task GetRootFromPathAsync(string devi } } // Network share - else if (devicePath.StartsWith(@"\\", StringComparison.Ordinal) && - !devicePath.StartsWith(@"\\SHELL\", StringComparison.Ordinal)) + else if ( + (devicePath.StartsWith(@"\\", StringComparison.Ordinal) || + GetDriveType(new SystemIO.DriveInfo(devicePath)) is DriveType.Network) && + !devicePath.StartsWith(@"\\SHELL\", StringComparison.Ordinal) + ) { - int lastSepIndex = rootPath.LastIndexOf(@"\", StringComparison.Ordinal); + int lastSepIndex = rootPath.LastIndexOf('\\'); rootPath = lastSepIndex > 1 ? rootPath.Substring(0, lastSepIndex) : rootPath; // Remove share name return new StorageFolderWithPath(await StorageFolder.GetFolderFromPathAsync(rootPath), rootPath); } @@ -118,31 +118,52 @@ public static Data.Items.DriveType GetDriveType(System.IO.DriveInfo drive) return drive.DriveType switch { - System.IO.DriveType.CDRom => Data.Items.DriveType.CDRom, - System.IO.DriveType.Fixed => Data.Items.DriveType.Fixed, - System.IO.DriveType.Network => Data.Items.DriveType.Network, - System.IO.DriveType.NoRootDirectory => Data.Items.DriveType.NoRootDirectory, - System.IO.DriveType.Ram => Data.Items.DriveType.Ram, - System.IO.DriveType.Removable => Data.Items.DriveType.Removable, + SystemIO.DriveType.CDRom => Data.Items.DriveType.CDRom, + SystemIO.DriveType.Fixed => Data.Items.DriveType.Fixed, + SystemIO.DriveType.Network => Data.Items.DriveType.Network, + SystemIO.DriveType.NoRootDirectory => Data.Items.DriveType.NoRootDirectory, + SystemIO.DriveType.Ram => Data.Items.DriveType.Ram, + SystemIO.DriveType.Removable => Data.Items.DriveType.Removable, _ => Data.Items.DriveType.Unknown, }; } - public static string GetExtendedDriveLabel(DriveInfo drive) + public static unsafe string GetExtendedDriveLabel(SystemIO.DriveInfo drive) { return SafetyExtensions.IgnoreExceptions(() => { - if (drive.DriveType is not System.IO.DriveType.CDRom || drive.DriveFormat is not "UDF") + if (drive.DriveType is not SystemIO.DriveType.CDRom || drive.DriveFormat is not "UDF") return drive.VolumeLabel; + return SafetyExtensions.IgnoreExceptions(() => { - var dosDevicePath = Vanara.PInvoke.Kernel32.QueryDosDevice(drive.Name).FirstOrDefault(); + string dosDevicePath = ""; + + fixed (char* cDeviceName = drive.Name) + { + var cch = PInvoke.QueryDosDevice(cDeviceName, null, 0u); + + fixed (char* cTargetPath = new char[cch]) + { + PWSTR pszTargetPath = new(cTargetPath); + PInvoke.QueryDosDevice(cDeviceName, pszTargetPath, 0u); + dosDevicePath = pszTargetPath.ToString(); + } + } + if (string.IsNullOrEmpty(dosDevicePath)) return drive.VolumeLabel; - using var driveStream = new FileStream(dosDevicePath.Replace(@"\Device\", @"\\.\"), FileMode.Open, FileAccess.Read); + + using var driveStream = new SystemIO.FileStream( + dosDevicePath.Replace(@"\Device\", @"\\.\"), + SystemIO.FileMode.Open, + SystemIO.FileAccess.Read); + using var udf = new UdfReader(driveStream); + return udf.VolumeLabel; }) ?? drive.VolumeLabel; + }) ?? ""; } @@ -151,4 +172,4 @@ public static async Task GetThumbnailAsync(StorageFolder f => folder.GetThumbnailAsync(ThumbnailMode.SingleItem, 40, ThumbnailOptions.UseCurrentScale).AsTask() ); } -} \ No newline at end of file +} diff --git a/src/Files.App/Utils/Storage/Helpers/ErrorCodeConverter.cs b/src/Files.App/Utils/Storage/Helpers/ErrorCodeConverter.cs index 331a1ccc3009..a3be94bca180 100644 --- a/src/Files.App/Utils/Storage/Helpers/ErrorCodeConverter.cs +++ b/src/Files.App/Utils/Storage/Helpers/ErrorCodeConverter.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.Storage { diff --git a/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs b/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs index 527d7b2936a5..c08c961aff91 100644 --- a/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs +++ b/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.Views.Properties; using Microsoft.UI; @@ -7,9 +7,7 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media.Animation; -using Microsoft.Windows.ApplicationModel.Resources; using System.Collections.Concurrent; -using Windows.ApplicationModel; using Windows.Graphics; using Windows.Win32; @@ -22,12 +20,6 @@ public static class FilePropertiesHelpers { private static IAppThemeModeService AppThemeModeService { get; } = Ioc.Default.GetRequiredService(); - /// - /// Whether LayoutDirection (FlowDirection) is set to right-to-left (RTL) - /// - public static readonly bool FlowDirectionSettingIsRightToLeft = - new ResourceManager().CreateResourceContext().QualifierValues["LayoutDirection"] == "RTL"; - /// /// Get window handle (hWnd) of the given properties window instance /// @@ -37,7 +29,7 @@ public static nint GetWindowHandle(Window w) => WinRT.Interop.WindowNative.GetWindowHandle(w); private static TaskCompletionSource? PropertiesWindowsClosingTCS; - private static readonly BlockingCollection WindowCache = []; + private static readonly BlockingCollection WindowCache = []; /// /// Open properties window @@ -75,7 +67,7 @@ public static void OpenPropertiesWindow(IShellPage associatedInstance) foreach (var drive in drives) { // Current folder is drive - if (drive.Path.Equals(folder.ItemPath)) + if (drive.Id.Equals(folder.ItemPath)) { item = drive; break; @@ -99,27 +91,26 @@ public static void OpenPropertiesWindow(object item, IShellPage associatedInstan var frame = new Frame { - RequestedTheme = (ElementTheme)AppThemeModeService.AppThemeMode + RequestedTheme = AppThemeModeService.AppThemeMode }; - WinUIEx.WindowEx propertiesWindow; - if (!WindowCache.TryTake(out propertiesWindow!)) + if (!WindowCache.TryTake(out var propertiesWindow)) { - propertiesWindow = new(); + propertiesWindow = new(460, 550); propertiesWindow.Closed += PropertiesWindow_Closed; } + var width = Math.Max(1, Convert.ToInt32(800 * App.AppModel.AppWindowDPI)); + var height = Math.Max(1, Convert.ToInt32(500 * App.AppModel.AppWindowDPI)); + + propertiesWindow.AppWindow.Resize(new(width, height)); propertiesWindow.IsMinimizable = false; propertiesWindow.IsMaximizable = false; - propertiesWindow.MinWidth = 460; - propertiesWindow.MinHeight = 550; - propertiesWindow.Width = 800; - propertiesWindow.Height = 550; propertiesWindow.Content = frame; propertiesWindow.SystemBackdrop = new AppSystemBackdrop(true); var appWindow = propertiesWindow.AppWindow; - appWindow.Title = "Properties".GetLocalizedResource(); + appWindow.Title = Strings.Properties.GetLocalizedResource(); appWindow.TitleBar.ExtendsContentIntoTitleBar = true; appWindow.TitleBar.ButtonBackgroundColor = Colors.Transparent; appWindow.TitleBar.ButtonInactiveBackgroundColor = Colors.Transparent; @@ -127,7 +118,7 @@ public static void OpenPropertiesWindow(object item, IShellPage associatedInstan appWindow.SetIcon(AppLifecycleHelper.AppIconPath); frame.Navigate( - typeof(Views.Properties.MainPropertiesPage), + typeof(MainPropertiesPage), new PropertiesPageNavigationParameter { Parameter = item, @@ -160,7 +151,7 @@ public static void OpenPropertiesWindow(object item, IShellPage associatedInstan // So instead of destroying the Window object, cache it and reuse it as a workaround. private static void PropertiesWindow_Closed(object sender, WindowEventArgs args) { - if (!App.AppModel.IsMainWindowClosed && sender is WinUIEx.WindowEx window) + if (!App.AppModel.IsMainWindowClosed && sender is WindowEx window) { args.Handled = true; diff --git a/src/Files.App/Utils/Storage/Helpers/FileThumbnailHelper.cs b/src/Files.App/Utils/Storage/Helpers/FileThumbnailHelper.cs index ecf8a83f4a28..809088ab2451 100644 --- a/src/Files.App/Utils/Storage/Helpers/FileThumbnailHelper.cs +++ b/src/Files.App/Utils/Storage/Helpers/FileThumbnailHelper.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.Storage.FileProperties; @@ -13,8 +13,14 @@ public static class FileThumbnailHelper public static async Task GetIconAsync(string path, uint requestedSize, bool isFolder, IconOptions iconOptions) { var size = iconOptions.HasFlag(IconOptions.UseCurrentScale) ? requestedSize * App.AppModel.AppWindowDPI : requestedSize; + // Ensure size is at least 1 to prevent layout errors + size = Math.Max(1, size); - return await Win32Helper.StartSTATask(() => Win32Helper.GetIcon(path, (int)size, isFolder, iconOptions)); + var resolvedPath = path.StartsWith(@"\\?\", StringComparison.Ordinal) + ? MtpHelpers.ResolveMtpShellPath(path) ?? path + : path; + + return await STATask.Run(() => Win32Helper.GetIcon(resolvedPath, (int)size, isFolder, iconOptions), App.Logger); } /// @@ -24,7 +30,7 @@ public static class FileThumbnailHelper /// /// public static async Task GetIconOverlayAsync(string path, bool isFolder) - => await Win32Helper.StartSTATask(() => Win32Helper.GetIconOverlay(path, isFolder)); + => await STATask.Run(() => Win32Helper.GetIconOverlay(path, isFolder), App.Logger); [Obsolete] public static async Task LoadIconFromPathAsync(string filePath, uint thumbnailSize, ThumbnailMode thumbnailMode, ThumbnailOptions thumbnailOptions, bool isFolder = false) diff --git a/src/Files.App/Utils/Storage/Helpers/FilesystemResult.cs b/src/Files.App/Utils/Storage/Helpers/FilesystemResult.cs index f791fbcdb56b..ccb51f9209c8 100644 --- a/src/Files.App/Utils/Storage/Helpers/FilesystemResult.cs +++ b/src/Files.App/Utils/Storage/Helpers/FilesystemResult.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.Win32.Foundation; @@ -16,8 +16,8 @@ public class FilesystemResult public static implicit operator bool(FilesystemResult res) => res?.ErrorCode is FileSystemStatusCode.Success; public static explicit operator FilesystemResult(bool res) => new(res ? FileSystemStatusCode.Success : FileSystemStatusCode.Generic); - - + + public static implicit operator BOOL(FilesystemResult res) => res?.ErrorCode is FileSystemStatusCode.Success; public static explicit operator FilesystemResult(BOOL res) => new(res ? FileSystemStatusCode.Success : FileSystemStatusCode.Generic); } diff --git a/src/Files.App/Utils/Storage/Helpers/FilesystemTasks.cs b/src/Files.App/Utils/Storage/Helpers/FilesystemTasks.cs index 490a251ee5c8..924845641754 100644 --- a/src/Files.App/Utils/Storage/Helpers/FilesystemTasks.cs +++ b/src/Files.App/Utils/Storage/Helpers/FilesystemTasks.cs @@ -1,7 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.App.Data.Exceptions; using System.IO; using System.Runtime.InteropServices; using Windows.Storage; diff --git a/src/Files.App/Utils/Storage/Helpers/FolderHelpers.cs b/src/Files.App/Utils/Storage/Helpers/FolderHelpers.cs index e59c3ec330de..74f0a37099f1 100644 --- a/src/Files.App/Utils/Storage/Helpers/FolderHelpers.cs +++ b/src/Files.App/Utils/Storage/Helpers/FolderHelpers.cs @@ -1,8 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.IO; -using static Files.App.Helpers.Win32Helper; namespace Files.App.Utils.Storage { diff --git a/src/Files.App/Utils/Storage/Helpers/FtpHelpers.cs b/src/Files.App/Utils/Storage/Helpers/FtpHelpers.cs index 6bae7ecea7ec..14e80904a818 100644 --- a/src/Files.App/Utils/Storage/Helpers/FtpHelpers.cs +++ b/src/Files.App/Utils/Storage/Helpers/FtpHelpers.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using FluentFTP; @@ -33,7 +33,7 @@ public static bool VerifyFtpPath(string path) var authority = GetFtpAuthority(path); var index = authority.IndexOf(':', StringComparison.Ordinal); - return index == -1 || ushort.TryParse(authority.Substring(index + 1), out _); + return index == -1 || ushort.TryParse(authority.AsSpan(index + 1), out _); } public static string GetFtpHost(string path) @@ -57,7 +57,7 @@ public static ushort GetFtpPort(string path) public static string GetFtpAuthority(string path) { - path = path.Replace("\\", "/", StringComparison.Ordinal); + path = path.Replace('\\', '/'); if (Uri.TryCreate(path, UriKind.Absolute, out var uri)) return uri.Authority; return string.Empty; @@ -65,17 +65,17 @@ public static string GetFtpAuthority(string path) public static string GetFtpPath(string path) { - path = path.Replace("\\", "/", StringComparison.Ordinal); + path = path.Replace('\\', '/'); var schemaIndex = path.IndexOf("://", StringComparison.Ordinal) + 3; - var hostIndex = path.IndexOf("/", schemaIndex, StringComparison.Ordinal); + var hostIndex = path.IndexOf('/', schemaIndex); return hostIndex == -1 ? "/" : path.Substring(hostIndex); } public static int GetRootIndex(string path) { - path = path.Replace("\\", "/", StringComparison.Ordinal); + path = path.Replace('\\', '/'); var schemaIndex = path.IndexOf("://", StringComparison.Ordinal) + 3; - return path.IndexOf("/", schemaIndex, StringComparison.Ordinal); + return path.IndexOf('/', schemaIndex); } } } \ No newline at end of file diff --git a/src/Files.App/Utils/Storage/Helpers/IStorageItemWithPath.cs b/src/Files.App/Utils/Storage/Helpers/IStorageItemWithPath.cs index c4a9aeb1084c..19f080e987a9 100644 --- a/src/Files.App/Utils/Storage/Helpers/IStorageItemWithPath.cs +++ b/src/Files.App/Utils/Storage/Helpers/IStorageItemWithPath.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.Storage; diff --git a/src/Files.App/Utils/Storage/Helpers/MtpHelpers.cs b/src/Files.App/Utils/Storage/Helpers/MtpHelpers.cs new file mode 100644 index 000000000000..38b7463ecfc5 --- /dev/null +++ b/src/Files.App/Utils/Storage/Helpers/MtpHelpers.cs @@ -0,0 +1,71 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using System.Collections.Concurrent; +using System.IO; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.UI.Shell; + +namespace Files.App.Utils.Storage +{ + public static class MtpHelpers + { + private static readonly ConcurrentDictionary _deviceParsingNames = new(StringComparer.OrdinalIgnoreCase); + + /// + /// Resolves a \\?\DeviceName\path to a shell Portable Devices namespace path + /// so that shell APIs like work correctly. + /// + public unsafe static string? ResolveMtpShellPath(string mtpPath) + { + var withoutPrefix = mtpPath.AsSpan(4); + var sep = withoutPrefix.IndexOf('\\'); + var deviceName = (sep >= 0 ? withoutPrefix[..sep] : withoutPrefix).ToString(); + + if (!_deviceParsingNames.TryGetValue(deviceName, out var parsingName)) + _deviceParsingNames[deviceName] = parsingName = FindDeviceParsingName(deviceName); + + return parsingName is null ? null + : sep >= 0 ? Path.Combine(parsingName, withoutPrefix[(sep + 1)..].ToString()) + : parsingName; + } + + private unsafe static string? FindDeviceParsingName(string deviceName) + { + using ComPtr pComputer = default; + Guid folderId = new("0AC0837C-BBF8-452A-850D-79D08E667CA7"); + PInvoke.SHGetKnownFolderItem(&folderId, KNOWN_FOLDER_FLAG.KF_FLAG_DEFAULT, HANDLE.Null, IID.IID_IShellItem, (void**)pComputer.GetAddressOf()); + + if (pComputer.IsNull) + return null; + + using ComPtr pEnum = default; + pComputer.Get()->BindToHandler(null, BHID.BHID_EnumItems, IID.IID_IEnumShellItems, (void**)pEnum.GetAddressOf()); + + if (pEnum.IsNull) + return null; + + while (true) + { + using ComPtr pChild = default; + if (pEnum.Get()->Next(1, pChild.GetAddressOf()) != HRESULT.S_OK) + break; + + pChild.Get()->GetDisplayName(SIGDN.SIGDN_NORMALDISPLAY, out var szName); + var name = szName.ToString(); + PInvoke.CoTaskMemFree(szName.Value); + + if (!deviceName.StartsWith(name, StringComparison.OrdinalIgnoreCase)) + continue; + + pChild.Get()->GetDisplayName(SIGDN.SIGDN_DESKTOPABSOLUTEPARSING, out var szParsing); + var result = szParsing.ToString(); + PInvoke.CoTaskMemFree(szParsing.Value); + return result; + } + + return null; + } + } +} diff --git a/src/Files.App/Utils/Storage/Helpers/StorageFileExtensions.cs b/src/Files.App/Utils/Storage/Helpers/StorageFileExtensions.cs index 8d2c8fd6ca33..ba9ab3ba408a 100644 --- a/src/Files.App/Utils/Storage/Helpers/StorageFileExtensions.cs +++ b/src/Files.App/Utils/Storage/Helpers/StorageFileExtensions.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Collections.Immutable; using System.IO; @@ -139,7 +139,12 @@ public static async Task> GetDirectoryPathComponentsWithDispla foreach (var item in pathBoxItems) { if (item.Path == "Home") - item.Title = "Home".GetLocalizedResource(); + item.Title = Strings.Home.GetLocalizedResource(); + if (item.Path == "ReleaseNotes") + item.Title = Strings.ReleaseNotes.GetLocalizedResource(); + // TODO add settings page + //if (item.Path == "Settings") + // item.Title = Strings.Settings.GetLocalizedResource(); else { BaseStorageFolder folder = await FilesystemTasks.Wrap(() => DangerousGetFolderFromPathAsync(item.Path)); @@ -147,6 +152,8 @@ public static async Task> GetDirectoryPathComponentsWithDispla if (!string.IsNullOrEmpty(folder?.DisplayName)) item.Title = folder.DisplayName; } + + item.ChevronToolTip = string.Format(Strings.BreadcrumbBarChevronButtonToolTip.GetLocalizedResource(), item.Title); } return pathBoxItems; @@ -287,15 +294,15 @@ private static PathBoxItem GetPathItem(string component, string path) if (component.StartsWith(Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.Ordinal)) { // Handle the recycle bin: use the localized folder name - title = "RecycleBin".GetLocalizedResource(); + title = Strings.RecycleBin.GetLocalizedResource(); } else if (component.StartsWith(Constants.UserEnvironmentPaths.MyComputerPath, StringComparison.Ordinal)) { - title = "ThisPC".GetLocalizedResource(); + title = Strings.ThisPC.GetLocalizedResource(); } else if (component.StartsWith(Constants.UserEnvironmentPaths.NetworkFolderPath, StringComparison.Ordinal)) { - title = "Network".GetLocalizedResource(); + title = Strings.Network.GetLocalizedResource(); } else if (component.EndsWith(':')) { @@ -303,7 +310,7 @@ private static PathBoxItem GetPathItem(string component, string path) var drives = drivesViewModel.Drives.Cast(); var drive = drives.FirstOrDefault(y => y.ItemType is NavigationControlItemType.Drive && y.Path.Contains(component, StringComparison.OrdinalIgnoreCase)); - title = drive is not null ? drive.Text : string.Format("DriveWithLetter".GetLocalizedResource(), component); + title = drive is not null ? drive.Text : string.Format(Strings.DriveWithLetter.GetLocalizedResource(), component); } else { @@ -316,7 +323,8 @@ private static PathBoxItem GetPathItem(string component, string path) return new PathBoxItem() { Title = title, - Path = path + Path = path, + ChevronToolTip = string.Format(Strings.BreadcrumbBarChevronButtonToolTip.GetLocalizedResource(), title), }; } @@ -341,6 +349,12 @@ private static string ResolvePath(string path, bool isFtp) if (path.StartsWith("Home")) return "Home"; + if (path.StartsWith("ReleaseNotes")) + return "ReleaseNotes"; + + if (path.StartsWith("Settings")) + return "Settings"; + if (ShellStorageFolder.IsShellPath(path)) return ShellHelpers.ResolveShellPath(path); diff --git a/src/Files.App/Utils/Storage/Helpers/StorageFileWithPath.cs b/src/Files.App/Utils/Storage/Helpers/StorageFileWithPath.cs index 6a64258ce189..9fb6dccfbffe 100644 --- a/src/Files.App/Utils/Storage/Helpers/StorageFileWithPath.cs +++ b/src/Files.App/Utils/Storage/Helpers/StorageFileWithPath.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.Storage; using IO = System.IO; diff --git a/src/Files.App/Utils/Storage/Helpers/StorageFolderWithPath.cs b/src/Files.App/Utils/Storage/Helpers/StorageFolderWithPath.cs index 4cbfdcde9374..d9d5eaff4cfd 100644 --- a/src/Files.App/Utils/Storage/Helpers/StorageFolderWithPath.cs +++ b/src/Files.App/Utils/Storage/Helpers/StorageFolderWithPath.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.Storage; using IO = System.IO; diff --git a/src/Files.App/Utils/Storage/Helpers/StorageHelpers.cs b/src/Files.App/Utils/Storage/Helpers/StorageHelpers.cs index 8c6555311235..5af8817424d7 100644 --- a/src/Files.App/Utils/Storage/Helpers/StorageHelpers.cs +++ b/src/Files.App/Utils/Storage/Helpers/StorageHelpers.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.Shared.Helpers; using System.Runtime.InteropServices; diff --git a/src/Files.App/Utils/Storage/Helpers/StorageItemIconHelpers.cs b/src/Files.App/Utils/Storage/Helpers/StorageItemIconHelpers.cs index f404fe29e970..ff6dbefba6ec 100644 --- a/src/Files.App/Utils/Storage/Helpers/StorageItemIconHelpers.cs +++ b/src/Files.App/Utils/Storage/Helpers/StorageItemIconHelpers.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.Storage; using Windows.Storage.FileProperties; diff --git a/src/Files.App/Utils/Storage/Helpers/StorageSenseHelper.cs b/src/Files.App/Utils/Storage/Helpers/StorageSenseHelper.cs index 0299f7570cba..b66db47004a5 100644 --- a/src/Files.App/Utils/Storage/Helpers/StorageSenseHelper.cs +++ b/src/Files.App/Utils/Storage/Helpers/StorageSenseHelper.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.Foundation.Metadata; using Windows.System; @@ -10,7 +10,7 @@ internal sealed class StorageSenseHelper { public static async Task OpenStorageSenseAsync(string path) { - if (!path.StartsWith(Environment.GetEnvironmentVariable("SystemDrive"), StringComparison.OrdinalIgnoreCase) + if (!path.StartsWith(Constants.UserEnvironmentPaths.SystemDrivePath, StringComparison.OrdinalIgnoreCase) && ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) { LaunchHelper.LaunchSettings("page=SettingsPageStorageSenseStorageOverview&target=SystemSettings_StorageSense_VolumeListLink"); diff --git a/src/Files.App/Utils/Storage/Helpers/SyncRootHelpers.cs b/src/Files.App/Utils/Storage/Helpers/SyncRootHelpers.cs new file mode 100644 index 000000000000..042fb8564e1a --- /dev/null +++ b/src/Files.App/Utils/Storage/Helpers/SyncRootHelpers.cs @@ -0,0 +1,71 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.Win32; +using Windows.Win32; +using Windows.Win32.System.Com; +using Windows.Win32.System.WinRT; +using WinRT; + +namespace Files.App.Utils.Storage +{ + internal static class SyncRootHelpers + { + private static unsafe (bool Success, ulong Capacity, ulong Used) GetSyncRootQuotaFromSyncRootId(string syncRootId) + { + using var key = Registry.LocalMachine.OpenSubKey($"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\SyncRootManager\\{syncRootId}"); + if (key?.GetValue("StorageProviderStatusUISourceFactory") is not string factoryClsidString || + !Guid.TryParse(factoryClsidString, out var factoryClsid)) + return (false, 0, 0); + + ulong ulTotalSize = 0ul, ulUsedSize = 0ul; + using ComPtr pStorageProviderStatusUISourceFactory = default; + using ComPtr pStorageProviderStatusUISource = default; + using ComPtr pStorageProviderStatusUI = default; + using ComPtr pStorageProviderQuotaUI = default; + + if (PInvoke.CoCreateInstance( + &factoryClsid, + null, + CLSCTX.CLSCTX_LOCAL_SERVER, + IID.IID_IStorageProviderStatusUISourceFactory, + (void**)pStorageProviderStatusUISourceFactory.GetAddressOf()).ThrowIfFailedOnDebug().Failed) + return (false, 0, 0); + + var syncRootIdHString = new MarshalString.Pinnable(syncRootId); + fixed (char* pSyncRootIdHString = syncRootIdHString) + { + if (pStorageProviderStatusUISourceFactory.Get()->GetStatusUISource(syncRootIdHString.GetAbi(), pStorageProviderStatusUISource.GetAddressOf()).ThrowIfFailedOnDebug().Failed || + pStorageProviderStatusUISource.Get()->GetStatusUI(pStorageProviderStatusUI.GetAddressOf()).ThrowIfFailedOnDebug().Failed || + pStorageProviderStatusUI.Get()->GetQuotaUI(pStorageProviderQuotaUI.GetAddressOf()).ThrowIfFailedOnDebug().Failed || + pStorageProviderQuotaUI.Get()->GetQuotaTotalInBytes(&ulTotalSize).ThrowIfFailedOnDebug().Failed || + pStorageProviderQuotaUI.Get()->GetQuotaUsedInBytes(&ulUsedSize).ThrowIfFailedOnDebug().Failed) + return (false, 0, 0); + } + + return (true, ulTotalSize, ulUsedSize); + } + + public static async Task<(bool Success, ulong Capacity, ulong Used)> GetSyncRootQuotaAsync(string path) + { + Windows.Storage.StorageFolder folder = await Windows.Storage.StorageFolder.GetFolderFromPathAsync(path); + Windows.Storage.Provider.StorageProviderSyncRootInfo? syncRootInfo = null; + + try + { + syncRootInfo = Windows.Storage.Provider.StorageProviderSyncRootManager.GetSyncRootInformationForFolder(folder); + } + catch + { + return (false, 0, 0); + } + + if (syncRootInfo is null || syncRootInfo.Id is null) + { + return (false, 0, 0); + } + + return GetSyncRootQuotaFromSyncRootId(syncRootInfo.Id); + } + } +} diff --git a/src/Files.App/Utils/Storage/History/IStorageHistory.cs b/src/Files.App/Utils/Storage/History/IStorageHistory.cs index 2a558c98b4bc..e1247978f08a 100644 --- a/src/Files.App/Utils/Storage/History/IStorageHistory.cs +++ b/src/Files.App/Utils/Storage/History/IStorageHistory.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.Storage { diff --git a/src/Files.App/Utils/Storage/History/IStorageHistoryOperations.cs b/src/Files.App/Utils/Storage/History/IStorageHistoryOperations.cs index c3e3973f6a01..8b4117f266f2 100644 --- a/src/Files.App/Utils/Storage/History/IStorageHistoryOperations.cs +++ b/src/Files.App/Utils/Storage/History/IStorageHistoryOperations.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.Storage { diff --git a/src/Files.App/Utils/Storage/History/StorageHistory.cs b/src/Files.App/Utils/Storage/History/StorageHistory.cs index 2cfdd2eb09e2..ad434d36db40 100644 --- a/src/Files.App/Utils/Storage/History/StorageHistory.cs +++ b/src/Files.App/Utils/Storage/History/StorageHistory.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.Storage { diff --git a/src/Files.App/Utils/Storage/History/StorageHistoryHelpers.cs b/src/Files.App/Utils/Storage/History/StorageHistoryHelpers.cs index d2495cbc90d2..c798d35af4bc 100644 --- a/src/Files.App/Utils/Storage/History/StorageHistoryHelpers.cs +++ b/src/Files.App/Utils/Storage/History/StorageHistoryHelpers.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.Storage { - public sealed class StorageHistoryHelpers : IDisposable + public sealed partial class StorageHistoryHelpers : IDisposable { private IStorageHistoryOperations operations; diff --git a/src/Files.App/Utils/Storage/History/StorageHistoryOperations.cs b/src/Files.App/Utils/Storage/History/StorageHistoryOperations.cs index c78375318323..76a07c913a95 100644 --- a/src/Files.App/Utils/Storage/History/StorageHistoryOperations.cs +++ b/src/Files.App/Utils/Storage/History/StorageHistoryOperations.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.IO; using Windows.Storage; namespace Files.App.Utils.Storage { - public sealed class StorageHistoryOperations : IStorageHistoryOperations + public sealed partial class StorageHistoryOperations : IStorageHistoryOperations { private IFilesystemHelpers helpers; private ShellFilesystemOperations operations; diff --git a/src/Files.App/Utils/Storage/History/StorageHistoryWrapper.cs b/src/Files.App/Utils/Storage/History/StorageHistoryWrapper.cs index ba846486dcc3..d9d8839aa5a6 100644 --- a/src/Files.App/Utils/Storage/History/StorageHistoryWrapper.cs +++ b/src/Files.App/Utils/Storage/History/StorageHistoryWrapper.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.Storage { - public sealed class StorageHistoryWrapper : IDisposable + public sealed partial class StorageHistoryWrapper : IDisposable { private int index = -1; diff --git a/src/Files.App/Utils/Storage/Operations/FileOperationsHelpers.cs b/src/Files.App/Utils/Storage/Operations/FileOperationsHelpers.cs index b60655b1a7bf..9b8d506e50a7 100644 --- a/src/Files.App/Utils/Storage/Operations/FileOperationsHelpers.cs +++ b/src/Files.App/Utils/Storage/Operations/FileOperationsHelpers.cs @@ -1,21 +1,21 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.Utils.Storage.Operations; using Files.Shared.Helpers; using Microsoft.Extensions.Logging; using System.Collections.Concurrent; using System.IO; -using System.Runtime.InteropServices; using System.Security.Principal; using Tulpep.ActiveDirectoryObjectPicker; using Vanara.PInvoke; using Vanara.Windows.Shell; using Windows.ApplicationModel.DataTransfer; +using Windows.Win32.UI.WindowsAndMessaging; namespace Files.App.Utils.Storage { - public sealed class FileOperationsHelpers + public sealed partial class FileOperationsHelpers { private static readonly Ole32.PROPERTYKEY PKEY_FilePlaceholderStatus = new Ole32.PROPERTYKEY(new Guid("B2F9B9D6-FEC4-4DD5-94D7-8957488C807B"), 2); private const uint PS_CLOUDFILE_PLACEHOLDER = 8; @@ -24,7 +24,7 @@ public sealed class FileOperationsHelpers public static Task SetClipboard(string[] filesToCopy, DataPackageOperation operation) { - return Win32Helper.StartSTATask(() => + return STATask.Run(() => { System.Windows.Forms.Clipboard.Clear(); var fileList = new System.Collections.Specialized.StringCollection(); @@ -35,12 +35,12 @@ public static Task SetClipboard(string[] filesToCopy, DataPackageOperation opera data.SetFileDropList(fileList); data.SetData("Preferred DropEffect", dropEffect); System.Windows.Forms.Clipboard.SetDataObject(data, true); - }); + }, App.Logger); } public static Task<(bool, ShellOperationResult)> CreateItemAsync(string filePath, string fileOp, long ownerHwnd, bool asAdmin, string template = "", byte[]? dataBytes = null) { - return Win32Helper.StartSTATask(async () => + return STATask.Run(async () => { using var op = new ShellFileOperations2(); @@ -104,12 +104,12 @@ public static Task SetClipboard(string[] filesToCopy, DataPackageOperation opera } return (await createTcs.Task, shellOperationResult); - }); + }, App.Logger); } public static Task<(bool, ShellOperationResult)> TestRecycleAsync(string[] fileToDeletePath) { - return Win32Helper.StartSTATask(async () => + return STATask.Run(async () => { using var op = new ShellFileOperations2(); @@ -127,7 +127,7 @@ public static Task SetClipboard(string[] filesToCopy, DataPackageOperation opera { using var shi = new ShellItem(fileToDeletePath[i]); using var file = SafetyExtensions.IgnoreExceptions(() => GetFirstFile(shi)) ?? shi; - if ((uint?)file.Properties.GetValueOrDefault(PKEY_FilePlaceholderStatus) == PS_CLOUDFILE_PLACEHOLDER) + if (file.Properties.TryGetValue(PKEY_FilePlaceholderStatus, out var value) && value == PS_CLOUDFILE_PLACEHOLDER) { // Online only files cannot be tried for deletion, so they are treated as to be permanently deleted. shellOperationResult.Items.Add(new ShellOperationItemResult() @@ -192,7 +192,7 @@ public static Task SetClipboard(string[] filesToCopy, DataPackageOperation opera } return (await deleteTcs.Task, shellOperationResult); - }); + }, App.Logger); } public static Task<(bool, ShellOperationResult)> DeleteItemAsync(string[] fileToDeletePath, bool permanently, long ownerHwnd, bool asAdmin, IProgress progress, string operationID = "") @@ -206,6 +206,14 @@ public static Task SetClipboard(string[] filesToCopy, DataPackageOperation opera var cts = new CancellationTokenSource(); var sizeCalculator = new FileSizeCalculator(fileToDeletePath); + + // Track the count and update the progress + sizeCalculator.ItemsCountChanged += (newCount) => + { + fsProgress.ItemsCount = newCount; + fsProgress.Report(); + }; + var sizeTask = sizeCalculator.ComputeSizeAsync(cts.Token); sizeTask.ContinueWith(_ => { @@ -218,7 +226,7 @@ public static Task SetClipboard(string[] filesToCopy, DataPackageOperation opera fsProgress.Report(); progressHandler ??= new(); - return Win32Helper.StartSTATask(async () => + return STATask.Run(async () => { using var op = new ShellFileOperations2(); @@ -271,10 +279,6 @@ public static Task SetClipboard(string[] filesToCopy, DataPackageOperation opera // Right before deleting item op.PreDeleteItem += (s, e) => { - // E_FAIL, stops operation - if (!permanently && !e.Flags.HasFlag(ShellFileOperations.TransferFlags.DeleteRecycleIfPossible)) - throw new Win32Exception(HRESULT.COPYENGINE_E_RECYCLE_BIN_NOT_FOUND); - sizeCalculator.ForceComputeFileSize(e.SourceItem.GetParsingPath()); fsProgress.FileName = e.SourceItem.Name; fsProgress.Report(); @@ -327,7 +331,7 @@ public static Task SetClipboard(string[] filesToCopy, DataPackageOperation opera cts.Cancel(); return (await deleteTcs.Task, shellOperationResult); - }); + }, App.Logger); } public static Task<(bool, ShellOperationResult)> RenameItemAsync(string fileToRenamePath, string newName, bool overwriteOnRename, long ownerHwnd, bool asAdmin, string operationID = "") @@ -336,7 +340,7 @@ public static Task SetClipboard(string[] filesToCopy, DataPackageOperation opera progressHandler ??= new(); - return Win32Helper.StartSTATask(async () => + return STATask.Run(async () => { using var op = new ShellFileOperations2(); var shellOperationResult = new ShellOperationResult(); @@ -394,7 +398,7 @@ public static Task SetClipboard(string[] filesToCopy, DataPackageOperation opera progressHandler.RemoveOperation(operationID); return (await renameTcs.Task, shellOperationResult); - }); + }, App.Logger); } public static Task<(bool, ShellOperationResult)> MoveItemAsync(string[] fileToMovePath, string[] moveDestination, bool overwriteOnMove, long ownerHwnd, bool asAdmin, IProgress progress, string operationID = "") @@ -408,6 +412,14 @@ public static Task SetClipboard(string[] filesToCopy, DataPackageOperation opera var cts = new CancellationTokenSource(); var sizeCalculator = new FileSizeCalculator(fileToMovePath); + + // Track the count and update the progress + sizeCalculator.ItemsCountChanged += (newCount) => + { + fsProgress.ItemsCount = newCount; + fsProgress.Report(); + }; + var sizeTask = sizeCalculator.ComputeSizeAsync(cts.Token); sizeTask.ContinueWith(_ => { @@ -420,7 +432,7 @@ public static Task SetClipboard(string[] filesToCopy, DataPackageOperation opera fsProgress.Report(); progressHandler ??= new(); - return Win32Helper.StartSTATask(async () => + return STATask.Run(async () => { using var op = new ShellFileOperations2(); var shellOperationResult = new ShellOperationResult(); @@ -522,7 +534,7 @@ public static Task SetClipboard(string[] filesToCopy, DataPackageOperation opera cts.Cancel(); return (await moveTcs.Task, shellOperationResult); - }); + }, App.Logger); } public static Task<(bool, ShellOperationResult)> CopyItemAsync(string[] fileToCopyPath, string[] copyDestination, bool overwriteOnCopy, long ownerHwnd, bool asAdmin, IProgress progress, string operationID = "") @@ -536,6 +548,14 @@ public static Task SetClipboard(string[] filesToCopy, DataPackageOperation opera var cts = new CancellationTokenSource(); var sizeCalculator = new FileSizeCalculator(fileToCopyPath); + + // Track the count and update the progress + sizeCalculator.ItemsCountChanged += (newCount) => + { + fsProgress.ItemsCount = newCount; + fsProgress.Report(); + }; + var sizeTask = sizeCalculator.ComputeSizeAsync(cts.Token); sizeTask.ContinueWith(_ => { @@ -548,7 +568,7 @@ public static Task SetClipboard(string[] filesToCopy, DataPackageOperation opera fsProgress.Report(); progressHandler ??= new(); - return Win32Helper.StartSTATask(async () => + return STATask.Run(async () => { using var op = new ShellFileOperations2(); @@ -653,7 +673,7 @@ public static Task SetClipboard(string[] filesToCopy, DataPackageOperation opera cts.Cancel(); return (await copyTcs.Task, shellOperationResult); - }); + }, App.Logger); } public static void TryCancelOperation(string operationId) @@ -699,13 +719,13 @@ public static void TryCancelOperation(string operationId) } else if (FileExtensionHelpers.IsWebLinkFile(linkPath)) { - targetPath = await Win32Helper.StartSTATask(() => + targetPath = await STATask.Run(() => { var ipf = new Url.IUniformResourceLocator(); (ipf as System.Runtime.InteropServices.ComTypes.IPersistFile).Load(linkPath, 0); ipf.GetUrl(out var retVal); return retVal; - }); + }, App.Logger); return string.IsNullOrEmpty(targetPath) ? new ShellLinkItem { @@ -733,28 +753,41 @@ public static void TryCancelOperation(string operationId) } } - public static Task CreateOrUpdateLinkAsync(string linkSavePath, string targetPath, string arguments = "", string workingDirectory = "", bool runAsAdmin = false) + public static Task CreateOrUpdateLinkAsync(string linkSavePath, string targetPath, string arguments = "", string workingDirectory = "", bool runAsAdmin = false, SHOW_WINDOW_CMD showWindowCommand = SHOW_WINDOW_CMD.SW_NORMAL) { try { if (FileExtensionHelpers.IsShortcutFile(linkSavePath)) { using var newLink = new ShellLink(targetPath, arguments, workingDirectory); - newLink.RunAsAdministrator = runAsAdmin; + + // Check if the target is a file + if (File.Exists(targetPath)) + newLink.RunAsAdministrator = runAsAdmin; + newLink.SaveAs(linkSavePath); // Overwrite if exists + + // ShowState has to be set after SaveAs has been called, otherwise an UnauthorizedAccessException gets thrown in some cases + newLink.ShowState = (ShowWindowCommand)showWindowCommand; + return Task.FromResult(true); } else if (FileExtensionHelpers.IsWebLinkFile(linkSavePath)) { - return Win32Helper.StartSTATask(() => + return STATask.Run(() => { var ipf = new Url.IUniformResourceLocator(); ipf.SetUrl(targetPath, Url.IURL_SETURL_FLAGS.IURL_SETURL_FL_GUESS_PROTOCOL); (ipf as System.Runtime.InteropServices.ComTypes.IPersistFile).Save(linkSavePath, false); // Overwrite if exists return true; - }); + }, App.Logger); } } + catch (UnauthorizedAccessException ex) + { + // Could not create shortcut + App.Logger.LogInformation(ex, "Failed to create shortcut"); + } catch (Exception ex) { // Could not create shortcut @@ -766,25 +799,56 @@ public static Task CreateOrUpdateLinkAsync(string linkSavePath, string tar public static bool SetLinkIcon(string filePath, string iconFile, int iconIndex) { + var ext = Path.GetExtension(filePath).ToLowerInvariant(); + try { - using var link = new ShellLink(filePath, LinkResolution.NoUIWithMsgPump, default, TimeSpan.FromMilliseconds(100)); - link.IconLocation = new IconLocation(iconFile, iconIndex); - link.SaveAs(filePath); // Overwrite if exists - return true; + return ext switch + { + ".lnk" => TrySetLnkShortcutIcon(filePath, iconFile, iconIndex), + ".url" => TrySetUrlShortcutIcon(filePath, iconFile, iconIndex), + _ => false, + }; } catch (UnauthorizedAccessException) { - string psScript = $@" - $FilePath = '{filePath}' - $IconFile = '{iconFile}' - $IconIndex = '{iconIndex}' + string psScript; + filePath = filePath.Replace("'", "''"); + iconFile = iconFile.Replace("'", "''"); - $Shell = New-Object -ComObject WScript.Shell - $Shortcut = $Shell.CreateShortcut($FilePath) - $Shortcut.IconLocation = ""$IconFile, $IconIndex"" - $Shortcut.Save() - "; + if(ext == ".url") + { + psScript = $@" + $path = '{filePath}' + $iconFile = '{iconFile}' + $iconIndex = '{iconIndex}' + $content = Get-Content -LiteralPath $path + + $content = $content | Where-Object {{ $_ -notmatch '^IconFile=' -and $_ -notmatch '^IconIndex=' }} + + $newContent = foreach ($line in $content) {{ + $line + if ($line -eq '[InternetShortcut]') {{ + ""IconFile=$iconFile"" + ""IconIndex=$iconIndex"" + }} + }} + $newContent | Set-Content -LiteralPath $path -Encoding UTF8 + "; + } + else + { + psScript = $@" + $FilePath = '{filePath}' + $IconFile = '{iconFile}' + $IconIndex = '{iconIndex}' + + $Shell = New-Object -ComObject WScript.Shell + $Shortcut = $Shell.CreateShortcut($FilePath) + $Shortcut.IconLocation = ""$IconFile, $IconIndex"" + $Shortcut.Save() + "; + } var base64EncodedScript = Convert.ToBase64String(System.Text.Encoding.Unicode.GetBytes(psScript)); @@ -814,9 +878,70 @@ public static bool SetLinkIcon(string filePath, string iconFile, int iconIndex) return false; } + private static bool TrySetUrlShortcutIcon(string filePath, string iconFile, int iconIndex) + { + var fileExist = File.Exists(filePath); + if (!fileExist) + { + return false; + } + + var lines = File.ReadAllLines(filePath).ToList(); + var hasInternetShortcutHeader = lines.Any(l => l.Trim().Equals("[InternetShortcut]", StringComparison.OrdinalIgnoreCase)); + + if (!hasInternetShortcutHeader) + { + return false; + } + + lines.RemoveAll(l => + l.StartsWith("IconFile=", StringComparison.OrdinalIgnoreCase) || + l.StartsWith("IconIndex=", StringComparison.OrdinalIgnoreCase)); + + int index = 0; + int insertedIndex = 0; + foreach(var line in lines) + { + var isInternetShortcutHeader = line.Trim().Equals("[InternetShortcut]", StringComparison.OrdinalIgnoreCase); + if(isInternetShortcutHeader) + { + insertedIndex = index + 1; + break; + } + + index++; + } + + if(insertedIndex > 0) + { + lines.Insert(insertedIndex, $"IconFile={iconFile}"); + lines.Insert(insertedIndex + 1, $"IconIndex={iconIndex}"); + } + + File.WriteAllLines(filePath, lines); + + return true; + } + + private static bool TrySetLnkShortcutIcon(string filePath, string iconFile, int iconIndex) + { + using var link = new ShellLink(filePath, LinkResolution.NoUIWithMsgPump, default, TimeSpan.FromMilliseconds(100)); + if (string.IsNullOrWhiteSpace(iconFile)) + { + link.IconLocation = new IconLocation(string.Empty, 0); + } + else + { + link.IconLocation = new IconLocation(iconFile, iconIndex); + } + link.SaveAs(filePath); // Overwrite if exists + + return true; + } + public static Task OpenObjectPickerAsync(long hWnd) { - return Win32Helper.StartSTATask(() => + return STATask.Run(() => { var picker = new DirectoryObjectPickerDialog() { @@ -845,7 +970,7 @@ public static bool SetLinkIcon(string filePath, string iconFile, int iconIndex) } return null; - }); + }, App.Logger); } private static ShellItem? GetFirstFile(ShellItem shi) @@ -948,7 +1073,7 @@ private static void UpdateFileTagsDb(ShellFileOperations2.ShellFileOpEventArgs e public static void WaitForCompletion() => progressHandler?.WaitForCompletion(); - private sealed class ProgressHandler : Disposable + private sealed partial class ProgressHandler : Disposable { private readonly ManualResetEvent operationsCompletedEvent; @@ -958,14 +1083,12 @@ public sealed class OperationWithProgress public bool Canceled { get; set; } } - private readonly Shell32.ITaskbarList4? taskbar; private readonly ConcurrentDictionary operations; public HWND OwnerWindow { get; set; } public ProgressHandler() { - taskbar = Win32Helper.CreateTaskbarObject(); operations = new ConcurrentDictionary(); operationsCompletedEvent = new ManualResetEvent(true); } @@ -982,14 +1105,12 @@ public int Progress public void AddOperation(string uid) { operations.TryAdd(uid, new OperationWithProgress()); - UpdateTaskbarProgress(); operationsCompletedEvent.Reset(); } public void RemoveOperation(string uid) { operations.TryRemove(uid, out _); - UpdateTaskbarProgress(); if (!operations.Any()) { operationsCompletedEvent.Set(); @@ -1001,7 +1122,6 @@ public void UpdateOperation(string uid, double progress) if (operations.TryGetValue(uid, out var op)) { op.Progress = progress; - UpdateTaskbarProgress(); } } @@ -1015,23 +1135,6 @@ public void TryCancel(string uid) if (operations.TryGetValue(uid, out var op)) { op.Canceled = true; - UpdateTaskbarProgress(); - } - } - - private void UpdateTaskbarProgress() - { - if (OwnerWindow == HWND.NULL || taskbar is null) - { - return; - } - if (operations.Any()) - { - taskbar.SetProgressValue(OwnerWindow, (ulong)Progress, 100); - } - else - { - taskbar.SetProgressState(OwnerWindow, Shell32.TBPFLAG.TBPF_NOPROGRESS); } } @@ -1045,8 +1148,6 @@ protected override void Dispose(bool disposing) if (disposing) { operationsCompletedEvent?.Dispose(); - if (taskbar is not null) - Marshal.ReleaseComObject(taskbar); } } } @@ -1062,7 +1163,7 @@ private static string GetIncrementalName(bool overWriteOnCopy, string? filePathT var index = 2; var filePath = filePathToCheck; if (Path.HasExtension(filePathToCheck)) - filePath = filePathToCheck.Substring(0, filePathToCheck.LastIndexOf(".")); + filePath = filePathToCheck.Substring(0, filePathToCheck.LastIndexOf('.')); Func genFilePath = x => string.Concat([filePath, " (", x.ToString(), ")", Path.GetExtension(filePathToCheck)]); diff --git a/src/Files.App/Utils/Storage/Operations/FileSizeCalculator.cs b/src/Files.App/Utils/Storage/Operations/FileSizeCalculator.cs index 5a0e058550f2..a260e315ebfb 100644 --- a/src/Files.App/Utils/Storage/Operations/FileSizeCalculator.cs +++ b/src/Files.App/Utils/Storage/Operations/FileSizeCalculator.cs @@ -1,7 +1,10 @@ -using System.Collections.Concurrent; +// Copyright (c) Files Community +// Licensed under the MIT License. + +using System.Collections.Concurrent; using System.IO; -using Vanara.PInvoke; -using static Vanara.PInvoke.Kernel32; +using Windows.Win32; +using Windows.Win32.Storage.FileSystem; namespace Files.App.Utils.Storage.Operations { @@ -15,6 +18,8 @@ internal sealed class FileSizeCalculator public int ItemsCount => _computedFiles.Count; public bool Completed { get; private set; } + public event Action? ItemsCountChanged; + public FileSizeCalculator(params string[] paths) { _paths = paths; @@ -22,7 +27,18 @@ public FileSizeCalculator(params string[] paths) public async Task ComputeSizeAsync(CancellationToken cancellationToken = default) { - await Parallel.ForEachAsync(_paths, cancellationToken, async (path, token) => await Task.Factory.StartNew(() => + await Parallel.ForEachAsync( + _paths, + cancellationToken, + async (path, token) => await Task.Factory.StartNew(() => + { + ComputeSizeRecursively(path, token); + }, + token, + TaskCreationOptions.LongRunning, + TaskScheduler.Default)); + + unsafe void ComputeSizeRecursively(string path, CancellationToken token) { var queue = new Queue(); if (!Win32Helper.HasFileAttribute(path, FileAttributes.Directory)) @@ -35,65 +51,78 @@ await Parallel.ForEachAsync(_paths, cancellationToken, async (path, token) => aw while (queue.TryDequeue(out var directory)) { - using var hFile = FindFirstFileEx( - directory + "\\*.*", - FINDEX_INFO_LEVELS.FindExInfoBasic, - out WIN32_FIND_DATA findData, - FINDEX_SEARCH_OPS.FindExSearchNameMatch, - IntPtr.Zero, - FIND_FIRST.FIND_FIRST_EX_LARGE_FETCH); - - if (!hFile.IsInvalid) + WIN32_FIND_DATAW findData = default; + + fixed (char* pszFilePath = directory + "\\*.*") { - do + var hFile = PInvoke.FindFirstFileEx( + pszFilePath, + FINDEX_INFO_LEVELS.FindExInfoBasic, + &findData, + FINDEX_SEARCH_OPS.FindExSearchNameMatch, + null, + FIND_FIRST_EX_FLAGS.FIND_FIRST_EX_LARGE_FETCH); + + if (!hFile.IsNull) { - if ((findData.dwFileAttributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint) - // Skip symbolic links and junctions - continue; + do + { + FILE_FLAGS_AND_ATTRIBUTES attributes = (FILE_FLAGS_AND_ATTRIBUTES)findData.dwFileAttributes; - var itemPath = Path.Combine(directory, findData.cFileName); + if (attributes.HasFlag(FILE_FLAGS_AND_ATTRIBUTES.FILE_ATTRIBUTE_REPARSE_POINT)) + // Skip symbolic links and junctions + continue; - if ((findData.dwFileAttributes & FileAttributes.Directory) != FileAttributes.Directory) - { - ComputeFileSize(itemPath); - } - else if (findData.cFileName != "." && findData.cFileName != "..") - { - queue.Enqueue(itemPath); + var itemPath = Path.Combine(directory, findData.cFileName.ToString()); + + // Skip current and parent directory entries + var fileName = findData.cFileName.ToString(); + if (fileName.Equals(".", StringComparison.OrdinalIgnoreCase) || + fileName.Equals("..", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + if (attributes.HasFlag(FILE_FLAGS_AND_ATTRIBUTES.FILE_ATTRIBUTE_DIRECTORY)) + { + queue.Enqueue(itemPath); + } + else + { + ComputeFileSize(itemPath); + } + + if (token.IsCancellationRequested) + break; } + while (PInvoke.FindNextFile(hFile, &findData)); - if (token.IsCancellationRequested) - break; + PInvoke.FindClose(hFile); } - while (FindNextFile(hFile, out findData)); } } } - }, token, TaskCreationOptions.LongRunning, TaskScheduler.Default)); + } } private long ComputeFileSize(string path) { if (_computedFiles.TryGetValue(path, out var size)) - { return size; - } - using var hFile = CreateFile( + using var hFile = PInvoke.CreateFile( path, - Kernel32.FileAccess.FILE_READ_ATTRIBUTES, - FileShare.Read, + (uint)FILE_ACCESS_RIGHTS.FILE_READ_ATTRIBUTES, + FILE_SHARE_MODE.FILE_SHARE_READ, null, - FileMode.Open, + FILE_CREATION_DISPOSITION.OPEN_EXISTING, 0, null); - if (!hFile.IsInvalid) + if (!hFile.IsInvalid && PInvoke.GetFileSizeEx(hFile, out size) && _computedFiles.TryAdd(path, size)) { - if (GetFileSizeEx(hFile, out size) && _computedFiles.TryAdd(path, size)) - { - Interlocked.Add(ref _size, size); - } + Interlocked.Add(ref _size, size); + ItemsCountChanged?.Invoke(ItemsCount); } return size; @@ -102,9 +131,7 @@ private long ComputeFileSize(string path) public void ForceComputeFileSize(string path) { if (!Win32Helper.HasFileAttribute(path, FileAttributes.Directory)) - { ComputeFileSize(path); - } } public bool TryGetComputedFileSize(string path, out long size) diff --git a/src/Files.App/Utils/Storage/Operations/FilesystemHelpers.cs b/src/Files.App/Utils/Storage/Operations/FilesystemHelpers.cs index fc60fa8c05d8..5ca29d830913 100644 --- a/src/Files.App/Utils/Storage/Operations/FilesystemHelpers.cs +++ b/src/Files.App/Utils/Storage/Operations/FilesystemHelpers.cs @@ -1,8 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.Core.Storage; -using Files.Core.Storage.Extensions; using Microsoft.Extensions.Logging; using Microsoft.Win32; using System.IO; @@ -18,8 +16,9 @@ namespace Files.App.Utils.Storage { - public sealed class FilesystemHelpers : IFilesystemHelpers + public sealed partial class FilesystemHelpers : IFilesystemHelpers { + private readonly IStorageTrashBinService StorageTrashBinService = Ioc.Default.GetRequiredService(); private readonly static StatusCenterViewModel _statusCenterViewModel = Ioc.Default.GetRequiredService(); private IShellPage associatedInstance; @@ -68,8 +67,8 @@ public FilesystemHelpers(IShellPage associatedInstance, CancellationToken cancel if (!IsValidForFilename(source.Name)) { await DialogDisplayHelper.ShowDialogAsync( - "ErrorDialogThisActionCannotBeDone".GetLocalizedResource(), - "ErrorDialogNameNotAllowed".GetLocalizedResource()); + Strings.ErrorDialogThisActionCannotBeDone.GetLocalizedResource(), + Strings.ErrorDialogNameNotAllowed.GetLocalizedResource()); return (ReturnResult.Failed, null); } @@ -90,8 +89,8 @@ public async Task DeleteItemsAsync(IEnumerable item.Path).Any(path => RecycleBinHelpers.IsPathUnderRecycleBin(path)); - var canBeSentToBin = !deleteFromRecycleBin && await RecycleBinHelpers.HasRecycleBin(source.FirstOrDefault()?.Path); + var deleteFromRecycleBin = source.Select(item => item.Path).Any(StorageTrashBinService.IsUnderTrashBin); + var canBeSentToBin = !deleteFromRecycleBin && await StorageTrashBinService.CanGoTrashBin(source.FirstOrDefault()?.Path); if (showDialog is DeleteConfirmationPolicies.Always || showDialog is DeleteConfirmationPolicies.PermanentOnly && @@ -102,9 +101,9 @@ showDialog is DeleteConfirmationPolicies.PermanentOnly && foreach (var src in source) { - if (RecycleBinHelpers.IsPathUnderRecycleBin(src.Path)) + if (StorageTrashBinService.IsUnderTrashBin(src.Path)) { - binItems ??= await RecycleBinHelpers.EnumerateRecycleBin(); + binItems ??= await StorageTrashBinService.GetAllRecycleBinFoldersAsync(); // Might still be null because we're deserializing the list from Json if (!binItems.IsEmpty()) @@ -163,7 +162,8 @@ showDialog is DeleteConfirmationPolicies.PermanentOnly && App.HistoryWrapper.AddHistory(history); // Execute removal tasks concurrently in background - _ = Task.WhenAll(source.Select(x => jumpListService.RemoveFolderAsync(x.Path))); + var sourcePaths = source.Select(x => x.Path); + _ = Task.WhenAll(sourcePaths.Select(jumpListService.RemoveFolderAsync)); var itemsCount = banner.TotalItemsCount; @@ -257,7 +257,7 @@ public async Task PerformOperationTypeAsync( if (isTargetExecutable || isTargetScriptFile) { var items = await GetDraggedStorageItems(packageView); - NavigationHelpers.OpenItemsWithExecutableAsync(associatedInstance, items, destination); + await NavigationHelpers.OpenItemsWithExecutableAsync(associatedInstance, items, destination); return ReturnResult.Success; } else @@ -303,7 +303,7 @@ public async Task CopyItemsAsync(IEnumerable var token = banner.CancellationToken; - var (collisions, cancelOperation, itemsResult) = await GetCollision(FilesystemOperationType.Copy, source, destination, showDialog); + var (collisions, cancelOperation, itemsResult) = await GetCollisions(FilesystemOperationType.Copy, source, destination, showDialog); if (cancelOperation) { @@ -319,14 +319,14 @@ public async Task CopyItemsAsync(IEnumerable if (registerHistory && history is not null && source.Any((item) => !string.IsNullOrWhiteSpace(item.Path))) { - foreach (var item in history.Source.Zip(history.Destination, (k, v) => new { Key = k, Value = v }).ToDictionary(k => k.Key, v => v.Value)) + foreach (var (histSrcItem, histDestItem) in history.Source.Zip(history.Destination)) { - foreach (var item2 in itemsResult) + foreach (var conflictItem in itemsResult) { - if (!string.IsNullOrEmpty(item2.CustomName) && item2.SourcePath == item.Key.Path && Path.GetFileName(item2.SourcePath) != item2.CustomName) + if (!string.IsNullOrEmpty(conflictItem.CustomName) && conflictItem.SourcePath == histSrcItem.Path && Path.GetFileName(conflictItem.SourcePath) != conflictItem.CustomName) { - var renameHistory = await filesystemOperations.RenameAsync(item.Value, item2.CustomName, NameCollisionOption.FailIfExists, banner.ProgressEventSource, token); - history.Destination[history.Source.IndexOf(item.Key)] = renameHistory.Destination[0]; + var renameHistory = await filesystemOperations.RenameAsync(histDestItem, conflictItem.CustomName, NameCollisionOption.FailIfExists, banner.ProgressEventSource, token); + history.Destination[history.Source.IndexOf(histSrcItem)] = renameHistory.Destination[0]; } } } @@ -363,9 +363,9 @@ public async Task CopyItemsFromClipboard(DataPackageView packageVi List? binItems = null; foreach (var item in source) { - if (RecycleBinHelpers.IsPathUnderRecycleBin(item.Path)) + if (StorageTrashBinService.IsUnderTrashBin(item.Path)) { - binItems ??= await RecycleBinHelpers.EnumerateRecycleBin(); + binItems ??= await StorageTrashBinService.GetAllRecycleBinFoldersAsync(); if (!binItems.IsEmpty()) // Might still be null because we're deserializing the list from Json { var matchingItem = binItems.FirstOrDefault(x => x.RecyclePath == item.Path); // Get original file name @@ -437,7 +437,7 @@ public async Task MoveItemsAsync(IEnumerable var token = banner.CancellationToken; - var (collisions, cancelOperation, itemsResult) = await GetCollision(FilesystemOperationType.Move, source, destination, showDialog); + var (collisions, cancelOperation, itemsResult) = await GetCollisions(FilesystemOperationType.Move, source, destination, showDialog); if (cancelOperation) { @@ -446,9 +446,6 @@ public async Task MoveItemsAsync(IEnumerable return ReturnResult.Cancelled; } - var sw = new Stopwatch(); - sw.Start(); - itemManipulationModel?.ClearSelection(); IStorageHistory history = await filesystemOperations.MoveItemsAsync((IList)source, (IList)destination, collisions, banner.ProgressEventSource, token); @@ -459,14 +456,14 @@ public async Task MoveItemsAsync(IEnumerable if (registerHistory && history is not null && source.Any((item) => !string.IsNullOrWhiteSpace(item.Path))) { - foreach (var item in history.Source.Zip(history.Destination, (k, v) => new { Key = k, Value = v }).ToDictionary(k => k.Key, v => v.Value)) + foreach (var (histSrcItem, histDestItem) in history.Source.Zip(history.Destination)) { - foreach (var item2 in itemsResult) + foreach (var conflictItem in itemsResult) { - if (!string.IsNullOrEmpty(item2.CustomName) && item2.SourcePath == item.Key.Path) + if (!string.IsNullOrEmpty(conflictItem.CustomName) && conflictItem.SourcePath == histSrcItem.Path) { - var renameHistory = await filesystemOperations.RenameAsync(item.Value, item2.CustomName, NameCollisionOption.FailIfExists, banner.ProgressEventSource, token); - history.Destination[history.Source.IndexOf(item.Key)] = renameHistory.Destination[0]; + var renameHistory = await filesystemOperations.RenameAsync(histDestItem, conflictItem.CustomName, NameCollisionOption.FailIfExists, banner.ProgressEventSource, token); + history.Destination[history.Source.IndexOf(histSrcItem)] = renameHistory.Destination[0]; } } } @@ -475,14 +472,13 @@ public async Task MoveItemsAsync(IEnumerable } // Execute removal tasks concurrently in background - _ = Task.WhenAll(source.Select(x => jumpListService.RemoveFolderAsync(x.Path))); + var sourcePaths = source.Select(x => x.Path); + _ = Task.WhenAll(sourcePaths.Select(jumpListService.RemoveFolderAsync)); var itemsCount = banner.TotalItemsCount; _statusCenterViewModel.RemoveItem(banner); - sw.Stop(); - StatusCenterHelper.AddCard_Move( token.IsCancellationRequested ? ReturnResult.Cancelled : returnStatus, source, @@ -511,9 +507,9 @@ public async Task MoveItemsFromClipboard(DataPackageView packageVi List? binItems = null; foreach (var item in source) { - if (RecycleBinHelpers.IsPathUnderRecycleBin(item.Path)) + if (StorageTrashBinService.IsUnderTrashBin(item.Path)) { - binItems ??= await RecycleBinHelpers.EnumerateRecycleBin(); + binItems ??= await StorageTrashBinService.GetAllRecycleBinFoldersAsync(); if (!binItems.IsEmpty()) // Might still be null because we're deserializing the list from Json { var matchingItem = binItems.FirstOrDefault(x => x.RecyclePath == item.Path); // Get original file name @@ -543,8 +539,8 @@ public async Task RenameAsync(IStorageItemWithPath source, string if (!IsValidForFilename(newName)) { await DialogDisplayHelper.ShowDialogAsync( - "ErrorDialogThisActionCannotBeDone".GetLocalizedResource(), - "ErrorDialogNameNotAllowed".GetLocalizedResource()); + Strings.ErrorDialogThisActionCannotBeDone.GetLocalizedResource(), + Strings.ErrorDialogNameNotAllowed.GetLocalizedResource()); return ReturnResult.Failed; } @@ -565,7 +561,7 @@ await DialogDisplayHelper.ShowDialogAsync( UserSettingsService.FoldersSettingsService.ShowFileExtensionWarning ) { - var yesSelected = await DialogDisplayHelper.ShowDialogAsync("Rename".GetLocalizedResource(), "RenameFileDialog/Text".GetLocalizedResource(), "Yes".GetLocalizedResource(), "No".GetLocalizedResource()); + var yesSelected = await DialogDisplayHelper.ShowDialogAsync(Strings.Rename.GetLocalizedResource(), Strings.RenameFileDialog_Text.GetLocalizedResource(), Strings.Yes.GetLocalizedResource(), Strings.No.GetLocalizedResource()); if (yesSelected) { history = await filesystemOperations.RenameAsync(source, newName, collision, progress, cancellationToken); @@ -636,7 +632,7 @@ public async Task RecycleItemsFromClipboard(DataPackageView packag var source = await GetDraggedStorageItems(packageView); ReturnResult returnStatus = ReturnResult.InProgress; - source = source.Where(x => !RecycleBinHelpers.IsPathUnderRecycleBin(x.Path)); // Can't recycle items already in recyclebin + source = source.Where(x => !StorageTrashBinService.IsUnderTrashBin(x.Path)); // Can't recycle items already in recyclebin returnStatus = await DeleteItemsAsync(source, showDialog, false, registerHistory); return returnStatus; @@ -644,42 +640,50 @@ public async Task RecycleItemsFromClipboard(DataPackageView packag public static bool IsValidForFilename(string name) => !string.IsNullOrWhiteSpace(name) && !ContainsRestrictedCharacters(name) && !ContainsRestrictedFileName(name); - private static async Task<(List collisions, bool cancelOperation, IEnumerable)> GetCollision(FilesystemOperationType operationType, IEnumerable source, IEnumerable destination, bool forceDialog) + private static async Task<(List collisions, bool cancelOperation, IEnumerable)> GetCollisions(FilesystemOperationType operationType, IEnumerable source, IEnumerable destination, bool forceDialog) { - var incomingItems = new List(); + var nonConflictingItems = new List(); var conflictingItems = new List(); var collisions = new Dictionary(); - foreach (var item in source.Zip(destination, (src, dest, index) => new { src, dest, index })) + foreach (var (src, dest) in source.Zip(destination)) { - var itemPathOrName = string.IsNullOrEmpty(item.src.Path) ? item.src.Item.Name : item.src.Path; - incomingItems.Add(new FileSystemDialogConflictItemViewModel() { ConflictResolveOption = FileNameConflictResolveOptionType.None, SourcePath = itemPathOrName, DestinationPath = item.dest, DestinationDisplayName = Path.GetFileName(item.dest) }); - var path = incomingItems.ElementAt(item.index).SourcePath; - if (path is not null && collisions.ContainsKey(path)) + string itemPathOrName = string.IsNullOrEmpty(src.Path) ? src.Item.Name : src.Path; + var incomingItem = new FileSystemDialogConflictItemViewModel + { + ConflictResolveOption = FileNameConflictResolveOptionType.None, + SourcePath = itemPathOrName, + DestinationPath = dest, + DestinationDisplayName = Path.GetFileName(dest) + }; + + if (!collisions.TryAdd(itemPathOrName, FileNameConflictResolveOptionType.GenerateNewName)) { // Something strange happened, log - App.Logger.LogWarning($"Duplicate key when resolving conflicts: {incomingItems.ElementAt(item.index).SourcePath}, {item.src.Name}\n" + + App.Logger.LogWarning($"Duplicate key when resolving conflicts: {itemPathOrName}, {src.Name}\n" + $"Source: {string.Join(", ", source.Select(x => string.IsNullOrEmpty(x.Path) ? x.Item.Name : x.Path))}"); } - collisions.AddIfNotPresent(incomingItems.ElementAt(item.index).SourcePath, FileNameConflictResolveOptionType.GenerateNewName); // Assume GenerateNewName when source and destination are the same - if (string.IsNullOrEmpty(item.src.Path) || item.src.Path != item.dest) + if (string.IsNullOrEmpty(src.Path) || src.Path != dest) { // Same item names in both directories - if (StorageHelpers.Exists(item.dest) || - (FtpHelpers.IsFtpPath(item.dest) && - await Ioc.Default.GetRequiredService().TryGetFileAsync(item.dest) is not null)) + if (StorageHelpers.Exists(dest) || + (FtpHelpers.IsFtpPath(dest) && + await Ioc.Default.GetRequiredService().TryGetFileAsync(dest) is not null)) { - (incomingItems[item.index] as FileSystemDialogConflictItemViewModel)!.ConflictResolveOption = FileNameConflictResolveOptionType.GenerateNewName; - conflictingItems.Add(incomingItems.ElementAt(item.index)); + incomingItem.ConflictResolveOption = FileNameConflictResolveOptionType.GenerateNewName; + conflictingItems.Add(incomingItem); + continue; } } + + nonConflictingItems.Add(incomingItem); } IEnumerable? itemsResult = null; - var mustResolveConflicts = !conflictingItems.IsEmpty(); + bool mustResolveConflicts = conflictingItems.Count > 0; if (mustResolveConflicts || forceDialog) { var dialogService = Ioc.Default.GetRequiredService(); @@ -688,7 +692,7 @@ await Ioc.Default.GetRequiredService().TryGetFileAsync(item. new() { ConflictsExist = mustResolveConflicts }, (false, false), operationType, - incomingItems.Except(conflictingItems).ToList(), // TODO: Could be optimized + nonConflictingItems, conflictingItems); var result = await dialogService.ShowDialogAsync(dialogViewModel); @@ -704,7 +708,7 @@ await Ioc.Default.GetRequiredService().TryGetFileAsync(item. collisions.Clear(); foreach (var item in itemsResult) { - collisions.AddIfNotPresent(item.SourcePath, item.ConflictResolveOption); + collisions.TryAdd(item.SourcePath!, item.ConflictResolveOption); } } @@ -714,8 +718,8 @@ await Ioc.Default.GetRequiredService().TryGetFileAsync(item. foreach (var src in source) { var itemPathOrName = string.IsNullOrEmpty(src.Path) ? src.Item.Name : src.Path; - var match = collisions.SingleOrDefault(x => x.Key == itemPathOrName); - var fileNameConflictResolveOptionType = (match.Key is not null) ? match.Value : FileNameConflictResolveOptionType.Skip; + bool found = collisions.TryGetValue(itemPathOrName, out var match); + var fileNameConflictResolveOptionType = found ? match : FileNameConflictResolveOptionType.Skip; newCollisions.Add(fileNameConflictResolveOptionType); } @@ -873,7 +877,7 @@ public static string GetShortcutNamingPreference(string itemName) var value = Registry.GetValue(keyName, "ShortcutNameTemplate", null); if (value is null) - return string.Format("ShortcutCreateNewSuffix".GetLocalizedResource(), itemName) + ".lnk"; + return string.Format(Strings.ShortcutCreateNewSuffix.GetLocalizedResource(), itemName) + ".lnk"; else { // Trim the quotes and the "%s" from the string diff --git a/src/Files.App/Utils/Storage/Operations/FilesystemOperations.cs b/src/Files.App/Utils/Storage/Operations/FilesystemOperations.cs index 1d5f420e8372..72e49351393d 100644 --- a/src/Files.App/Utils/Storage/Operations/FilesystemOperations.cs +++ b/src/Files.App/Utils/Storage/Operations/FilesystemOperations.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.Shared.Helpers; using Microsoft.UI.Xaml.Controls; @@ -14,8 +14,10 @@ namespace Files.App.Utils.Storage /// /// Provides group of file system operation for given page instance. /// - public sealed class FilesystemOperations : IFilesystemOperations + public sealed partial class FilesystemOperations : IFilesystemOperations { + private readonly IStorageTrashBinService StorageTrashBinService = Ioc.Default.GetRequiredService(); + private IShellPage _associatedInstance; public FilesystemOperations(IShellPage associatedInstance) @@ -124,8 +126,8 @@ public async Task CopyAsync(IStorageItemWithPath source, string // Do not paste files and folders inside the recycle bin await DialogDisplayHelper.ShowDialogAsync( - "ErrorDialogThisActionCannotBeDone".GetLocalizedResource(), - "ErrorDialogUnsupportedOperation".GetLocalizedResource()); + Strings.ErrorDialogThisActionCannotBeDone.GetLocalizedResource(), + Strings.ErrorDialogUnsupportedOperation.GetLocalizedResource()); return null; } @@ -142,10 +144,10 @@ await DialogDisplayHelper.ShowDialogAsync( ContentDialog dialog = new() { - Title = "ErrorDialogThisActionCannotBeDone".GetLocalizedResource(), - Content = $"{"ErrorDialogTheDestinationFolder".GetLocalizedResource()} ({destinationName}) {"ErrorDialogIsASubfolder".GetLocalizedResource()} ({sourceName})", + Title = Strings.ErrorDialogThisActionCannotBeDone.GetLocalizedResource(), + Content = $"{Strings.ErrorDialogTheDestinationFolder.GetLocalizedResource()} ({destinationName}) {Strings.ErrorDialogIsASubfolder.GetLocalizedResource()} ({sourceName})", //PrimaryButtonText = "Skip".GetLocalizedResource(), - CloseButtonText = "Cancel".GetLocalizedResource() + CloseButtonText = Strings.Cancel.GetLocalizedResource() }; if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) @@ -164,7 +166,7 @@ await DialogDisplayHelper.ShowDialogAsync( { // CopyFileFromApp only works on file not directories var fsSourceFolder = await source.ToStorageItemResult(); - var fsDestinationFolder = await _associatedInstance.ShellViewModel.GetFolderFromPathAsync(PathNormalization.GetParentDir(destination)); + var fsDestinationFolder = await _associatedInstance.ShellViewModel.GetFolderFromPathAsync(PathNormalization.GetParentDir(destination), cancellationToken); var fsResult = (FilesystemResult)(fsSourceFolder.ErrorCode | fsDestinationFolder.ErrorCode); if (fsResult) @@ -217,7 +219,7 @@ await DialogDisplayHelper.ShowDialogAsync( { Debug.WriteLine(System.Runtime.InteropServices.Marshal.GetLastWin32Error()); - FilesystemResult destinationResult = await _associatedInstance.ShellViewModel.GetFolderFromPathAsync(PathNormalization.GetParentDir(destination)); + FilesystemResult destinationResult = await _associatedInstance.ShellViewModel.GetFolderFromPathAsync(PathNormalization.GetParentDir(destination), cancellationToken); var sourceResult = await source.ToStorageItemResult(); fsResult = sourceResult.ErrorCode | destinationResult.ErrorCode; @@ -325,8 +327,8 @@ public async Task MoveAsync(IStorageItemWithPath source, string // Do not paste files and folders inside the recycle bin await DialogDisplayHelper.ShowDialogAsync( - "ErrorDialogThisActionCannotBeDone".GetLocalizedResource(), - "ErrorDialogUnsupportedOperation".GetLocalizedResource()); + Strings.ErrorDialogThisActionCannotBeDone.GetLocalizedResource(), + Strings.ErrorDialogUnsupportedOperation.GetLocalizedResource()); return null; } @@ -344,10 +346,10 @@ await DialogDisplayHelper.ShowDialogAsync( ContentDialog dialog = new() { - Title = "ErrorDialogThisActionCannotBeDone".GetLocalizedResource(), - Content = $"{"ErrorDialogTheDestinationFolder".GetLocalizedResource()} ({destinationName}) {"ErrorDialogIsASubfolder".GetLocalizedResource()} ({sourceName})", + Title = Strings.ErrorDialogThisActionCannotBeDone.GetLocalizedResource(), + Content = $"{Strings.ErrorDialogTheDestinationFolder.GetLocalizedResource()} ({destinationName}) {Strings.ErrorDialogIsASubfolder.GetLocalizedResource()} ({sourceName})", //PrimaryButtonText = "Skip".GetLocalizedResource(), - CloseButtonText = "Cancel".GetLocalizedResource() + CloseButtonText = Strings.Cancel.GetLocalizedResource() }; if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) @@ -371,7 +373,7 @@ await DialogDisplayHelper.ShowDialogAsync( Debug.WriteLine(System.Runtime.InteropServices.Marshal.GetLastWin32Error()); var fsSourceFolder = await source.ToStorageItemResult(); - var fsDestinationFolder = await _associatedInstance.ShellViewModel.GetFolderFromPathAsync(PathNormalization.GetParentDir(destination)); + var fsDestinationFolder = await _associatedInstance.ShellViewModel.GetFolderFromPathAsync(PathNormalization.GetParentDir(destination), cancellationToken); fsResult = fsSourceFolder.ErrorCode | fsDestinationFolder.ErrorCode; if (fsResult) @@ -387,7 +389,7 @@ await DialogDisplayHelper.ShowDialogAsync( // Moving folders using Storage API can result in data loss, copy instead //var fsResultMove = await FilesystemTasks.Wrap(() => MoveDirectoryAsync((BaseStorageFolder)fsSourceFolder, (BaseStorageFolder)fsDestinationFolder, fsSourceFolder.Result.Name, collision.Convert(), true)); - if (await DialogDisplayHelper.ShowDialogAsync("ErrorDialogThisActionCannotBeDone".GetLocalizedResource(), "ErrorDialogUnsupportedMoveOperation".GetLocalizedResource(), "OK", "Cancel".GetLocalizedResource())) + if (await DialogDisplayHelper.ShowDialogAsync(Strings.ErrorDialogThisActionCannotBeDone.GetLocalizedResource(), Strings.ErrorDialogUnsupportedMoveOperation.GetLocalizedResource(), "OK", Strings.Cancel.GetLocalizedResource())) fsResultMove = await FilesystemTasks.Wrap(() => CloneDirectoryAsync((BaseStorageFolder)fsSourceFolder, (BaseStorageFolder)fsDestinationFolder, fsSourceFolder.Result.Name, collision.Convert())); } @@ -430,7 +432,7 @@ await DialogDisplayHelper.ShowDialogAsync( { Debug.WriteLine(System.Runtime.InteropServices.Marshal.GetLastWin32Error()); - FilesystemResult destinationResult = await _associatedInstance.ShellViewModel.GetFolderFromPathAsync(PathNormalization.GetParentDir(destination)); + FilesystemResult destinationResult = await _associatedInstance.ShellViewModel.GetFolderFromPathAsync(PathNormalization.GetParentDir(destination), cancellationToken); var sourceResult = await source.ToStorageItemResult(); fsResult = sourceResult.ErrorCode | destinationResult.ErrorCode; @@ -498,7 +500,7 @@ public async Task DeleteAsync(IStorageItemWithPath source, IPro fsProgress.Report(); - bool deleteFromRecycleBin = RecycleBinHelpers.IsPathUnderRecycleBin(source.Path); + bool deleteFromRecycleBin = StorageTrashBinService.IsUnderTrashBin(source.Path); FilesystemResult fsResult = FileSystemStatusCode.InProgress; @@ -510,12 +512,12 @@ public async Task DeleteAsync(IStorageItemWithPath source, IPro { if (source.ItemType == FilesystemItemType.File) { - fsResult = await _associatedInstance.ShellViewModel.GetFileFromPathAsync(source.Path) + fsResult = await _associatedInstance.ShellViewModel.GetFileFromPathAsync(source.Path, cancellationToken) .OnSuccess((t) => t.DeleteAsync(permanently ? StorageDeleteOption.PermanentDelete : StorageDeleteOption.Default).AsTask()); } else if (source.ItemType == FilesystemItemType.Directory) { - fsResult = await _associatedInstance.ShellViewModel.GetFolderFromPathAsync(source.Path) + fsResult = await _associatedInstance.ShellViewModel.GetFolderFromPathAsync(source.Path, cancellationToken) .OnSuccess((t) => t.DeleteAsync(permanently ? StorageDeleteOption.PermanentDelete : StorageDeleteOption.Default).AsTask()); } } @@ -537,7 +539,7 @@ public async Task DeleteAsync(IStorageItemWithPath source, IPro // Recycle bin also stores a file starting with $I for each item string iFilePath = Path.Combine(Path.GetDirectoryName(source.Path), Path.GetFileName(source.Path).Replace("$R", "$I", StringComparison.Ordinal)); - await _associatedInstance.ShellViewModel.GetFileFromPathAsync(iFilePath) + await _associatedInstance.ShellViewModel.GetFileFromPathAsync(iFilePath, cancellationToken) .OnSuccess(iFile => iFile.DeleteAsync(StorageDeleteOption.PermanentDelete).AsTask()); } fsProgress.ReportStatus(fsResult); @@ -549,7 +551,7 @@ await _associatedInstance.ShellViewModel.GetFileFromPathAsync(iFilePath) if (!permanently) { // Enumerate Recycle Bin - IEnumerable nameMatchItems, items = await RecycleBinHelpers.EnumerateRecycleBin(); + IEnumerable nameMatchItems, items = await StorageTrashBinService.GetAllRecycleBinFoldersAsync(); // Get name matching files if (FileExtensionHelpers.IsShortcutOrUrlFile(source.Path)) // We need to check if it is a shortcut file @@ -634,11 +636,11 @@ public async Task RenameAsync( } else if (renamed == FileSystemStatusCode.NotAFile || renamed == FileSystemStatusCode.NotAFolder) { - await DialogDisplayHelper.ShowDialogAsync("RenameError/NameInvalid/Title".GetLocalizedResource(), "RenameError/NameInvalid/Text".GetLocalizedResource()); + await DialogDisplayHelper.ShowDialogAsync(Strings.RenameError_NameInvalid_Title.GetLocalizedResource(), Strings.RenameError_NameInvalid_Text.GetLocalizedResource()); } else if (renamed == FileSystemStatusCode.NameTooLong) { - await DialogDisplayHelper.ShowDialogAsync("RenameError/TooLong/Title".GetLocalizedResource(), "RenameError/TooLong/Text".GetLocalizedResource()); + await DialogDisplayHelper.ShowDialogAsync(Strings.RenameError_TooLong_Title.GetLocalizedResource(), Strings.RenameError_TooLong_Text.GetLocalizedResource()); } else if (renamed == FileSystemStatusCode.InUse) { @@ -647,17 +649,17 @@ public async Task RenameAsync( } else if (renamed == FileSystemStatusCode.NotFound) { - await DialogDisplayHelper.ShowDialogAsync("RenameError/ItemDeleted/Title".GetLocalizedResource(), "RenameError/ItemDeleted/Text".GetLocalizedResource()); + await DialogDisplayHelper.ShowDialogAsync(Strings.RenameError_ItemDeleted_Title.GetLocalizedResource(), Strings.RenameError_ItemDeleted_Text.GetLocalizedResource()); } else if (renamed == FileSystemStatusCode.AlreadyExists) { var ItemAlreadyExistsDialog = new ContentDialog() { - Title = "ItemAlreadyExistsDialogTitle".GetLocalizedResource(), - Content = "ItemAlreadyExistsDialogContent".GetLocalizedResource(), - PrimaryButtonText = "GenerateNewName".GetLocalizedResource(), - SecondaryButtonText = "ItemAlreadyExistsDialogSecondaryButtonText".GetLocalizedResource(), - CloseButtonText = "Cancel".GetLocalizedResource() + Title = Strings.ItemAlreadyExistsDialogTitle.GetLocalizedResource(), + Content = Strings.ItemAlreadyExistsDialogContent.GetLocalizedResource(), + PrimaryButtonText = Strings.GenerateNewName.GetLocalizedResource(), + SecondaryButtonText = Strings.ItemAlreadyExistsDialogSecondaryButtonText.GetLocalizedResource(), + CloseButtonText = Strings.Cancel.GetLocalizedResource() }; ContentDialogResult result = await ItemAlreadyExistsDialog.TryShowAsync(); @@ -736,8 +738,8 @@ public async Task RestoreFromTrashAsync(IStorageItemWithPath so { if (source.ItemType == FilesystemItemType.Directory) { - FilesystemResult sourceFolder = await _associatedInstance.ShellViewModel.GetFolderFromPathAsync(source.Path); - FilesystemResult destinationFolder = await _associatedInstance.ShellViewModel.GetFolderFromPathAsync(PathNormalization.GetParentDir(destination)); + FilesystemResult sourceFolder = await _associatedInstance.ShellViewModel.GetFolderFromPathAsync(source.Path, cancellationToken); + FilesystemResult destinationFolder = await _associatedInstance.ShellViewModel.GetFolderFromPathAsync(PathNormalization.GetParentDir(destination), cancellationToken); fsResult = sourceFolder.ErrorCode | destinationFolder.ErrorCode; fsProgress.ReportStatus(fsResult); @@ -747,7 +749,7 @@ public async Task RestoreFromTrashAsync(IStorageItemWithPath so // Moving folders using Storage API can result in data loss, copy instead //fsResult = await FilesystemTasks.Wrap(() => MoveDirectoryAsync(sourceFolder.Result, destinationFolder.Result, Path.GetFileName(destination), CreationCollisionOption.FailIfExists, true)); - if (await DialogDisplayHelper.ShowDialogAsync("ErrorDialogThisActionCannotBeDone".GetLocalizedResource(), "ErrorDialogUnsupportedMoveOperation".GetLocalizedResource(), "OK", "Cancel".GetLocalizedResource())) + if (await DialogDisplayHelper.ShowDialogAsync(Strings.ErrorDialogThisActionCannotBeDone.GetLocalizedResource(), Strings.ErrorDialogUnsupportedMoveOperation.GetLocalizedResource(), "OK", Strings.Cancel.GetLocalizedResource())) fsResult = await FilesystemTasks.Wrap(() => CloneDirectoryAsync(sourceFolder.Result, destinationFolder.Result, Path.GetFileName(destination), CreationCollisionOption.FailIfExists)); // TODO: we could use here FilesystemHelpers with registerHistory false? @@ -757,8 +759,8 @@ public async Task RestoreFromTrashAsync(IStorageItemWithPath so } else { - FilesystemResult sourceFile = await _associatedInstance.ShellViewModel.GetFileFromPathAsync(source.Path); - FilesystemResult destinationFolder = await _associatedInstance.ShellViewModel.GetFolderFromPathAsync(PathNormalization.GetParentDir(destination)); + FilesystemResult sourceFile = await _associatedInstance.ShellViewModel.GetFileFromPathAsync(source.Path, cancellationToken); + FilesystemResult destinationFolder = await _associatedInstance.ShellViewModel.GetFolderFromPathAsync(PathNormalization.GetParentDir(destination), cancellationToken); fsResult = sourceFile.ErrorCode | destinationFolder.ErrorCode; fsProgress.ReportStatus(fsResult); @@ -780,7 +782,7 @@ public async Task RestoreFromTrashAsync(IStorageItemWithPath so // Recycle bin also stores a file starting with $I for each item string iFilePath = Path.Combine(Path.GetDirectoryName(source.Path), Path.GetFileName(source.Path).Replace("$R", "$I", StringComparison.Ordinal)); - await _associatedInstance.ShellViewModel.GetFileFromPathAsync(iFilePath) + await _associatedInstance.ShellViewModel.GetFileFromPathAsync(iFilePath, cancellationToken) .OnSuccess(iFile => iFile.DeleteAsync(StorageDeleteOption.PermanentDelete).AsTask()); } @@ -790,15 +792,15 @@ await _associatedInstance.ShellViewModel.GetFileFromPathAsync(iFilePath) { if (((FileSystemStatusCode)fsResult).HasFlag(FileSystemStatusCode.Unauthorized)) { - await DialogDisplayHelper.ShowDialogAsync("AccessDenied".GetLocalizedResource(), "AccessDeniedDeleteDialog/Text".GetLocalizedResource()); + await DialogDisplayHelper.ShowDialogAsync(Strings.AccessDenied.GetLocalizedResource(), Strings.AccessDeniedDeleteDialog_Text.GetLocalizedResource()); } else if (((FileSystemStatusCode)fsResult).HasFlag(FileSystemStatusCode.NotFound)) { - await DialogDisplayHelper.ShowDialogAsync("FileNotFoundDialog/Title".GetLocalizedResource(), "FileNotFoundDialog/Text".GetLocalizedResource()); + await DialogDisplayHelper.ShowDialogAsync(Strings.FileNotFoundDialog_Title.GetLocalizedResource(), Strings.FileNotFoundDialog_Text.GetLocalizedResource()); } else if (((FileSystemStatusCode)fsResult).HasFlag(FileSystemStatusCode.AlreadyExists)) { - await DialogDisplayHelper.ShowDialogAsync("ItemAlreadyExistsDialogTitle".GetLocalizedResource(), "ItemAlreadyExistsDialogContent".GetLocalizedResource()); + await DialogDisplayHelper.ShowDialogAsync(Strings.ItemAlreadyExistsDialogTitle.GetLocalizedResource(), Strings.ItemAlreadyExistsDialogContent.GetLocalizedResource()); } } @@ -934,7 +936,7 @@ public async Task DeleteItemsAsync(IList if (token.IsCancellationRequested) break; - permanently = RecycleBinHelpers.IsPathUnderRecycleBin(source[i].Path) || originalPermanently; + permanently = StorageTrashBinService.IsUnderTrashBin(source[i].Path) || originalPermanently; rawStorageHistory.Add(await DeleteAsync(source[i], null, permanently, token)); fsProgress.AddProcessedItemsCount(1); diff --git a/src/Files.App/Utils/Storage/Operations/IFilesystemHelpers.cs b/src/Files.App/Utils/Storage/Operations/IFilesystemHelpers.cs index 98ab5f00d17f..c3ce45e24944 100644 --- a/src/Files.App/Utils/Storage/Operations/IFilesystemHelpers.cs +++ b/src/Files.App/Utils/Storage/Operations/IFilesystemHelpers.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.ApplicationModel.DataTransfer; using Windows.Storage; diff --git a/src/Files.App/Utils/Storage/Operations/IFilesystemOperations.cs b/src/Files.App/Utils/Storage/Operations/IFilesystemOperations.cs index 620d9cafee02..add069d2ab7d 100644 --- a/src/Files.App/Utils/Storage/Operations/IFilesystemOperations.cs +++ b/src/Files.App/Utils/Storage/Operations/IFilesystemOperations.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.Storage; diff --git a/src/Files.App/Utils/Storage/Operations/ShellFilesystemOperations.cs b/src/Files.App/Utils/Storage/Operations/ShellFilesystemOperations.cs index bcab988dc692..c89155f375e3 100644 --- a/src/Files.App/Utils/Storage/Operations/ShellFilesystemOperations.cs +++ b/src/Files.App/Utils/Storage/Operations/ShellFilesystemOperations.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.IO; using Windows.Storage; @@ -9,8 +9,10 @@ namespace Files.App.Utils.Storage /// /// Provides group of shell file system operation for given page instance. /// - public sealed class ShellFilesystemOperations : IFilesystemOperations + public sealed partial class ShellFilesystemOperations : IFilesystemOperations { + private readonly IStorageTrashBinService StorageTrashBinService = Ioc.Default.GetRequiredService(); + private IShellPage _associatedInstance; private FilesystemOperations _filesystemOperations; @@ -47,7 +49,7 @@ public async Task CopyItemsAsync(IList so StatusCenterItemProgressModel fsProgress = new( progress, - true, + false, FileSystemStatusCode.InProgress, source.Count); @@ -59,7 +61,7 @@ public async Task CopyItemsAsync(IList so var operationID = Guid.NewGuid().ToString(); - using var r = cancellationToken.Register(CancelOperation, operationID, false); + await using var r = cancellationToken.Register(CancelOperation, operationID, false); var sourceReplace = sourceNoSkip.Zip(collisionsNoSkip, (src, coll) => new { src, coll }).Where(item => item.coll == FileNameConflictResolveOptionType.ReplaceExisting).Select(item => item.src); var destinationReplace = destinationNoSkip.Zip(collisionsNoSkip, (src, coll) => new { src, coll }).Where(item => item.coll == FileNameConflictResolveOptionType.ReplaceExisting).Select(item => item.src); @@ -147,18 +149,18 @@ await sourceMatch.Select(x => x.dest).ToListAsync(), } else if (copyResult.Items.Any(x => CopyEngineResult.Convert(x.HResult) == FileSystemStatusCode.NotFound)) { - await DialogDisplayHelper.ShowDialogAsync("FileNotFoundDialog/Title".GetLocalizedResource(), "FileNotFoundDialog/Text".GetLocalizedResource()); + await DialogDisplayHelper.ShowDialogAsync(Strings.FileNotFoundDialog_Title.GetLocalizedResource(), Strings.FileNotFoundDialog_Text.GetLocalizedResource()); } else if (copyResult.Items.Any(x => CopyEngineResult.Convert(x.HResult) == FileSystemStatusCode.AlreadyExists)) { - await DialogDisplayHelper.ShowDialogAsync("ItemAlreadyExistsDialogTitle".GetLocalizedResource(), "ItemAlreadyExistsDialogContent".GetLocalizedResource()); + await DialogDisplayHelper.ShowDialogAsync(Strings.ItemAlreadyExistsDialogTitle.GetLocalizedResource(), Strings.ItemAlreadyExistsDialogContent.GetLocalizedResource()); } else if (copyResult.Items.Any(x => CopyEngineResult.Convert(x.HResult) == FileSystemStatusCode.PropertyLoss)) { var failedSources = copyResult.Items.Where(x => CopyEngineResult.Convert(x.HResult) == FileSystemStatusCode.PropertyLoss); var filePath = failedSources.Select(x => x.Source); - switch (await GetFileListDialog(filePath, "FilePropertiesCannotBeCopied".GetLocalizedResource(), "CopyFileWithoutProperties".GetLocalizedResource(), "OK".GetLocalizedResource(), "Cancel".GetLocalizedResource())) + switch (await GetFileListDialog(filePath, Strings.FilePropertiesCannotBeCopied.GetLocalizedResource(), Strings.CopyFileWithoutProperties.GetLocalizedResource(), Strings.OK.GetLocalizedResource(), Strings.Cancel.GetLocalizedResource())) { case DialogResult.Primary: var copyZip = sourceNoSkip.Zip(destinationNoSkip, (src, dest) => new { src, dest }).Zip(collisionsNoSkip, (z1, coll) => new { z1.src, z1.dest, coll }); @@ -282,11 +284,11 @@ await sourceMatch.Select(x => x.dest).ToListAsync(), } else if (createResult.Items.Any(x => CopyEngineResult.Convert(x.HResult) == FileSystemStatusCode.NotFound)) { - await DialogDisplayHelper.ShowDialogAsync("FileNotFoundDialog/Title".GetLocalizedResource(), "FileNotFoundDialog/Text".GetLocalizedResource()); + await DialogDisplayHelper.ShowDialogAsync(Strings.FileNotFoundDialog_Title.GetLocalizedResource(), Strings.FileNotFoundDialog_Text.GetLocalizedResource()); } else if (createResult.Items.Any(x => CopyEngineResult.Convert(x.HResult) == FileSystemStatusCode.AlreadyExists)) { - await DialogDisplayHelper.ShowDialogAsync("ItemAlreadyExistsDialogTitle".GetLocalizedResource(), "ItemAlreadyExistsDialogContent".GetLocalizedResource()); + await DialogDisplayHelper.ShowDialogAsync(Strings.ItemAlreadyExistsDialogTitle.GetLocalizedResource(), Strings.ItemAlreadyExistsDialogContent.GetLocalizedResource()); } fsProgress.ReportStatus(CopyEngineResult.Convert(createResult.Items.FirstOrDefault(x => !x.Succeeded)?.HResult)); @@ -358,7 +360,7 @@ public async Task DeleteItemsAsync(IList fsProgress.Report(); var deleteFilePaths = source.Select(s => s.Path).Distinct(); - var deleteFromRecycleBin = source.Any() && RecycleBinHelpers.IsPathUnderRecycleBin(source.ElementAt(0).Path); + var deleteFromRecycleBin = source.Any() && StorageTrashBinService.IsUnderTrashBin(source.ElementAt(0).Path); permanently |= deleteFromRecycleBin; @@ -369,7 +371,7 @@ public async Task DeleteItemsAsync(IList } var operationID = Guid.NewGuid().ToString(); - using var r = cancellationToken.Register(CancelOperation, operationID, false); + await using var r = cancellationToken.Register(CancelOperation, operationID, false); var (success, response) = await FileOperationsHelpers.DeleteItemAsync(deleteFilePaths.ToArray(), permanently, MainWindow.Instance.WindowHandle.ToInt64(), asAdmin, progress, operationID); @@ -413,7 +415,26 @@ public async Task DeleteItemsAsync(IList else if (deleteResult.Items.Any(x => CopyEngineResult.Convert(x.HResult) == FileSystemStatusCode.InUse)) { var failedSources = deleteResult.Items.Where(x => CopyEngineResult.Convert(x.HResult) == FileSystemStatusCode.InUse); - var filePath = failedSources.Select(x => x.Source); // When deleting only source can be in use but shell returns COPYENGINE_E_SHARING_VIOLATION_DEST for folders + + var filesToCheck = new List(); + foreach (var failedSource in failedSources) + { + if (Directory.Exists(failedSource.Source)) + { + try + { + var files = Directory.EnumerateFiles(failedSource.Source, "*", SearchOption.AllDirectories); + filesToCheck.AddRange(files); + } + catch { } + } + else if (File.Exists(failedSource.Source)) + { + filesToCheck.Add(failedSource.Source); + } + } + + var filePath = filesToCheck.Any() ? filesToCheck : failedSources.Select(x => x.Source); var lockingProcess = WhoIsLocking(filePath); switch (await GetFileInUseDialog(filePath, lockingProcess)) @@ -428,7 +449,7 @@ public async Task DeleteItemsAsync(IList } else if (deleteResult.Items.Any(x => CopyEngineResult.Convert(x.HResult) == FileSystemStatusCode.NotFound)) { - await DialogDisplayHelper.ShowDialogAsync("FileNotFoundDialog/Title".GetLocalizedResource(), "FileNotFoundDialog/Text".GetLocalizedResource()); + await DialogDisplayHelper.ShowDialogAsync(Strings.FileNotFoundDialog_Title.GetLocalizedResource(), Strings.FileNotFoundDialog_Text.GetLocalizedResource()); } else if (deleteResult.Items.All(x => x.HResult == -1) && permanently) // ADS { @@ -481,7 +502,7 @@ public async Task MoveItemsAsync(IList so var collisionsNoSkip = collisions.Where(c => c != FileNameConflictResolveOptionType.Skip); var operationID = Guid.NewGuid().ToString(); - using var r = cancellationToken.Register(CancelOperation, operationID, false); + await using var r = cancellationToken.Register(CancelOperation, operationID, false); var sourceReplace = sourceNoSkip.Zip(collisionsNoSkip, (src, coll) => new { src, coll }).Where(item => item.coll == FileNameConflictResolveOptionType.ReplaceExisting).Select(item => item.src); var destinationReplace = destinationNoSkip.Zip(collisionsNoSkip, (src, coll) => new { src, coll }).Where(item => item.coll == FileNameConflictResolveOptionType.ReplaceExisting).Select(item => item.src); @@ -540,7 +561,7 @@ public async Task MoveItemsAsync(IList so var destName = subtree.dest.Split(Path.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries).Last(); var srcName = subtree.src.Path.Split(Path.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries).Last(); - await DialogDisplayHelper.ShowDialogAsync("ErrorDialogThisActionCannotBeDone".GetLocalizedResource(), $"{"ErrorDialogTheDestinationFolder".GetLocalizedResource()} ({destName}) {"ErrorDialogIsASubfolder".GetLocalizedResource()} ({srcName})"); + await DialogDisplayHelper.ShowDialogAsync(Strings.ErrorDialogThisActionCannotBeDone.GetLocalizedResource(), $"{Strings.ErrorDialogTheDestinationFolder.GetLocalizedResource()} ({destName}) {Strings.ErrorDialogIsASubfolder.GetLocalizedResource()} ({srcName})"); } else if (moveResult.Items.Any(x => CopyEngineResult.Convert(x.HResult) == FileSystemStatusCode.InUse)) { @@ -574,17 +595,17 @@ await sourceMatch.Select(x => x.dest).ToListAsync(), } else if (moveResult.Items.Any(x => CopyEngineResult.Convert(x.HResult) == FileSystemStatusCode.NotFound)) { - await DialogDisplayHelper.ShowDialogAsync("FileNotFoundDialog/Title".GetLocalizedResource(), "FileNotFoundDialog/Text".GetLocalizedResource()); + await DialogDisplayHelper.ShowDialogAsync(Strings.FileNotFoundDialog_Title.GetLocalizedResource(), Strings.FileNotFoundDialog_Text.GetLocalizedResource()); } else if (moveResult.Items.Any(x => CopyEngineResult.Convert(x.HResult) == FileSystemStatusCode.AlreadyExists)) { - await DialogDisplayHelper.ShowDialogAsync("ItemAlreadyExistsDialogTitle".GetLocalizedResource(), "ItemAlreadyExistsDialogContent".GetLocalizedResource()); + await DialogDisplayHelper.ShowDialogAsync(Strings.ItemAlreadyExistsDialogTitle.GetLocalizedResource(), Strings.ItemAlreadyExistsDialogContent.GetLocalizedResource()); } else if (moveResult.Items.Any(x => CopyEngineResult.Convert(x.HResult) == FileSystemStatusCode.PropertyLoss)) { var failedSources = moveResult.Items.Where(x => CopyEngineResult.Convert(x.HResult) == FileSystemStatusCode.PropertyLoss); var filePath = failedSources.Select(x => x.Source); - switch (await GetFileListDialog(filePath, "FilePropertiesCannotBeMoved".GetLocalizedResource(), "MoveFileWithoutProperties".GetLocalizedResource(), "OK".GetLocalizedResource(), "Cancel".GetLocalizedResource())) + switch (await GetFileListDialog(filePath, Strings.FilePropertiesCannotBeMoved.GetLocalizedResource(), Strings.MoveFileWithoutProperties.GetLocalizedResource(), Strings.OK.GetLocalizedResource(), Strings.Cancel.GetLocalizedResource())) { case DialogResult.Primary: var copyZip = sourceNoSkip.Zip(destinationNoSkip, (src, dest) => new { src, dest }).Zip(collisionsNoSkip, (z1, coll) => new { z1.src, z1.dest, coll }); @@ -689,11 +710,11 @@ await DynamicDialogFactory } else if (renameResult.Items.Any(x => CopyEngineResult.Convert(x.HResult) == FileSystemStatusCode.NotFound)) { - await DialogDisplayHelper.ShowDialogAsync("RenameError/ItemDeleted/Title".GetLocalizedResource(), "RenameError/ItemDeleted/Text".GetLocalizedResource()); + await DialogDisplayHelper.ShowDialogAsync(Strings.RenameError_ItemDeleted_Title.GetLocalizedResource(), Strings.RenameError_ItemDeleted_Text.GetLocalizedResource()); } else if (renameResult.Items.Any(x => CopyEngineResult.Convert(x.HResult) == FileSystemStatusCode.AlreadyExists)) { - await DialogDisplayHelper.ShowDialogAsync("ItemAlreadyExistsDialogTitle".GetLocalizedResource(), "ItemAlreadyExistsDialogContent".GetLocalizedResource()); + await DialogDisplayHelper.ShowDialogAsync(Strings.ItemAlreadyExistsDialogTitle.GetLocalizedResource(), Strings.ItemAlreadyExistsDialogContent.GetLocalizedResource()); } // ADS else if (renameResult.Items.All(x => x.HResult == -1)) @@ -734,7 +755,7 @@ public async Task RestoreItemsFromTrashAsync(IList s.Path).ToArray(), [.. destination], false, MainWindow.Instance.WindowHandle.ToInt64(), asAdmin, progress, operationID); @@ -805,11 +826,11 @@ await sourceMatch.Select(x => x.src).ToListAsync(), } else if (moveResult.Items.Any(x => CopyEngineResult.Convert(x.HResult) == FileSystemStatusCode.NotFound)) { - await DialogDisplayHelper.ShowDialogAsync("FileNotFoundDialog/Title".GetLocalizedResource(), "FileNotFoundDialog/Text".GetLocalizedResource()); + await DialogDisplayHelper.ShowDialogAsync(Strings.FileNotFoundDialog_Title.GetLocalizedResource(), Strings.FileNotFoundDialog_Text.GetLocalizedResource()); } else if (moveResult.Items.Any(x => CopyEngineResult.Convert(x.HResult) == FileSystemStatusCode.AlreadyExists)) { - await DialogDisplayHelper.ShowDialogAsync("ItemAlreadyExistsDialogTitle".GetLocalizedResource(), "ItemAlreadyExistsDialogContent".GetLocalizedResource()); + await DialogDisplayHelper.ShowDialogAsync(Strings.ItemAlreadyExistsDialogTitle.GetLocalizedResource(), Strings.ItemAlreadyExistsDialogContent.GetLocalizedResource()); } fsProgress.ReportStatus(CopyEngineResult.Convert(moveResult.Items.FirstOrDefault(x => !x.Succeeded)?.HResult)); @@ -831,12 +852,12 @@ private async Task RequestAdminOperation() private Task GetFileInUseDialog(IEnumerable source, IEnumerable lockingProcess = null) { - var titleText = "FileInUseDialog/Title".GetLocalizedResource(); + var titleText = Strings.FileInUseDialog_Title.GetLocalizedResource(); var subtitleText = lockingProcess.IsEmpty() - ? "FileInUseDialog/Text".GetLocalizedResource() - : string.Format("FileInUseByDialog/Text".GetLocalizedResource(), string.Join(", ", lockingProcess.Select(x => $"{x.AppName ?? x.Name} (PID: {x.Pid})"))); + ? Strings.FileInUseDialog_Text.GetLocalizedResource() + : string.Format(Strings.FileInUseByDialog_Text.GetLocalizedResource(), string.Join(", ", lockingProcess.Select(x => $"{x.AppName ?? x.Name} (PID: {x.Pid})"))); - return GetFileListDialog(source, titleText, subtitleText, "Retry".GetLocalizedResource(), "Cancel".GetLocalizedResource()); + return GetFileListDialog(source, titleText, subtitleText, Strings.Retry.GetLocalizedResource(), Strings.Cancel.GetLocalizedResource()); } private async Task GetFileListDialog(IEnumerable source, string titleText, string descriptionText = null, string primaryButtonText = null, string secondaryButtonText = null) @@ -845,9 +866,9 @@ private async Task GetFileListDialog(IEnumerable source, s List binItems = null; foreach (var src in source) { - if (RecycleBinHelpers.IsPathUnderRecycleBin(src)) + if (StorageTrashBinService.IsUnderTrashBin(src)) { - binItems ??= await RecycleBinHelpers.EnumerateRecycleBin(); + binItems ??= await StorageTrashBinService.GetAllRecycleBinFoldersAsync(); // Might still be null because we're deserializing the list from Json if (!binItems.IsEmpty()) diff --git a/src/Files.App/Utils/Storage/Search/FolderSearch.cs b/src/Files.App/Utils/Storage/Search/FolderSearch.cs index 23432318648a..f99346b4b2bc 100644 --- a/src/Files.App/Utils/Storage/Search/FolderSearch.cs +++ b/src/Files.App/Utils/Storage/Search/FolderSearch.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.Extensions.Logging; using System.IO; +using System.Text.RegularExpressions; using Windows.Storage; using Windows.Storage.FileProperties; using Windows.Storage.Search; -using static Files.App.Helpers.Win32Helper; using FileAttributes = System.IO.FileAttributes; using WIN32_FIND_DATA = Files.App.Helpers.Win32PInvoke.WIN32_FIND_DATA; @@ -16,16 +16,17 @@ public sealed class FolderSearch { private IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetRequiredService(); private DrivesViewModel drivesViewModel = Ioc.Default.GetRequiredService(); - + private readonly IStorageTrashBinService StorageTrashBinService = Ioc.Default.GetRequiredService(); private readonly IFileTagsSettingsService fileTagsSettingsService = Ioc.Default.GetRequiredService(); + private readonly ILogger logger = Ioc.Default.GetRequiredService>(); private const uint defaultStepSize = 500; public string? Query { get; set; } + public string? Folder { get; set; } public uint MaxItemCount { get; set; } = 0; // 0: no limit - public uint ThumbnailSize { get; set; } = 24; private uint UsedMaxItemCount => MaxItemCount > 0 ? MaxItemCount : uint.MaxValue; @@ -95,7 +96,7 @@ public Task SearchAsync(IList results, CancellationToken token) private async Task AddItemsForHomeAsync(IList results, CancellationToken token) { - if (AQSQuery.StartsWith("tag:", StringComparison.Ordinal)) + if (IsTagQuery(AQSQuery)) { await SearchTagsAsync("", results, token); // Search tags everywhere, not only local drives } @@ -184,21 +185,120 @@ private async Task AddItemsForLibraryAsync(LibraryLocationItem library, IList
  • (); + var andParts = Regex.Split(orPart, @"\s+AND\s+", RegexOptions.IgnoreCase); + + foreach (var andPart in andParts) + { + var matches = Regex.Matches(andPart.Trim(), @"(NOT\s+)?tag:(?:""([^""]+)""|([^\s""]+))", RegexOptions.IgnoreCase); + foreach (Match match in matches) + { + var isExclude = !string.IsNullOrEmpty(match.Groups[1].Value); + var tagValue = match.Groups[2].Value; + if (string.IsNullOrEmpty(tagValue)) + tagValue = match.Groups[3].Value; + + if (string.IsNullOrEmpty(tagValue)) + { + logger.LogWarning("Failed to parse tag query: {Query}", andPart); + continue; + } + + var tagValues = tagValue.Split(',', StringSplitOptions.RemoveEmptyEntries); + var tagUids = new HashSet(); + + foreach (var tagName in tagValues) + { + var uids = fileTagsSettingsService.GetTagsByName(tagName).Select(t => t.Uid); + foreach (var uid in uids) + { + tagUids.Add(uid); + } + } + + andGroup.Add(new TagTerm { TagUids = tagUids, IsExclude = isExclude }); + } + } + + if (andGroup.Count > 0) + { + expression.OrGroups.Add(andGroup); + } + } + + return expression; + } + + private bool MatchesTagExpression(IEnumerable fileTags, TagQueryExpression expression) + { + foreach (var orGroup in expression.OrGroups) + { + bool groupMatches = true; + foreach (var term in orGroup) + { + if (term.IsExclude) + { + if (term.TagUids.Count > 0 && term.TagUids.Any(fileTags.Contains)) + { + groupMatches = false; + break; + } + } + else + { + if (term.TagUids.Count == 0 || !term.TagUids.Any(fileTags.Contains)) + { + groupMatches = false; + break; + } + } + } + + if (groupMatches) + { + return true; + } + } + + return false; + } + private async Task SearchTagsAsync(string folder, IList results, CancellationToken token) { //var sampler = new IntervalSampler(500); - var tags = AQSQuery.Substring("tag:".Length)?.Split(',').Where(t => !string.IsNullOrWhiteSpace(t)) - .SelectMany(t => fileTagsSettingsService.GetTagsByName(t), (_, t) => t.Uid).ToHashSet(); - if (tags?.Any() != true) + var expression = ParseTagQuery(AQSQuery); + + if (expression.OrGroups.Count == 0) { return; } var dbInstance = FileTagsHelper.GetDbInstance(); var matches = dbInstance.GetAllUnderPath(folder) - .Where(x => tags.All(x.Tags.Contains)); + .Where(x => MatchesTagExpression(x.Tags, expression)); if (string.IsNullOrEmpty(folder)) - matches = matches.Where(x => !RecycleBinHelpers.IsPathUnderRecycleBin(x.FilePath)); + matches = matches.Where(x => !StorageTrashBinService.IsUnderTrashBin(x.FilePath)); foreach (var match in matches) { @@ -259,7 +359,7 @@ private async Task SearchTagsAsync(string folder, IList results, Can private async Task AddItemsAsync(string folder, IList results, CancellationToken token) { - if (AQSQuery.StartsWith("tag:", StringComparison.Ordinal)) + if (IsTagQuery(AQSQuery)) { await SearchTagsAsync(folder, results, token); } @@ -337,7 +437,7 @@ await Task.Run(() => } while (hasNextFile); Win32PInvoke.FindClose(hFile); - }); + }, token); } } @@ -346,10 +446,14 @@ private ListedItem GetListedItemAsync(string itemPath, WIN32_FIND_DATA findData) ListedItem listedItem = null; var isHidden = ((FileAttributes)findData.dwFileAttributes & FileAttributes.Hidden) == FileAttributes.Hidden; var isFolder = ((FileAttributes)findData.dwFileAttributes & FileAttributes.Directory) == FileAttributes.Directory; + Win32PInvoke.FileTimeToSystemTime(ref findData.ftLastWriteTime, out Win32PInvoke.SYSTEMTIME systemModifiedTimeOutput); + Win32PInvoke.FileTimeToSystemTime(ref findData.ftCreationTime, out Win32PInvoke.SYSTEMTIME systemCreatedTimeOutput); + if (!isFolder) { string itemFileExtension = null; string itemType = null; + long fileSize = Win32FindDataExtensions.GetSize(findData); if (findData.cFileName.Contains('.', StringComparison.Ordinal)) { itemFileExtension = Path.GetExtension(itemPath); @@ -361,11 +465,15 @@ private ListedItem GetListedItemAsync(string itemPath, WIN32_FIND_DATA findData) PrimaryItemAttribute = StorageItemTypes.File, ItemNameRaw = findData.cFileName, ItemPath = itemPath, + ItemDateModifiedReal = systemModifiedTimeOutput.ToDateTime(), + ItemDateCreatedReal = systemCreatedTimeOutput.ToDateTime(), IsHiddenItem = isHidden, LoadFileIcon = false, FileExtension = itemFileExtension, ItemType = itemType, - Opacity = isHidden ? Constants.UI.DimItemOpacity : 1 + Opacity = isHidden ? Constants.UI.DimItemOpacity : 1, + FileSize = fileSize.ToSizeString(), + FileSizeBytes = fileSize, }; } else @@ -377,26 +485,36 @@ private ListedItem GetListedItemAsync(string itemPath, WIN32_FIND_DATA findData) PrimaryItemAttribute = StorageItemTypes.Folder, ItemNameRaw = findData.cFileName, ItemPath = itemPath, + ItemDateModifiedReal = systemModifiedTimeOutput.ToDateTime(), + ItemDateCreatedReal = systemCreatedTimeOutput.ToDateTime(), IsHiddenItem = isHidden, LoadFileIcon = false, Opacity = isHidden ? Constants.UI.DimItemOpacity : 1 }; } } + if (listedItem is not null && MaxItemCount > 0) // Only load icon for searchbox suggestions { - _ = FileThumbnailHelper.LoadIconFromPathAsync(listedItem.ItemPath, ThumbnailSize, ThumbnailMode.ListView, ThumbnailOptions.ResizeThumbnail, isFolder) + _ = FileThumbnailHelper.GetIconAsync( + listedItem.ItemPath, + Constants.ShellIconSizes.Small, + isFolder, + IconOptions.ReturnIconOnly | IconOptions.UseCurrentScale) .ContinueWith((t) => { if (t.IsCompletedSuccessfully && t.Result is not null) { _ = FilesystemTasks.Wrap(() => MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(async () => { - listedItem.FileImage = await t.Result.ToBitmapAsync(); + var bitmapImage = await t.Result.ToBitmapAsync(); + if (bitmapImage is not null) + listedItem.FileImage = bitmapImage; }, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low)); } }); } + return listedItem; } diff --git a/src/Files.App/Utils/Storage/Search/TagQueryExpression.cs b/src/Files.App/Utils/Storage/Search/TagQueryExpression.cs new file mode 100644 index 000000000000..df34979d1743 --- /dev/null +++ b/src/Files.App/Utils/Storage/Search/TagQueryExpression.cs @@ -0,0 +1,10 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Utils.Storage +{ + public sealed class TagQueryExpression + { + public List> OrGroups { get; set; } = new(); + } +} diff --git a/src/Files.App/Utils/Storage/Search/TagTerm.cs b/src/Files.App/Utils/Storage/Search/TagTerm.cs new file mode 100644 index 000000000000..0614dfd35013 --- /dev/null +++ b/src/Files.App/Utils/Storage/Search/TagTerm.cs @@ -0,0 +1,12 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Utils.Storage +{ + public class TagTerm + { + public HashSet TagUids { get; set; } = new(); + + public bool IsExclude { get; set; } + } +} diff --git a/src/Files.App/Utils/Storage/StorageBaseItems/BaseBasicProperties.cs b/src/Files.App/Utils/Storage/StorageBaseItems/BaseBasicProperties.cs index 1db02a82b8cd..8efb09a5441b 100644 --- a/src/Files.App/Utils/Storage/StorageBaseItems/BaseBasicProperties.cs +++ b/src/Files.App/Utils/Storage/StorageBaseItems/BaseBasicProperties.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Utils.Storage { - public class BaseBasicProperties : BaseStorageItemExtraProperties + public partial class BaseBasicProperties : BaseStorageItemExtraProperties { public virtual ulong Size => 0; diff --git a/src/Files.App/Utils/Storage/StorageBaseItems/BaseBasicStorageItemExtraProperties.cs b/src/Files.App/Utils/Storage/StorageBaseItems/BaseBasicStorageItemExtraProperties.cs index 7c09eb0d8d75..03134b3f8125 100644 --- a/src/Files.App/Utils/Storage/StorageBaseItems/BaseBasicStorageItemExtraProperties.cs +++ b/src/Files.App/Utils/Storage/StorageBaseItems/BaseBasicStorageItemExtraProperties.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Runtime.InteropServices.WindowsRuntime; using Windows.Foundation; @@ -7,7 +7,7 @@ namespace Files.App.Utils.Storage { - public sealed class BaseBasicStorageItemExtraProperties : BaseStorageItemExtraProperties + public sealed partial class BaseBasicStorageItemExtraProperties : BaseStorageItemExtraProperties { private readonly IStorageItem _item; diff --git a/src/Files.App/Utils/Storage/StorageBaseItems/BaseStorageFile.cs b/src/Files.App/Utils/Storage/StorageBaseItems/BaseStorageFile.cs index 126d2439d54f..11f4eb53e8cd 100644 --- a/src/Files.App/Utils/Storage/StorageBaseItems/BaseStorageFile.cs +++ b/src/Files.App/Utils/Storage/StorageBaseItems/BaseStorageFile.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.IO; using System.Runtime.InteropServices.WindowsRuntime; @@ -158,7 +158,7 @@ public static IAsyncOperation GetFileFromPathAsync(string path) public async Task ReadTextAsync(int maxLength = -1) { using var inputStream = await OpenReadAsync(); - using var stream = inputStream.AsStreamForRead(); + await using var stream = inputStream.AsStreamForRead(); using var dataReader = new StreamReader(stream, true); StringBuilder builder = new(); int charsRead, charsToRead; diff --git a/src/Files.App/Utils/Storage/StorageBaseItems/BaseStorageFolder.cs b/src/Files.App/Utils/Storage/StorageBaseItems/BaseStorageFolder.cs index 79f59e0744f6..5517798b8727 100644 --- a/src/Files.App/Utils/Storage/StorageBaseItems/BaseStorageFolder.cs +++ b/src/Files.App/Utils/Storage/StorageBaseItems/BaseStorageFolder.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Runtime.InteropServices.WindowsRuntime; using Windows.Foundation; @@ -108,7 +108,7 @@ IAsyncOperation> IStorageFolderQueryOperations.GetFil public abstract IAsyncOperation GetFolderAsync(string name); IAsyncOperation IStorageFolder.GetFolderAsync(string name) - { + { return AsyncInfo.Run(async (cancellationToken) => await (await GetFolderAsync(name)).ToStorageFolderAsync()); diff --git a/src/Files.App/Utils/Storage/StorageBaseItems/BaseStorageItemExtraProperties.cs b/src/Files.App/Utils/Storage/StorageBaseItems/BaseStorageItemExtraProperties.cs index c79183d67e8b..1c2f532e3498 100644 --- a/src/Files.App/Utils/Storage/StorageBaseItems/BaseStorageItemExtraProperties.cs +++ b/src/Files.App/Utils/Storage/StorageBaseItems/BaseStorageItemExtraProperties.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Runtime.InteropServices.WindowsRuntime; using Windows.Foundation; @@ -8,7 +8,7 @@ namespace Files.App.Utils.Storage { - public class BaseStorageItemExtraProperties : IStorageItemExtraProperties + public partial class BaseStorageItemExtraProperties : IStorageItemExtraProperties { public virtual IAsyncOperation> RetrievePropertiesAsync(IEnumerable propertiesToRetrieve) { diff --git a/src/Files.App/Utils/Storage/StorageBaseItems/IBaseStorageFile.cs b/src/Files.App/Utils/Storage/StorageBaseItems/IBaseStorageFile.cs index 1531f11fa9de..c8e9d201cce1 100644 --- a/src/Files.App/Utils/Storage/StorageBaseItems/IBaseStorageFile.cs +++ b/src/Files.App/Utils/Storage/StorageBaseItems/IBaseStorageFile.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Windows.Foundation; using Windows.Storage; diff --git a/src/Files.App/Utils/Storage/StorageBaseItems/IBaseStorageFolder.cs b/src/Files.App/Utils/Storage/StorageBaseItems/IBaseStorageFolder.cs index 7e0db91cd0cc..44827c86732b 100644 --- a/src/Files.App/Utils/Storage/StorageBaseItems/IBaseStorageFolder.cs +++ b/src/Files.App/Utils/Storage/StorageBaseItems/IBaseStorageFolder.cs @@ -1,7 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using System.IO; using Windows.Foundation; using Windows.Storage; using Windows.Storage.FileProperties; diff --git a/src/Files.App/Utils/Storage/StorageBaseItems/ICreateFileWithStream.cs b/src/Files.App/Utils/Storage/StorageBaseItems/ICreateFileWithStream.cs index 73d3b8aa931f..0a0d13daa1a2 100644 --- a/src/Files.App/Utils/Storage/StorageBaseItems/ICreateFileWithStream.cs +++ b/src/Files.App/Utils/Storage/StorageBaseItems/ICreateFileWithStream.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.IO; using Windows.Foundation; diff --git a/src/Files.App/Utils/Storage/StorageBaseItems/IPasswordProtectedItem.cs b/src/Files.App/Utils/Storage/StorageBaseItems/IPasswordProtectedItem.cs index 08839f1e56de..7278d390cd27 100644 --- a/src/Files.App/Utils/Storage/StorageBaseItems/IPasswordProtectedItem.cs +++ b/src/Files.App/Utils/Storage/StorageBaseItems/IPasswordProtectedItem.cs @@ -1,10 +1,8 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using FluentFTP; +using FluentFTP.Exceptions; using SevenZip; -using System; -using Windows.Storage; namespace Files.App.Utils.Storage { diff --git a/src/Files.App/Utils/Storage/StorageBaseItems/StorageCredential.cs b/src/Files.App/Utils/Storage/StorageBaseItems/StorageCredential.cs index 85e550b06c16..8687d7fa078b 100644 --- a/src/Files.App/Utils/Storage/StorageBaseItems/StorageCredential.cs +++ b/src/Files.App/Utils/Storage/StorageBaseItems/StorageCredential.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Runtime.InteropServices; using System.Security; diff --git a/src/Files.App/Utils/Storage/StorageItems/BaseQueryResults.cs b/src/Files.App/Utils/Storage/StorageItems/BaseQueryResults.cs index 5629c265f58a..3a5a55e4460d 100644 --- a/src/Files.App/Utils/Storage/StorageItems/BaseQueryResults.cs +++ b/src/Files.App/Utils/Storage/StorageItems/BaseQueryResults.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Runtime.InteropServices.WindowsRuntime; using System.Text.RegularExpressions; @@ -45,12 +45,12 @@ public virtual IAsyncOperation> GetItemsAsync() { if (colonSplit[0] == "System.FileName" || colonSplit[0] == "fileName" || colonSplit[0] == "name") { - items = items.Where(x => Regex.IsMatch(x.Name, colonSplit[1].Replace("\"", "", StringComparison.Ordinal).Replace("*", "(.*?)", StringComparison.Ordinal), RegexOptions.IgnoreCase)).ToList(); + items = items.Where(x => Regex.IsMatch(x.Name, Regex.Escape(colonSplit[1].Replace("\"", "", StringComparison.Ordinal)).Replace("\\*", ".*").Replace("\\?", "."), RegexOptions.IgnoreCase)).ToList(); } } else { - items = items.Where(x => Regex.IsMatch(x.Name, split.Replace("\"", "", StringComparison.Ordinal).Replace("*", "(.*?)", StringComparison.Ordinal), RegexOptions.IgnoreCase)).ToList(); + items = items.Where(x => Regex.IsMatch(x.Name, Regex.Escape(split.Replace("\"", "", StringComparison.Ordinal)).Replace("\\*", ".*").Replace("\\?", "."), RegexOptions.IgnoreCase)).ToList(); } } } diff --git a/src/Files.App/Utils/Storage/StorageItems/FtpStorageFile.cs b/src/Files.App/Utils/Storage/StorageItems/FtpStorageFile.cs index 70216b8cf1e4..ea2b0dc0b711 100644 --- a/src/Files.App/Utils/Storage/StorageItems/FtpStorageFile.cs +++ b/src/Files.App/Utils/Storage/StorageItems/FtpStorageFile.cs @@ -1,7 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.App.Storage.Storables; using FluentFTP; using System.IO; using System.Net; @@ -14,7 +13,7 @@ namespace Files.App.Utils.Storage { - public sealed class FtpStorageFile : BaseStorageFile, IPasswordProtectedItem + public sealed partial class FtpStorageFile : BaseStorageFile, IPasswordProtectedItem { public override string Path { get; } public override string Name { get; } @@ -28,7 +27,7 @@ public override string DisplayType { get { - var itemType = "File".GetLocalizedResource(); + var itemType = Strings.File.GetLocalizedResource(); if (Name.Contains('.', StringComparison.Ordinal)) { itemType = IO.Path.GetExtension(Name).Trim('.') + " " + itemType; @@ -171,13 +170,13 @@ public override IAsyncOperation CopyAsync(IStorageFolder destin if (destFolder is ICreateFileWithStream cwsf) { - using var inStream = await ftpClient.OpenRead(FtpPath, token: cancellationToken); + await using var inStream = await ftpClient.OpenRead(FtpPath, token: cancellationToken); return await cwsf.CreateFileAsync(inStream, desiredNewName, option.Convert()); } else { BaseStorageFile file = await destFolder.CreateFileAsync(desiredNewName, option.Convert()); - using var stream = await file.OpenStreamForWriteAsync(); + await using var stream = await file.OpenStreamForWriteAsync(); return await ftpClient.DownloadStream(stream, FtpPath, token: cancellationToken) ? file : null; } }, ((IPasswordProtectedItem)this).RetryWithCredentialsAsync)); @@ -279,7 +278,7 @@ private async void FtpDataStreamingHandlerAsync(StreamedFileDataRequest request) return; } - using (var outStream = request.AsStreamForWrite()) + await using (var outStream = request.AsStreamForWrite()) { await ftpClient.DownloadStream(outStream, FtpPath); await outStream.FlushAsync(); @@ -292,7 +291,7 @@ private async void FtpDataStreamingHandlerAsync(StreamedFileDataRequest request) } } - private sealed class FtpFileBasicProperties : BaseBasicProperties + private sealed partial class FtpFileBasicProperties : BaseBasicProperties { public override ulong Size { get; } diff --git a/src/Files.App/Utils/Storage/StorageItems/FtpStorageFolder.cs b/src/Files.App/Utils/Storage/StorageItems/FtpStorageFolder.cs index 9cbd14aa17bb..48ddd3d47394 100644 --- a/src/Files.App/Utils/Storage/StorageItems/FtpStorageFolder.cs +++ b/src/Files.App/Utils/Storage/StorageItems/FtpStorageFolder.cs @@ -1,8 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.App.Data.Exceptions; -using Files.App.Storage.Storables; using FluentFTP; using System.IO; using System.Net; @@ -14,12 +12,12 @@ namespace Files.App.Utils.Storage { - public sealed class FtpStorageFolder : BaseStorageFolder, IPasswordProtectedItem + public sealed partial class FtpStorageFolder : BaseStorageFolder, IPasswordProtectedItem { public override string Path { get; } public override string Name { get; } public override string DisplayName => Name; - public override string DisplayType => "Folder".GetLocalizedResource(); + public override string DisplayType => Strings.Folder.GetLocalizedResource(); public string FtpPath { get; } public override string FolderRelativeId => $"0\\{Name}"; @@ -222,7 +220,7 @@ public override IAsyncOperation CreateFileAsync(string desiredN if (result is FtpStatus.Skipped) { if (options is CreationCollisionOption.FailIfExists) - throw new FileAlreadyExistsException("File already exists.", desiredName); + throw new FileAlreadyExistsException(desiredName); return null; } @@ -362,7 +360,7 @@ private AsyncFtpClient GetFtpClient() return new(host, credentials, port); } - private sealed class FtpFolderBasicProperties : BaseBasicProperties + private sealed partial class FtpFolderBasicProperties : BaseBasicProperties { public override ulong Size { get; } diff --git a/src/Files.App/Utils/Storage/StorageItems/NativeStorageFile.cs b/src/Files.App/Utils/Storage/StorageItems/NativeStorageFile.cs index ac855ca4a107..83c5e8f829d6 100644 --- a/src/Files.App/Utils/Storage/StorageItems/NativeStorageFile.cs +++ b/src/Files.App/Utils/Storage/StorageItems/NativeStorageFile.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.Shared.Helpers; using System.IO; @@ -10,6 +10,8 @@ using Windows.Storage.FileProperties; using Windows.Storage.Streams; using Windows.Win32; +using Windows.Win32.Storage.FileSystem; +using Windows.Win32.UI.Shell; using IO = System.IO; namespace Files.App.Utils.Storage @@ -18,7 +20,7 @@ namespace Files.App.Utils.Storage /// Shortcuts and alternate data stream. /// Uses *FromApp methods for file operations ///
  • - public sealed class NativeStorageFile : BaseStorageFile + public sealed partial class NativeStorageFile : BaseStorageFile { public override string Path { get; } public override string Name { get; } @@ -30,19 +32,56 @@ public sealed class NativeStorageFile : BaseStorageFile public bool IsShortcut => FileExtensionHelpers.IsShortcutOrUrlFile(FileType); public bool IsAlternateStream => System.Text.RegularExpressions.Regex.IsMatch(Path, @"\w:\w"); + private string? _DisplayType; public override string DisplayType { get { - var itemType = "File".GetLocalizedResource(); + if (_DisplayType is not null) + return _DisplayType; - if (Name.Contains('.', StringComparison.Ordinal)) - itemType = IO.Path.GetExtension(Name).Trim('.') + " " + itemType; + // Try to get the proper display type from Windows Shell + _DisplayType = GetDisplayTypeFromShell(); - return itemType; + return _DisplayType; } } + private unsafe string GetDisplayTypeFromShell() + { + // Try using SHGetFileInfo to get proper file type description + var extension = IO.Path.GetExtension(Name); + if (!string.IsNullOrEmpty(extension)) + { + SHFILEINFOW shfi = default; + var flags = SHGFI_FLAGS.SHGFI_TYPENAME | SHGFI_FLAGS.SHGFI_USEFILEATTRIBUTES; + + fixed (char* pExtension = extension) + { + var result = PInvoke.SHGetFileInfo( + pExtension, + FILE_FLAGS_AND_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL, + &shfi, + (uint)sizeof(SHFILEINFOW), + flags); + + if (result != 0 && shfi.szTypeName.Value[0] != '\0') + { + var typeName = shfi.szTypeName.ToString(); + if (!string.IsNullOrEmpty(typeName)) + return typeName; + } + } + } + + // Fallback to generic format + var itemType = Strings.File.GetLocalizedResource(); + if (Name.Contains('.', StringComparison.Ordinal)) + itemType = extension?.Trim('.') + " " + itemType; + + return itemType; + } + public override DateTimeOffset DateCreated { get; } public override Windows.Storage.FileAttributes Attributes { get; } = Windows.Storage.FileAttributes.Normal; public override IStorageItemExtraProperties Properties => new BaseBasicStorageItemExtraProperties(this); @@ -83,11 +122,11 @@ public override IAsyncOperation CopyAsync(IStorageFolder destin else { destFile.CreateFile(); - using (var inStream = await this.OpenStreamForReadAsync()) - using (var outStream = await destFile.OpenStreamForWriteAsync()) + await using (var inStream = await this.OpenStreamForReadAsync()) + await using (var outStream = await destFile.OpenStreamForWriteAsync()) { - await inStream.CopyToAsync(outStream); - await outStream.FlushAsync(); + await inStream.CopyToAsync(outStream, cancellationToken); + await outStream.FlushAsync(cancellationToken); } } return destFile; @@ -147,7 +186,7 @@ public static IAsyncOperation FromPathAsync(string path) if (IsNativePath(path) && CheckAccess(path)) { var name = IO.Path.GetFileName(path); - return Task.FromResult((BaseStorageFile)new NativeStorageFile(path, name[(name.LastIndexOf(":") + 1)..], DateTime.Now)).AsAsyncOperation(); + return Task.FromResult((BaseStorageFile)new NativeStorageFile(path, name[(name.LastIndexOf(':') + 1)..], DateTime.Now)).AsAsyncOperation(); } return Task.FromResult(null).AsAsyncOperation(); } @@ -162,7 +201,26 @@ private static bool IsNativePath(string path) { var isShortcut = FileExtensionHelpers.IsShortcutOrUrlFile(path); var isAlternateStream = RegexHelpers.AlternateStream().IsMatch(path); - return isShortcut || isAlternateStream; + var hasRestrictedAttributes = HasHiddenOrSystemAttributes(path); + return isShortcut || isAlternateStream || hasRestrictedAttributes; + } + + private static bool HasHiddenOrSystemAttributes(string path) + { + try + { + // Check if file has hidden or system attributes + // This will work even if WinRT APIs fail + var fileInfo = new FileInfo(path); + if (!fileInfo.Exists) + return false; + + return (fileInfo.Attributes & (IO.FileAttributes.Hidden | IO.FileAttributes.System)) != 0; + } + catch + { + return false; + } } public override bool IsEqual(IStorageItem item) => item?.Path == Path; @@ -248,11 +306,11 @@ public override IAsyncAction RenameAsync(string desiredName, NameCollisionOption else { destFile.CreateFile(); - using (var inStream = await this.OpenStreamForReadAsync()) - using (var outStream = await destFile.OpenStreamForWriteAsync()) + await using (var inStream = await this.OpenStreamForReadAsync()) + await using (var outStream = await destFile.OpenStreamForWriteAsync()) { - await inStream.CopyToAsync(outStream); - await outStream.FlushAsync(); + await inStream.CopyToAsync(outStream, cancellationToken); + await outStream.FlushAsync(cancellationToken); } await DeleteAsync(); } diff --git a/src/Files.App/Utils/Storage/StorageItems/ShellStorageFile.cs b/src/Files.App/Utils/Storage/StorageItems/ShellStorageFile.cs index aa75c1909940..1ef3e7343109 100644 --- a/src/Files.App/Utils/Storage/StorageItems/ShellStorageFile.cs +++ b/src/Files.App/Utils/Storage/StorageItems/ShellStorageFile.cs @@ -1,21 +1,23 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Runtime.InteropServices.WindowsRuntime; using Windows.Foundation; using Windows.Storage; using Windows.Storage.FileProperties; using Windows.Storage.Streams; +using Windows.Win32.UI.WindowsAndMessaging; using IO = System.IO; namespace Files.App.Utils.Storage { - public sealed class ShortcutStorageFile : ShellStorageFile, IShortcutStorageItem + public sealed partial class ShortcutStorageFile : ShellStorageFile, IShortcutStorageItem { public string TargetPath { get; } public string Arguments { get; } public string WorkingDirectory { get; } public bool RunAsAdmin { get; } + public SHOW_WINDOW_CMD ShowWindowCommand { get; set; } public ShortcutStorageFile(ShellLinkItem item) : base(item) { @@ -23,10 +25,11 @@ public ShortcutStorageFile(ShellLinkItem item) : base(item) Arguments = item.Arguments; WorkingDirectory = item.WorkingDirectory; RunAsAdmin = item.RunAsAdmin; + ShowWindowCommand = item.ShowWindowCommand; } } - public sealed class BinStorageFile : ShellStorageFile, IBinStorageItem + public sealed partial class BinStorageFile : ShellStorageFile, IBinStorageItem { public string OriginalPath { get; } public DateTimeOffset DateDeleted { get; } @@ -38,7 +41,7 @@ public BinStorageFile(ShellFileItem item) : base(item) } } - public class ShellStorageFile : BaseStorageFile + public partial class ShellStorageFile : BaseStorageFile { public override string Path { get; } public override string Name { get; } @@ -174,7 +177,7 @@ private Task GetBasicProperties() return Task.FromResult(new BaseBasicProperties()); } - private sealed class ShellFileBasicProperties : BaseBasicProperties + private sealed partial class ShellFileBasicProperties : BaseBasicProperties { private readonly ShellFileItem file; diff --git a/src/Files.App/Utils/Storage/StorageItems/ShellStorageFolder.cs b/src/Files.App/Utils/Storage/StorageItems/ShellStorageFolder.cs index 44b867205205..4189cdac8474 100644 --- a/src/Files.App/Utils/Storage/StorageItems/ShellStorageFolder.cs +++ b/src/Files.App/Utils/Storage/StorageItems/ShellStorageFolder.cs @@ -1,20 +1,22 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Runtime.InteropServices.WindowsRuntime; using Windows.Foundation; using Windows.Storage; using Windows.Storage.FileProperties; using Windows.Storage.Search; +using Windows.Win32.UI.WindowsAndMessaging; namespace Files.App.Utils.Storage { - public sealed class ShortcutStorageFolder : ShellStorageFolder, IShortcutStorageItem + public sealed partial class ShortcutStorageFolder : ShellStorageFolder, IShortcutStorageItem { public string TargetPath { get; } public string Arguments { get; } public string WorkingDirectory { get; } public bool RunAsAdmin { get; } + public SHOW_WINDOW_CMD ShowWindowCommand { get; set; } public ShortcutStorageFolder(ShellLinkItem item) : base(item) { @@ -22,6 +24,7 @@ public ShortcutStorageFolder(ShellLinkItem item) : base(item) Arguments = item.Arguments; WorkingDirectory = item.WorkingDirectory; RunAsAdmin = item.RunAsAdmin; + ShowWindowCommand = item.ShowWindowCommand; } } @@ -33,7 +36,7 @@ public interface IShortcutStorageItem : IStorageItem bool RunAsAdmin { get; } } - public sealed class BinStorageFolder : ShellStorageFolder, IBinStorageItem + public sealed partial class BinStorageFolder : ShellStorageFolder, IBinStorageItem { public string OriginalPath { get; } public DateTimeOffset DateDeleted { get; } @@ -51,7 +54,7 @@ public interface IBinStorageItem : IStorageItem DateTimeOffset DateDeleted { get; } } - public class ShellStorageFolder : BaseStorageFolder + public partial class ShellStorageFolder : BaseStorageFolder { public override string Path { get; } public override string Name { get; } @@ -292,7 +295,7 @@ public override IAsyncOperation GetThumbnailAsync(Thumbnai }); } - private sealed class ShellFolderBasicProperties : BaseBasicProperties + private sealed partial class ShellFolderBasicProperties : BaseBasicProperties { private readonly ShellFileItem folder; diff --git a/src/Files.App/Utils/Storage/StorageItems/StreamWithContentType.cs b/src/Files.App/Utils/Storage/StorageItems/StreamWithContentType.cs index d1971d9fe17d..e12d5f143a25 100644 --- a/src/Files.App/Utils/Storage/StorageItems/StreamWithContentType.cs +++ b/src/Files.App/Utils/Storage/StorageItems/StreamWithContentType.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.IO; using System.Runtime.InteropServices; @@ -10,7 +10,7 @@ namespace Files.App.Utils.Storage { - public sealed class InputStreamWithDisposeCallback : IInputStream + public sealed partial class InputStreamWithDisposeCallback : IInputStream { private Stream stream; private IInputStream iStream; @@ -35,7 +35,7 @@ public void Dispose() } } - public sealed class NonSeekableRandomAccessStreamForWrite : IRandomAccessStream + public sealed partial class NonSeekableRandomAccessStreamForWrite : IRandomAccessStream { private Stream stream; private IOutputStream oStream; @@ -117,7 +117,7 @@ public IAsyncOperation FlushAsync() return AsyncInfo.Run(async (cancellationToken) => { - await stream.FlushAsync(); + await stream.FlushAsync(cancellationToken); return true; }); } @@ -131,7 +131,7 @@ public void Dispose() } } - public sealed class NonSeekableRandomAccessStreamForRead : IRandomAccessStream + public sealed partial class NonSeekableRandomAccessStreamForRead : IRandomAccessStream { private Stream stream; private IRandomAccessStream imrac; @@ -227,7 +227,7 @@ public void Dispose() } } - public sealed class StreamWithContentType : IRandomAccessStreamWithContentType + public sealed partial class StreamWithContentType : IRandomAccessStreamWithContentType { private IRandomAccessStream baseStream; @@ -269,7 +269,7 @@ public void Dispose() public string ContentType { get; set; } = "application/octet-stream"; } - public sealed class ComStreamWrapper : Stream + public sealed partial class ComStreamWrapper : Stream { private IStream iStream; private STATSTG iStreamStat; diff --git a/src/Files.App/Utils/Storage/StorageItems/SystemStorageFile.cs b/src/Files.App/Utils/Storage/StorageItems/SystemStorageFile.cs index a836bde6cd4e..c0eb809d0c39 100644 --- a/src/Files.App/Utils/Storage/StorageItems/SystemStorageFile.cs +++ b/src/Files.App/Utils/Storage/StorageItems/SystemStorageFile.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.IO; using System.Runtime.InteropServices.WindowsRuntime; @@ -12,7 +12,7 @@ namespace Files.App.Utils.Storage { - public sealed class SystemStorageFile : BaseStorageFile + public sealed partial class SystemStorageFile : BaseStorageFile { public StorageFile File { get; } @@ -31,9 +31,22 @@ public sealed class SystemStorageFile : BaseStorageFile public SystemStorageFile(StorageFile file) => File = file; public static IAsyncOperation FromPathAsync(string path) - => AsyncInfo.Run(async (cancellationToken) - => new SystemStorageFile(await StorageFile.GetFileFromPathAsync(path)) - ); + => AsyncInfo.Run(async (cancellationToken) => + { + try + { + return new SystemStorageFile(await StorageFile.GetFileFromPathAsync(path)); + } + catch (UnauthorizedAccessException) + { + // File may have hidden/system attributes that prevent WinRT API access + return null; + } + catch (FileNotFoundException) + { + return null; + } + }); public override IAsyncOperation ToStorageFileAsync() => Task.FromResult(File).AsAsyncOperation(); @@ -69,17 +82,17 @@ public override IAsyncOperation CopyAsync(IStorageFolder destin } else if (destFolder is ICreateFileWithStream cwsf) { - using var inStream = await this.OpenStreamForReadAsync(); + await using var inStream = await this.OpenStreamForReadAsync(); return await cwsf.CreateFileAsync(inStream, desiredNewName, option.Convert()); } else { var destFile = await destFolder.CreateFileAsync(desiredNewName, option.Convert()); - using (var inStream = await this.OpenStreamForReadAsync()) - using (var outStream = await destFile.OpenStreamForWriteAsync()) + await using (var inStream = await this.OpenStreamForReadAsync()) + await using (var outStream = await destFile.OpenStreamForWriteAsync()) { - await inStream.CopyToAsync(outStream); - await outStream.FlushAsync(); + await inStream.CopyToAsync(outStream, cancellationToken); + await outStream.FlushAsync(cancellationToken); } return destFile; } @@ -93,11 +106,11 @@ public override IAsyncOperation CopyAsync(IStorageFolder destin option == NameCollisionOption.ReplaceExisting); if (!hFile.IsInvalid) { - using (var inStream = await this.OpenStreamForReadAsync()) - using (var outStream = new FileStream(hFile, FileAccess.Write)) + await using (var inStream = await this.OpenStreamForReadAsync()) + await using (var outStream = new FileStream(hFile, FileAccess.Write)) { - await inStream.CopyToAsync(outStream); - await outStream.FlushAsync(); + await inStream.CopyToAsync(outStream, cancellationToken); + await outStream.FlushAsync(cancellationToken); } return new NativeStorageFile(destination, desiredNewName, DateTime.Now); } @@ -140,22 +153,22 @@ public override IAsyncAction CopyAndReplaceAsync(IStorageFile fileToReplace) { return AsyncInfo.Run(async (cancellationToken) => { - using var inStream = await this.OpenStreamForReadAsync(); - using var outStream = await fileToReplace.OpenStreamForWriteAsync(); + await using var inStream = await this.OpenStreamForReadAsync(); + await using var outStream = await fileToReplace.OpenStreamForWriteAsync(); - await inStream.CopyToAsync(outStream); - await outStream.FlushAsync(); + await inStream.CopyToAsync(outStream, cancellationToken); + await outStream.FlushAsync(cancellationToken); }); } public override IAsyncAction MoveAndReplaceAsync(IStorageFile fileToReplace) { return AsyncInfo.Run(async (cancellationToken) => { - using var inStream = await this.OpenStreamForReadAsync(); - using var outStream = await fileToReplace.OpenStreamForWriteAsync(); + await using var inStream = await this.OpenStreamForReadAsync(); + await using var outStream = await fileToReplace.OpenStreamForWriteAsync(); - await inStream.CopyToAsync(outStream); - await outStream.FlushAsync(); + await inStream.CopyToAsync(outStream, cancellationToken); + await outStream.FlushAsync(cancellationToken); // Move unsupported, copy but do not delete original }); } @@ -173,7 +186,7 @@ public override IAsyncOperation GetThumbnailAsync(Thumbnai public override IAsyncOperation GetThumbnailAsync(ThumbnailMode mode, uint requestedSize, ThumbnailOptions options) => File.GetThumbnailAsync(mode, requestedSize, options); - private sealed class SystemFileBasicProperties : BaseBasicProperties + private sealed partial class SystemFileBasicProperties : BaseBasicProperties { private readonly IStorageItemExtraProperties basicProps; private readonly DateTimeOffset? dateCreated; diff --git a/src/Files.App/Utils/Storage/StorageItems/SystemStorageFolder.cs b/src/Files.App/Utils/Storage/StorageItems/SystemStorageFolder.cs index 95ffd811adad..9d7e6f2fa194 100644 --- a/src/Files.App/Utils/Storage/StorageItems/SystemStorageFolder.cs +++ b/src/Files.App/Utils/Storage/StorageItems/SystemStorageFolder.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.Extensions.Logging; using System.Runtime.InteropServices.WindowsRuntime; @@ -12,7 +12,7 @@ namespace Files.App.Utils.Storage { - public sealed class SystemStorageFolder : BaseStorageFolder + public sealed partial class SystemStorageFolder : BaseStorageFolder { public StorageFolder Folder { get; } @@ -151,7 +151,7 @@ public override IAsyncOperation GetThumbnailAsync(Thumbnai public override IAsyncOperation GetThumbnailAsync(ThumbnailMode mode, uint requestedSize, ThumbnailOptions options) => Folder.GetThumbnailAsync(mode, requestedSize, options); - private sealed class SystemFolderBasicProperties : BaseBasicProperties + private sealed partial class SystemFolderBasicProperties : BaseBasicProperties { private readonly IStorageItemExtraProperties basicProps; private readonly DateTimeOffset? dateCreated; diff --git a/src/Files.App/Utils/Storage/StorageItems/VirtualStorageFile.cs b/src/Files.App/Utils/Storage/StorageItems/VirtualStorageFile.cs index 06f9032f3ab0..a9602339917b 100644 --- a/src/Files.App/Utils/Storage/StorageItems/VirtualStorageFile.cs +++ b/src/Files.App/Utils/Storage/StorageItems/VirtualStorageFile.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.IO; using System.Runtime.InteropServices.WindowsRuntime; @@ -11,7 +11,7 @@ namespace Files.App.Utils.Storage { - public sealed class VirtualStorageFile : BaseStorageFile + public sealed partial class VirtualStorageFile : BaseStorageFile { public override string Path { get; } public override string Name { get; } @@ -24,7 +24,7 @@ public override string DisplayType { get { - var itemType = "File".GetLocalizedResource(); + var itemType = Strings.File.GetLocalizedResource(); if (Name.Contains('.', StringComparison.Ordinal)) { itemType = IO.Path.GetExtension(Name).Trim('.') + " " + itemType; @@ -50,7 +50,7 @@ private async void StreamedFileWriterAsync(StreamedFileDataRequest request) { try { - using (var stream = request.AsStreamForWrite()) + await using (var stream = request.AsStreamForWrite()) { await Contents.CopyToAsync(stream); await stream.FlushAsync(); @@ -119,17 +119,17 @@ public override IAsyncOperation CopyAsync(IStorageFolder destin if (destFolder is ICreateFileWithStream cwsf) { - using var inStream = await this.OpenStreamForReadAsync(); + await using var inStream = await this.OpenStreamForReadAsync(); return await cwsf.CreateFileAsync(inStream, desiredNewName, option.Convert()); } else { var destFile = await destFolder.CreateFileAsync(desiredNewName, option.Convert()); - using (var inStream = await this.OpenStreamForReadAsync()) - using (var outStream = await destFile.OpenStreamForWriteAsync()) + await using (var inStream = await this.OpenStreamForReadAsync()) + await using (var outStream = await destFile.OpenStreamForWriteAsync()) { - await inStream.CopyToAsync(outStream); - await outStream.FlushAsync(); + await inStream.CopyToAsync(outStream, cancellationToken); + await outStream.FlushAsync(cancellationToken); } return destFile; } diff --git a/src/Files.App/Utils/Storage/StorageItems/VirtualStorageFolder.cs b/src/Files.App/Utils/Storage/StorageItems/VirtualStorageFolder.cs index 7ce69520e063..d2c1b1cbc088 100644 --- a/src/Files.App/Utils/Storage/StorageItems/VirtualStorageFolder.cs +++ b/src/Files.App/Utils/Storage/StorageItems/VirtualStorageFolder.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Runtime.InteropServices.WindowsRuntime; using Windows.Foundation; @@ -9,12 +9,12 @@ namespace Files.App.Utils.Storage { - public sealed class VirtualStorageFolder : BaseStorageFolder + public sealed partial class VirtualStorageFolder : BaseStorageFolder { public override string Path { get; } public override string Name { get; } public override string DisplayName => Name; - public override string DisplayType => "Folder".GetLocalizedResource(); + public override string DisplayType => Strings.Folder.GetLocalizedResource(); public override string FolderRelativeId => $"0\\{Name}"; public override DateTimeOffset DateCreated { get; } diff --git a/src/Files.App/Utils/Storage/StorageItems/VirtualStorageItem.cs b/src/Files.App/Utils/Storage/StorageItems/VirtualStorageItem.cs index 62f749188e6b..4efdfcc6699e 100644 --- a/src/Files.App/Utils/Storage/StorageItems/VirtualStorageItem.cs +++ b/src/Files.App/Utils/Storage/StorageItems/VirtualStorageItem.cs @@ -1,12 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.IO; using System.Runtime.InteropServices.WindowsRuntime; using Windows.Foundation; using Windows.Storage; using Windows.Storage.FileProperties; -using static Files.App.Helpers.Win32Helper; namespace Files.App.Utils.Storage { @@ -15,7 +14,7 @@ namespace Files.App.Utils.Storage /// representing a standard filesystem item. As such, VirtualStorageItem does not support hidden, /// shortcut, or link items. ///
    - public sealed class VirtualStorageItem : IStorageItem + public sealed partial class VirtualStorageItem : IStorageItem { private static BasicProperties props; @@ -87,7 +86,7 @@ private async void StreamedFileWriterAsync(StreamedFileDataRequest request) { try { - using (var stream = request.AsStreamForWrite()) + await using (var stream = request.AsStreamForWrite()) { await stream.FlushAsync(); } diff --git a/src/Files.App/Utils/Storage/StorageItems/ZipStorageFile.cs b/src/Files.App/Utils/Storage/StorageItems/ZipStorageFile.cs index 64f9d532ac90..dff6b9dc7d93 100644 --- a/src/Files.App/Utils/Storage/StorageItems/ZipStorageFile.cs +++ b/src/Files.App/Utils/Storage/StorageItems/ZipStorageFile.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.Shared.Helpers; using SevenZip; @@ -14,7 +14,7 @@ namespace Files.App.Utils.Storage { - public sealed class ZipStorageFile : BaseStorageFile, IPasswordProtectedItem + public sealed partial class ZipStorageFile : BaseStorageFile, IPasswordProtectedItem { private readonly string containerPath; private readonly BaseStorageFile backingFile; @@ -30,7 +30,7 @@ public override string DisplayType { get { - var itemType = "File".GetLocalizedResource(); + var itemType = Strings.File.GetLocalizedResource(); if (Name.Contains('.', StringComparison.Ordinal)) { itemType = FileType.Trim('.') + " " + itemType; @@ -254,7 +254,7 @@ public override IAsyncOperation CopyAsync(IStorageFolder destin else { var destFile = await destFolder.CreateFileAsync(desiredNewName, option.Convert()); - using var outStream = await destFile.OpenStreamForWriteAsync(); + await using var outStream = await destFile.OpenStreamForWriteAsync(); await SafetyExtensions.WrapAsync(() => zipFile.ExtractFileAsync(entry.Index, outStream), async (_, exception) => { await destFile.DeleteAsync(); @@ -281,7 +281,7 @@ public override IAsyncAction CopyAndReplaceAsync(IStorageFile fileToReplace) } using var hDestFile = fileToReplace.CreateSafeFileHandle(FileAccess.ReadWrite); - using (var outStream = new FileStream(hDestFile, FileAccess.Write)) + await using (var outStream = new FileStream(hDestFile, FileAccess.Write)) { await zipFile.ExtractFileAsync(entry.Index, outStream); } @@ -323,14 +323,16 @@ public override IAsyncAction RenameAsync(string desiredName, NameCollisionOption } using (var ms = new MemoryStream()) { - using (var archiveStream = await OpenZipFileAsync(FileAccessMode.Read)) + await using (var archiveStream = await OpenZipFileAsync(FileAccessMode.Read)) { SevenZipCompressor compressor = new SevenZipCompressor() { CompressionMode = CompressionMode.Append }; + compressor.CustomParameters.Add("cu", "on"); compressor.SetFormatFromExistingArchive(archiveStream); var fileName = IO.Path.GetRelativePath(containerPath, IO.Path.Combine(IO.Path.GetDirectoryName(Path), desiredName)); await compressor.ModifyArchiveAsync(archiveStream, new Dictionary() { { index, fileName } }, Credentials.Password, ms); } - using (var archiveStream = await OpenZipFileAsync(FileAccessMode.ReadWrite)) + + await using (var archiveStream = await OpenZipFileAsync(FileAccessMode.ReadWrite)) { ms.Position = 0; await ms.CopyToAsync(archiveStream); @@ -371,13 +373,14 @@ public override IAsyncAction DeleteAsync(StorageDeleteOption option) } using (var ms = new MemoryStream()) { - using (var archiveStream = await OpenZipFileAsync(FileAccessMode.Read)) + await using (var archiveStream = await OpenZipFileAsync(FileAccessMode.Read)) { SevenZipCompressor compressor = new SevenZipCompressor() { CompressionMode = CompressionMode.Append }; + compressor.CustomParameters.Add("cu", "on"); compressor.SetFormatFromExistingArchive(archiveStream); await compressor.ModifyArchiveAsync(archiveStream, new Dictionary() { { index, null } }, Credentials.Password, ms); } - using (var archiveStream = await OpenZipFileAsync(FileAccessMode.ReadWrite)) + await using (var archiveStream = await OpenZipFileAsync(FileAccessMode.ReadWrite)) { ms.Position = 0; await ms.CopyToAsync(archiveStream); @@ -505,7 +508,7 @@ private StreamedFileDataRequestedHandler ZipDataStreamingHandler(string name) } else { - using (var outStream = request.AsStreamForWrite()) + await using (var outStream = request.AsStreamForWrite()) { await zipFile.ExtractFileAsync(entry.Index, outStream); } @@ -519,7 +522,7 @@ private StreamedFileDataRequestedHandler ZipDataStreamingHandler(string name) }; } - private sealed class ZipFileBasicProperties : BaseBasicProperties + private sealed partial class ZipFileBasicProperties : BaseBasicProperties { private ArchiveFileInfo entry; diff --git a/src/Files.App/Utils/Storage/StorageItems/ZipStorageFolder.cs b/src/Files.App/Utils/Storage/StorageItems/ZipStorageFolder.cs index 23897ee7d09d..cca4acadd169 100644 --- a/src/Files.App/Utils/Storage/StorageItems/ZipStorageFolder.cs +++ b/src/Files.App/Utils/Storage/StorageItems/ZipStorageFolder.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.Shared.Helpers; using SevenZip; @@ -16,7 +16,7 @@ namespace Files.App.Utils.Storage { - public sealed class ZipStorageFolder : BaseStorageFolder, ICreateFileWithStream, IPasswordProtectedItem + public sealed partial class ZipStorageFolder : BaseStorageFolder, ICreateFileWithStream, IPasswordProtectedItem { private readonly string containerPath; private BaseStorageFile backingFile; @@ -24,7 +24,7 @@ public sealed class ZipStorageFolder : BaseStorageFolder, ICreateFileWithStream, public override string Path { get; } public override string Name { get; } public override string DisplayName => Name; - public override string DisplayType => "Folder".GetLocalizedResource(); + public override string DisplayType => Strings.Folder.GetLocalizedResource(); public override string FolderRelativeId => $"0\\{Name}"; public override DateTimeOffset DateCreated { get; } @@ -47,10 +47,7 @@ public ZipStorageFolder(string path, string containerPath, ArchiveFileInfo entry => DateCreated = entry.CreationTime == DateTime.MinValue ? DateTimeOffset.MinValue : entry.CreationTime; public ZipStorageFolder(BaseStorageFile backingFile) { - if (string.IsNullOrEmpty(backingFile.Path)) - { - throw new ArgumentException("Backing file Path cannot be null"); - } + ArgumentException.ThrowIfNullOrEmpty(backingFile.Path); Name = IO.Path.GetFileName(backingFile.Path.TrimEnd('\\', '/')); Path = backingFile.Path; this.containerPath = backingFile.Path; @@ -101,17 +98,18 @@ public static async Task CheckDefaultZipApp(string filePath) { Func> queryFileAssoc = async () => { - var assoc = await Win32Helper.GetFileAssociationAsync(filePath); + var assoc = await Win32Helper.GetDefaultFileAssociationAsync(filePath); if (assoc is not null) { - return assoc == Package.Current.Id.FamilyName - || assoc.EndsWith("Files.App\\Files.exe", StringComparison.OrdinalIgnoreCase) + return Constants.Distributions.KnownAppNames.Any(x => assoc.StartsWith(x, StringComparison.OrdinalIgnoreCase)) + || assoc == Package.Current.Id.FamilyName + || assoc.EndsWith("Files.exe", StringComparison.OrdinalIgnoreCase) || assoc.Equals(IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "explorer.exe"), StringComparison.OrdinalIgnoreCase); } return true; }; var ext = IO.Path.GetExtension(filePath)?.ToLowerInvariant(); - return await defaultAppDict.GetAsync(ext, queryFileAssoc); + return await defaultAppDict.GetAsync(ext ?? "", queryFileAssoc); } public static IAsyncOperation FromPathAsync(string path) @@ -312,14 +310,15 @@ public override IAsyncOperation CreateFolderAsync(string desi using (var ms = new MemoryStream()) { - using (var archiveStream = await OpenZipFileAsync(FileAccessMode.Read)) + await using (var archiveStream = await OpenZipFileAsync(FileAccessMode.Read)) { SevenZipCompressor compressor = new SevenZipCompressor() { CompressionMode = CompressionMode.Append }; + compressor.CustomParameters.Add("cu", "on"); compressor.SetFormatFromExistingArchive(archiveStream); var fileName = IO.Path.GetRelativePath(containerPath, zipDesiredName); await compressor.CompressStreamDictionaryAsync(archiveStream, new Dictionary() { { fileName, null } }, Credentials.Password, ms); } - using (var archiveStream = await OpenZipFileAsync(FileAccessMode.ReadWrite)) + await using (var archiveStream = await OpenZipFileAsync(FileAccessMode.ReadWrite)) { ms.Position = 0; await ms.CopyToAsync(archiveStream); @@ -363,9 +362,10 @@ public override IAsyncAction RenameAsync(string desiredName, NameCollisionOption } using (var ms = new MemoryStream()) { - using (var archiveStream = await OpenZipFileAsync(FileAccessMode.Read)) + await using (var archiveStream = await OpenZipFileAsync(FileAccessMode.Read)) { SevenZipCompressor compressor = new SevenZipCompressor() { CompressionMode = CompressionMode.Append }; + compressor.CustomParameters.Add("cu", "on"); compressor.SetFormatFromExistingArchive(archiveStream); var folderKey = IO.Path.GetRelativePath(containerPath, Path); var folderDes = IO.Path.Combine(IO.Path.GetDirectoryName(folderKey), desiredName); @@ -373,7 +373,7 @@ public override IAsyncAction RenameAsync(string desiredName, NameCollisionOption IO.Path.Combine(folderDes, IO.Path.GetRelativePath(folderKey, x.Key))))); await compressor.ModifyArchiveAsync(archiveStream, entriesMap, Credentials.Password, ms); } - using (var archiveStream = await OpenZipFileAsync(FileAccessMode.ReadWrite)) + await using (var archiveStream = await OpenZipFileAsync(FileAccessMode.ReadWrite)) { ms.Position = 0; await ms.CopyToAsync(archiveStream); @@ -414,14 +414,15 @@ public override IAsyncAction DeleteAsync(StorageDeleteOption option) } using (var ms = new MemoryStream()) { - using (var archiveStream = await OpenZipFileAsync(FileAccessMode.Read)) + await using (var archiveStream = await OpenZipFileAsync(FileAccessMode.Read)) { SevenZipCompressor compressor = new SevenZipCompressor() { CompressionMode = CompressionMode.Append }; + compressor.CustomParameters.Add("cu", "on"); compressor.SetFormatFromExistingArchive(archiveStream); var entriesMap = new Dictionary(index.Select(x => new KeyValuePair(x.Index, null))); await compressor.ModifyArchiveAsync(archiveStream, entriesMap, Credentials.Password, ms); } - using (var archiveStream = await OpenZipFileAsync(FileAccessMode.ReadWrite)) + await using (var archiveStream = await OpenZipFileAsync(FileAccessMode.ReadWrite)) { ms.Position = 0; await ms.CopyToAsync(archiveStream); @@ -544,7 +545,7 @@ public static Task InitArchive(IStorageFile file, OutArchiveFormat format) return SafetyExtensions.IgnoreExceptions(async () => { using var fileStream = await file.OpenAsync(FileAccessMode.ReadWrite); - using var stream = fileStream.AsStream(); + await using var stream = fileStream.AsStream(); return await InitArchive(stream, format); }); } @@ -556,6 +557,7 @@ private static async Task InitArchive(Stream stream, OutArchiveFormat form CompressionMode = CompressionMode.Create, ArchiveFormat = format }; + compressor.CustomParameters.Add("cu", "on"); await compressor.CompressStreamDictionaryAsync(stream, new Dictionary()); await stream.FlushAsync(); return true; @@ -624,14 +626,15 @@ public IAsyncOperation CreateFileAsync(Stream contents, string using (var ms = new MemoryStream()) { - using (var archiveStream = await OpenZipFileAsync(FileAccessMode.Read)) + await using (var archiveStream = await OpenZipFileAsync(FileAccessMode.Read)) { SevenZipCompressor compressor = new SevenZipCompressor() { CompressionMode = CompressionMode.Append }; + compressor.CustomParameters.Add("cu", "on"); compressor.SetFormatFromExistingArchive(archiveStream); var fileName = IO.Path.GetRelativePath(containerPath, zipDesiredName); await compressor.CompressStreamDictionaryAsync(archiveStream, new Dictionary() { { fileName, contents } }, Credentials.Password, ms); } - using (var archiveStream = await OpenZipFileAsync(FileAccessMode.ReadWrite)) + await using (var archiveStream = await OpenZipFileAsync(FileAccessMode.ReadWrite)) { ms.Position = 0; await ms.CopyToAsync(archiveStream); @@ -646,7 +649,7 @@ public IAsyncOperation CreateFileAsync(Stream contents, string }, ((IPasswordProtectedItem)this).RetryWithCredentialsAsync)); } - private sealed class ZipFolderBasicProperties : BaseBasicProperties + private sealed partial class ZipFolderBasicProperties : BaseBasicProperties { private ArchiveFileInfo entry; diff --git a/src/Files.App/Utils/Taskbar/SystemTrayIcon.cs b/src/Files.App/Utils/Taskbar/SystemTrayIcon.cs index e94566279925..ecc590cf148f 100644 --- a/src/Files.App/Utils/Taskbar/SystemTrayIcon.cs +++ b/src/Files.App/Utils/Taskbar/SystemTrayIcon.cs @@ -1,22 +1,22 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using System.Runtime.InteropServices; using System.Drawing; +using System.Runtime.InteropServices; +using Windows.ApplicationModel; using Windows.Foundation; +using Windows.System; using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.UI.Shell; using Windows.Win32.UI.WindowsAndMessaging; -using Windows.ApplicationModel; -using Windows.System; namespace Files.App.Utils.Taskbar { /// /// Represents a tray icon of Notification Area so-called System Tray. /// - public sealed class SystemTrayIcon : IDisposable + public sealed partial class SystemTrayIcon : IDisposable { // Constants @@ -27,7 +27,16 @@ public sealed class SystemTrayIcon : IDisposable // Fields - private readonly static Guid _trayIconGuid = new("684F2832-AC2B-4630-98C2-73D6AEBD46B7"); + private readonly static Guid _trayIconGuid = AppLifecycleHelper.AppEnvironment switch + { + AppEnvironment.Dev => new Guid("684F2832-AC2B-4630-98C2-73D6AEBD4001"), + AppEnvironment.SideloadPreview => new Guid("684F2832-AC2B-4630-98C2-73D6AEBD4002"), + AppEnvironment.StorePreview => new Guid("684F2832-AC2B-4630-98C2-73D6AEBD4003"), + AppEnvironment.SideloadStable => new Guid("684F2832-AC2B-4630-98C2-73D6AEBD4004"), + AppEnvironment.StoreStable => new Guid("684F2832-AC2B-4630-98C2-73D6AEBD4005"), + _ => new Guid("684F2832-AC2B-4630-98C2-73D6AEBD4001") + }; + private readonly SystemTrayIconWindow _IconWindow; @@ -228,10 +237,10 @@ private void ShowContextMenu() DestroyMenuSafeHandle hMenu = PInvoke.CreatePopupMenu_SafeHandle(); // Generate the classic context menu - PInvoke.AppendMenu(hMenu, MENU_ITEM_FLAGS.MF_BYCOMMAND, WM_FILES_CONTEXTMENU_DOCSLINK, "Documentation".GetLocalizedResource()); + PInvoke.AppendMenu(hMenu, MENU_ITEM_FLAGS.MF_BYCOMMAND, WM_FILES_CONTEXTMENU_DOCSLINK, Strings.Documentation.GetLocalizedResource()); PInvoke.AppendMenu(hMenu, MENU_ITEM_FLAGS.MF_SEPARATOR, 0u, string.Empty); //PInvoke.AppendMenu(hMenu, MENU_ITEM_FLAGS.MF_BYCOMMAND, WM_FILES_CONTEXTMENU_RESTART, "Restart".GetLocalizedResource()); - PInvoke.AppendMenu(hMenu, MENU_ITEM_FLAGS.MF_BYCOMMAND, WM_FILES_CONTEXTMENU_QUIT, "Quit".GetLocalizedResource()); + PInvoke.AppendMenu(hMenu, MENU_ITEM_FLAGS.MF_BYCOMMAND, WM_FILES_CONTEXTMENU_QUIT, Strings.Quit.GetLocalizedResource()); PInvoke.SetForegroundWindow(_IconWindow.WindowHandle); TRACK_POPUP_MENU_FLAGS tRACK_POPUP_MENU_FLAGS = @@ -264,7 +273,7 @@ private void OnLeftClicked() { _lastLaunchDate = DateTime.Now; - _ = Launcher.LaunchUriAsync(new Uri("files-uwp:")); + _ = Launcher.LaunchUriAsync(new Uri("files-dev:")); } else MainWindow.Instance.Activate(); @@ -279,7 +288,10 @@ private void OnRestartClicked() { Microsoft.Windows.AppLifecycle.AppInstance.Restart(""); - Program.Pool.Release(); + var pool = new Semaphore(0, 1, $"Files-{AppLifecycleHelper.AppEnvironment}-Instance", out var isNew); + if (!isNew) + pool.Release(); + Environment.Exit(0); } @@ -288,8 +300,10 @@ private void OnQuitClicked() Hide(); App.AppModel.ForceProcessTermination = true; - if (Program.Pool is not null) - Program.Pool.Release(); + + var pool = new Semaphore(0, 1, $"Files-{AppLifecycleHelper.AppEnvironment}-Instance", out var isNew); + if (!isNew) + pool.Release(); else App.Current.Exit(); } diff --git a/src/Files.App/Utils/Taskbar/SystemTrayIconWindow.cs b/src/Files.App/Utils/Taskbar/SystemTrayIconWindow.cs index 3faa283152e2..532ed33baf5f 100644 --- a/src/Files.App/Utils/Taskbar/SystemTrayIconWindow.cs +++ b/src/Files.App/Utils/Taskbar/SystemTrayIconWindow.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Runtime.InteropServices; using Windows.Win32; @@ -13,7 +13,7 @@ namespace Files.App.Utils.Taskbar ///
    /// This is provided to handle context menu and retrieve mouse events, not a regular window class. ///
    - public sealed class SystemTrayIconWindow : IDisposable + public sealed partial class SystemTrayIconWindow : IDisposable { private SystemTrayIcon _trayIcon; @@ -26,16 +26,19 @@ internal HWND WindowHandle public unsafe SystemTrayIconWindow(SystemTrayIcon icon) { _windowProcedure = WindowProc; - this._trayIcon = icon; - string text = "FilesTrayIcon_" + this._trayIcon.Id; + _trayIcon = icon; + string text = "FilesTrayIcon_" + _trayIcon.Id; fixed (char* ptr = text) { + var pWindProc = Marshal.GetFunctionPointerForDelegate(_windowProcedure); + var pfnWndProc = (delegate* unmanaged[Stdcall])pWindProc; + WNDCLASSEXW param = new() { cbSize = (uint)Marshal.SizeOf(typeof(WNDCLASSEXW)), style = WNDCLASS_STYLES.CS_DBLCLKS, - lpfnWndProc = _windowProcedure, + lpfnWndProc = pfnWndProc, cbClsExtra = 0, cbWndExtra = 0, hInstance = PInvoke.GetModuleHandle(default(PCWSTR)), diff --git a/src/Files.App/Utils/Widgets/WidgetsHelpers.cs b/src/Files.App/Utils/Widgets/WidgetsHelpers.cs index 4a274bf123dd..09db8a1f5b29 100644 --- a/src/Files.App/Utils/Widgets/WidgetsHelpers.cs +++ b/src/Files.App/Utils/Widgets/WidgetsHelpers.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.Helpers { diff --git a/src/Files.App/ViewModels/Dialogs/AddBranchDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/AddBranchDialogViewModel.cs index 722a931b6a5c..d869959bf843 100644 --- a/src/Files.App/ViewModels/Dialogs/AddBranchDialogViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/AddBranchDialogViewModel.cs @@ -1,6 +1,6 @@ namespace Files.App.ViewModels.Dialogs { - public sealed class AddBranchDialogViewModel : ObservableObject + public sealed partial class AddBranchDialogViewModel : ObservableObject { private readonly string _repositoryPath; diff --git a/src/Files.App/ViewModels/Dialogs/AddItemDialog/AddItemDialogListItemViewModel.cs b/src/Files.App/ViewModels/Dialogs/AddItemDialog/AddItemDialogListItemViewModel.cs index dfc6664b0bdd..53c047dd518c 100644 --- a/src/Files.App/ViewModels/Dialogs/AddItemDialog/AddItemDialogListItemViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/AddItemDialog/AddItemDialogListItemViewModel.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.Shared.Utils; namespace Files.App.ViewModels.Dialogs.AddItemDialog { - public sealed class AddItemDialogListItemViewModel : ObservableObject + public sealed partial class AddItemDialogListItemViewModel : ObservableObject { public string? Header { get; set; } diff --git a/src/Files.App/ViewModels/Dialogs/AddItemDialog/AddItemDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/AddItemDialog/AddItemDialogViewModel.cs index c6992076c86c..9d5e459a7c06 100644 --- a/src/Files.App/ViewModels/Dialogs/AddItemDialog/AddItemDialogViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/AddItemDialog/AddItemDialogViewModel.cs @@ -1,12 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.Shared; using Files.Shared.Utils; namespace Files.App.ViewModels.Dialogs.AddItemDialog { - public sealed class AddItemDialogViewModel : ObservableObject + public sealed partial class AddItemDialogViewModel : ObservableObject { private readonly IImageService _imagingService; @@ -33,8 +32,8 @@ public async Task AddItemsToListAsync(IEnumerable itemTypes) AddItemsList.Add(new() { - Header = "Folder".ToLocalized(), - SubHeader = "AddDialogListFolderSubHeader".ToLocalized(), + Header = Strings.Folder.ToLocalized(), + SubHeader = Strings.AddDialogListFolderSubHeader.ToLocalized(), Glyph = "\xE838", IsItemEnabled = true, ItemResult = new() @@ -45,8 +44,8 @@ public async Task AddItemsToListAsync(IEnumerable itemTypes) AddItemsList.Add(new() { - Header = "File".ToLocalized(), - SubHeader = "AddDialogListFileSubHeader".ToLocalized(), + Header = Strings.File.ToLocalized(), + SubHeader = Strings.AddDialogListFileSubHeader.ToLocalized(), Glyph = "\xE8A5", IsItemEnabled = true, ItemResult = new() @@ -58,8 +57,8 @@ public async Task AddItemsToListAsync(IEnumerable itemTypes) AddItemsList.Add(new() { - Header = "Shortcut".ToLocalized(), - SubHeader = "AddDialogListShortcutSubHeader".ToLocalized(), + Header = Strings.Shortcut.ToLocalized(), + SubHeader = Strings.AddDialogListShortcutSubHeader.ToLocalized(), Glyph = "\uE71B", IsItemEnabled = true, ItemResult = new() diff --git a/src/Files.App/ViewModels/Dialogs/BaseDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/BaseDialogViewModel.cs index 5808e3d139da..15bb950cbf16 100644 --- a/src/Files.App/ViewModels/Dialogs/BaseDialogViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/BaseDialogViewModel.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Windows.Input; diff --git a/src/Files.App/ViewModels/Dialogs/BulkRenameDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/BulkRenameDialogViewModel.cs new file mode 100644 index 000000000000..339d0824ce73 --- /dev/null +++ b/src/Files.App/ViewModels/Dialogs/BulkRenameDialogViewModel.cs @@ -0,0 +1,63 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Windows.Storage; + +namespace Files.App.ViewModels.Dialogs +{ + public sealed partial class BulkRenameDialogViewModel : ObservableObject + { + private IContentPageContext context { get; } = Ioc.Default.GetRequiredService(); + + // Properties + + public bool IsNameValid => + FilesystemHelpers.IsValidForFilename(_FileName) && !_FileName.Contains('.'); + + public bool ShowNameWarning => + !string.IsNullOrEmpty(_FileName) && !IsNameValid; + + private string _FileName = string.Empty; + public string FileName + { + get => _FileName; + set + { + if (SetProperty(ref _FileName, value)) + { + OnPropertyChanged(nameof(IsNameValid)); + OnPropertyChanged(nameof(ShowNameWarning)); + } + } + } + + // Commands + + public IAsyncRelayCommand CommitRenameCommand { get; private set; } + + public BulkRenameDialogViewModel() + { + CommitRenameCommand = new AsyncRelayCommand(DoCommitRenameAsync); + } + + private async Task DoCommitRenameAsync() + { + if (context.ShellPage is null) + return; + + foreach (ListedItem item in context.SelectedItems) + { + var itemType = item.PrimaryItemAttribute == StorageItemTypes.Folder ? FilesystemItemType.Directory : FilesystemItemType.File; + await context.ShellPage.FilesystemHelpers.RenameAsync( + StorageHelpers.FromPathAndType(item.ItemPath, itemType), + FileName + item.FileExtension, + NameCollisionOption.GenerateUniqueName, + true, + false + ); + } + ; + } + + } +} \ No newline at end of file diff --git a/src/Files.App/ViewModels/Dialogs/CloneRepoDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/CloneRepoDialogViewModel.cs new file mode 100644 index 000000000000..e4298589ebd8 --- /dev/null +++ b/src/Files.App/ViewModels/Dialogs/CloneRepoDialogViewModel.cs @@ -0,0 +1,81 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using System.IO; +using System.Windows.Input; + +namespace Files.App.ViewModels.Dialogs +{ + public sealed partial class CloneRepoDialogViewModel : ObservableObject + { + public bool CanCloneRepo + { + get + { + var repoInfo = GitHelpers.GetRepoInfo(RepoUrl); + if (!string.IsNullOrEmpty(repoInfo.RepoUrl)) + { + RepoName = repoInfo.RepoName; + return true; + } + + return false; + } + } + + private string repoUrl = string.Empty; + public string RepoUrl + { + get => repoUrl; + set + { + if (SetProperty(ref repoUrl, value)) + OnPropertyChanged(nameof(CanCloneRepo)); + } + } + + private string repoName = string.Empty; + public string RepoName + { + get => repoName; + set => SetProperty(ref repoName, value); + + } + + private string workingDirectory = string.Empty; + public string WorkingDirectory + { + get => workingDirectory; + set => SetProperty(ref workingDirectory, value); + + } + + public ICommand CloneRepoCommand { get; private set; } + + public CloneRepoDialogViewModel(string repoUrl, string directory) + { + var repoInfo = GitHelpers.GetRepoInfo(repoUrl); + + if (!string.IsNullOrEmpty(repoInfo.RepoName)) + { + RepoUrl = repoInfo.RepoUrl; + RepoName = repoInfo.RepoName; + } + else + { + RepoUrl = string.Empty; + RepoName = string.Empty; + } + + WorkingDirectory = directory; + + CloneRepoCommand = new AsyncRelayCommand(DoCloneRepo); + } + + private async Task DoCloneRepo() + { + var targetDirectory = Path.Combine(workingDirectory, RepoName); + await GitHelpers.CloneRepoAsync(RepoUrl, repoName, targetDirectory); + } + } +} diff --git a/src/Files.App/ViewModels/Dialogs/CreateItemDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/CreateItemDialogViewModel.cs new file mode 100644 index 000000000000..15ceb3dc223b --- /dev/null +++ b/src/Files.App/ViewModels/Dialogs/CreateItemDialogViewModel.cs @@ -0,0 +1,15 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.ViewModels.Dialogs +{ + partial class CreateItemDialogViewModel : ObservableObject + { + private bool isNameInvalid; + public bool IsNameInvalid + { + get => isNameInvalid; + set => SetProperty(ref isNameInvalid, value); + } + } +} diff --git a/src/Files.App/ViewModels/Dialogs/CreateShortcutDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/CreateShortcutDialogViewModel.cs index 3ddfc8b8f7db..591a2c26529c 100644 --- a/src/Files.App/ViewModels/Dialogs/CreateShortcutDialogViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/CreateShortcutDialogViewModel.cs @@ -1,64 +1,188 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. +using Files.Shared.Helpers; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Windows.Input; -using Windows.Storage.Pickers; namespace Files.App.ViewModels.Dialogs { - public sealed class CreateShortcutDialogViewModel : ObservableObject + public sealed partial class CreateShortcutDialogViewModel : ObservableObject { // User's working directory public readonly string WorkingDirectory; // Placeholder text of destination path textbox - public readonly string DestinationPlaceholder = $@"{Environment.GetEnvironmentVariable("SystemDrive")}\Users\"; + public readonly string DestinationPlaceholder = $@"{Constants.UserEnvironmentPaths.SystemDrivePath}\Users\"; // Tells whether destination path exists public bool DestinationPathExists { get; set; } - // Tells wheteher the shortcut has been created + // Tells whether the shortcut has been created public bool ShortcutCreatedSuccessfully { get; private set; } // Shortcut name with extension public string ShortcutCompleteName { get; private set; } = string.Empty; - // Destination of the shortcut chosen by the user (can be a path or a URL) - private string _destinationItemPath; - public string DestinationItemPath + // Full path of the destination item + public string FullPath { get; private set; } + + // Arguments to be passed to the destination item if it's an executable + public string Arguments { get; private set; } + + // Previous path of the destination item + private string _previousShortcutTargetPath; + + private string _shortcutName; + public string ShortcutName { - get => _destinationItemPath; + get => _shortcutName; set { - if (!SetProperty(ref _destinationItemPath, value)) + if (SetProperty(ref _shortcutName, value)) + { + OnPropertyChanged(nameof(ShowNameWarningTip)); + OnPropertyChanged(nameof(IsShortcutValid)); + } + } + } + + // Destination of the shortcut chosen by the user (can be a path, a command or a URL) + private string _shortcutTarget; + public string ShortcutTarget + { + get => _shortcutTarget; + set + { + if (!SetProperty(ref _shortcutTarget, value)) return; OnPropertyChanged(nameof(ShowWarningTip)); - if (string.IsNullOrWhiteSpace(DestinationItemPath)) + if (string.IsNullOrWhiteSpace(ShortcutTarget)) { + DestinationPathExists = false; IsLocationValid = false; + _previousShortcutTargetPath = string.Empty; return; } - try { - DestinationPathExists = Path.Exists(DestinationItemPath) && DestinationItemPath != Path.GetPathRoot(DestinationItemPath); - if (DestinationPathExists) + var trimmed = ShortcutTarget.Trim(); + // If the text starts with '"', try to parse the quoted part as path, and the rest as arguments + if (trimmed.StartsWith('"')) { - IsLocationValid = true; + var endQuoteIndex = trimmed.IndexOf('"', 1); + if (endQuoteIndex == -1) + { + DestinationPathExists = false; + IsLocationValid = false; + _previousShortcutTargetPath = string.Empty; + return; + } + + var quoted = trimmed[1..endQuoteIndex]; + + if (quoted == _previousShortcutTargetPath) + { + Arguments = !Directory.Exists(FullPath) ? trimmed[(endQuoteIndex + 1)..] : string.Empty; + return; + } + + if (IsValidAbsolutePath(quoted)) + { + DestinationPathExists = true; + IsLocationValid = true; + FullPath = Path.GetFullPath(quoted); + Arguments = !Directory.Exists(FullPath) ? trimmed[(endQuoteIndex + 1)..] : string.Empty; + _previousShortcutTargetPath = quoted; + return; + } + + // If the quoted part is a valid filename, try to find it in the PATH + if (quoted == Path.GetFileName(quoted) + && quoted.IndexOfAny(Path.GetInvalidFileNameChars()) == -1 + && PathHelpers.TryGetFullPath(quoted, out var fullPath)) + { + DestinationPathExists = true; + IsLocationValid = true; + FullPath = fullPath; + Arguments = trimmed[(endQuoteIndex + 1)..]; + _previousShortcutTargetPath = quoted; + return; + } + + var uri = new Uri(quoted); + DestinationPathExists = false; + IsLocationValid = uri.IsWellFormedOriginalString(); + FullPath = quoted; + Arguments = string.Empty; + _previousShortcutTargetPath = string.Empty; } else { - var uri = new Uri(DestinationItemPath); + var filePath = trimmed.Split(' ')[0]; + + if (filePath == _previousShortcutTargetPath) + { + Arguments = !Directory.Exists(FullPath) ? trimmed.Split(' ')[1..].Aggregate(string.Empty, (current, arg) => current + arg + " ") : string.Empty; + return; + } + + if (IsValidAbsolutePath(filePath)) + { + DestinationPathExists = true; + IsLocationValid = true; + FullPath = Path.GetFullPath(filePath); + Arguments = !Directory.Exists(FullPath) ? trimmed.Split(' ')[1..].Aggregate(string.Empty, (current, arg) => current + arg + " ") : string.Empty; + _previousShortcutTargetPath = filePath; + return; + } + + // Try to parse the whole text as path + if (IsValidAbsolutePath(trimmed)) + { + DestinationPathExists = true; + IsLocationValid = true; + FullPath = Path.GetFullPath(trimmed); + Arguments = string.Empty; + _previousShortcutTargetPath = string.Empty; + return; + } + + if (filePath == Path.GetFileName(filePath) + && filePath.IndexOfAny(Path.GetInvalidFileNameChars()) == -1 + && PathHelpers.TryGetFullPath(filePath, out var fullPath)) + { + DestinationPathExists = true; + IsLocationValid = true; + FullPath = fullPath; + Arguments = trimmed.Split(' ')[1..].Aggregate(string.Empty, (current, arg) => current + arg + " "); + _previousShortcutTargetPath = filePath; + return; + } + + var uri = new Uri(trimmed); + DestinationPathExists = false; IsLocationValid = uri.IsWellFormedOriginalString(); + FullPath = trimmed; + Arguments = string.Empty; + _previousShortcutTargetPath = string.Empty; } + } catch (Exception) { + DestinationPathExists = false; IsLocationValid = false; + FullPath = string.Empty; + Arguments = string.Empty; + _previousShortcutTargetPath = string.Empty; + } + finally + { + AutoFillName(); } } } @@ -71,11 +195,18 @@ public bool IsLocationValid set { if (SetProperty(ref _isLocationValid, value)) + { OnPropertyChanged(nameof(ShowWarningTip)); + OnPropertyChanged(nameof(IsShortcutValid)); + } } } - public bool ShowWarningTip => !string.IsNullOrEmpty(DestinationItemPath) && !_isLocationValid; + public bool ShowWarningTip => !string.IsNullOrEmpty(ShortcutTarget) && !_isLocationValid; + + public bool ShowNameWarningTip => !string.IsNullOrEmpty(_shortcutTarget) && !FilesystemHelpers.IsValidForFilename(_shortcutName); + + public bool IsShortcutValid => _isLocationValid && !ShowNameWarningTip && !string.IsNullOrEmpty(_shortcutTarget); // Command invoked when the user clicks the 'Browse' button public ICommand SelectDestinationCommand { get; private set; } @@ -86,12 +217,17 @@ public bool IsLocationValid public CreateShortcutDialogViewModel(string workingDirectory) { WorkingDirectory = workingDirectory; - _destinationItemPath = string.Empty; + _shortcutTarget = string.Empty; SelectDestinationCommand = new AsyncRelayCommand(SelectDestination); PrimaryButtonCommand = new AsyncRelayCommand(CreateShortcutAsync); } + private bool IsValidAbsolutePath(string path) + { + return Path.Exists(path) && Path.IsPathFullyQualified(path) && path != Path.GetPathRoot(path); + } + private Task SelectDestination() { Win32PInvoke.BROWSEINFO bi = new Win32PInvoke.BROWSEINFO(); @@ -103,7 +239,7 @@ private Task SelectDestination() StringBuilder path = new StringBuilder(260); if (Win32PInvoke.SHGetPathFromIDList(pidl, path)) { - DestinationItemPath = path.ToString(); + ShortcutTarget = path.ToString(); } Marshal.FreeCoTaskMem(pidl); } @@ -113,29 +249,9 @@ private Task SelectDestination() private async Task CreateShortcutAsync() { - string? destinationName; var extension = DestinationPathExists ? ".lnk" : ".url"; - if (DestinationPathExists) - { - destinationName = Path.GetFileName(DestinationItemPath); - if (string.IsNullOrEmpty(destinationName)) - { - var destinationPath = DestinationItemPath.Replace('/', '\\'); - - if (destinationPath.EndsWith('\\')) - destinationPath = destinationPath.Substring(0, destinationPath.Length - 1); - - destinationName = destinationPath.Substring(destinationPath.LastIndexOf('\\') + 1); - } - } - else - { - var uri = new Uri(DestinationItemPath); - destinationName = uri.Host; - } - - var shortcutName = FilesystemHelpers.GetShortcutNamingPreference(destinationName); + var shortcutName = FilesystemHelpers.GetShortcutNamingPreference(_shortcutName); ShortcutCompleteName = shortcutName + extension; var filePath = Path.Combine(WorkingDirectory, ShortcutCompleteName); @@ -146,7 +262,36 @@ private async Task CreateShortcutAsync() filePath = Path.Combine(WorkingDirectory, ShortcutCompleteName); } - ShortcutCreatedSuccessfully = await FileOperationsHelpers.CreateOrUpdateLinkAsync(filePath, DestinationItemPath); + ShortcutCreatedSuccessfully = await FileOperationsHelpers.CreateOrUpdateLinkAsync(filePath, FullPath, Arguments); + } + + private void AutoFillName() + { + if (DestinationPathExists) + { + var destinationName = Path.GetFileName(FullPath); + if (DestinationPathExists) + { + destinationName = Path.GetFileName(FullPath); + + if (string.IsNullOrEmpty(FullPath)) + { + + var destinationPath = FullPath.Replace('/', '\\'); + + if (destinationPath.EndsWith('\\')) + destinationPath = destinationPath.Substring(0, destinationPath.Length - 1); + + destinationName = destinationPath.Substring(destinationPath.LastIndexOf('\\') + 1); + } + } + ShortcutName = destinationName; + } + else if (!string.IsNullOrEmpty(FullPath)) + { + var uri = new Uri(FullPath); + ShortcutName = uri.Host; + } } } } \ No newline at end of file diff --git a/src/Files.App/ViewModels/Dialogs/CredentialDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/CredentialDialogViewModel.cs index de1e7eeb206a..4613bcd1d746 100644 --- a/src/Files.App/ViewModels/Dialogs/CredentialDialogViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/CredentialDialogViewModel.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.ViewModels.Dialogs { - public sealed class CredentialDialogViewModel : ObservableObject + public sealed partial class CredentialDialogViewModel : ObservableObject { private string? _UserName; public string? UserName diff --git a/src/Files.App/ViewModels/Dialogs/DecompressArchiveDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/DecompressArchiveDialogViewModel.cs index 3ecf604e2e87..05045b2d2593 100644 --- a/src/Files.App/ViewModels/Dialogs/DecompressArchiveDialogViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/DecompressArchiveDialogViewModel.cs @@ -1,25 +1,66 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.IO; +using System.Text; using System.Windows.Input; using Windows.Storage; namespace Files.App.ViewModels.Dialogs { - public sealed class DecompressArchiveDialogViewModel : ObservableObject + public sealed partial class DecompressArchiveDialogViewModel : ObservableObject { + // Services private ICommonDialogService CommonDialogService { get; } = Ioc.Default.GetRequiredService(); + private readonly IUserSettingsService UserSettingsService = Ioc.Default.GetRequiredService(); + // Fields private readonly IStorageFile archive; + // Properties public BaseStorageFolder DestinationFolder { get; private set; } private string destinationFolderPath; public string DestinationFolderPath { get => destinationFolderPath; - private set => SetProperty(ref destinationFolderPath, value); + set + { + if (SetProperty(ref destinationFolderPath, value)) + { + OnPropertyChanged(nameof(IsDestinationPathValid)); + } + } + } + + public bool IsDestinationPathValid + { + get + { + try + { + if (string.IsNullOrWhiteSpace(DestinationFolderPath)) + return false; + + string parentDir = Path.GetDirectoryName(DestinationFolderPath); + string finalSegment = Path.GetFileName(DestinationFolderPath); + + // Check parent directory exists + if (string.IsNullOrWhiteSpace(parentDir) || !Directory.Exists(parentDir)) + return false; + + // Check for invalid characters (IsValidForFilename already does this) + if (!FilesystemHelpers.IsValidForFilename(finalSegment)) + return false; + + return true; + } + catch + { + // Catch any exception to prevent crashes + return false; + } + } } private bool openDestinationFolderOnCompletion; @@ -36,6 +77,24 @@ public bool IsArchiveEncrypted set => SetProperty(ref isArchiveEncrypted, value); } + private bool isArchiveEncodingUndetermined; + public bool IsArchiveEncodingUndetermined + { + get => isArchiveEncodingUndetermined; + set => SetProperty(ref isArchiveEncodingUndetermined, value); + } + + private Encoding? detectedEncoding; + public Encoding? DetectedEncoding + { + get => detectedEncoding; + set + { + SetProperty(ref detectedEncoding, value); + RefreshEncodingOptions(); + } + } + private bool showPathSelection; public bool ShowPathSelection { @@ -45,31 +104,81 @@ public bool ShowPathSelection public DisposableArray? Password { get; private set; } - public IRelayCommand PrimaryButtonClickCommand { get; private set; } + public EncodingItem[] EncodingOptions { get; set; } = EncodingItem.Defaults; + + public EncodingItem SelectedEncoding { get; set; } + + public ObservableCollection PreviousExtractionLocations { get; } = []; + // Commands + public IRelayCommand PrimaryButtonClickCommand { get; private set; } public ICommand SelectDestinationCommand { get; private set; } + public ICommand QuerySubmittedCommand { get; private set; } + // Constructor public DecompressArchiveDialogViewModel(IStorageFile archive) { this.archive = archive; destinationFolderPath = DefaultDestinationFolderPath(); + SelectedEncoding = EncodingOptions[0]; // Create commands SelectDestinationCommand = new AsyncRelayCommand(SelectDestinationAsync); PrimaryButtonClickCommand = new RelayCommand(password => Password = password); } + // Private Methods + private string DefaultDestinationFolderPath() + { + return Path.Combine(Path.GetDirectoryName(archive.Path), Path.GetFileNameWithoutExtension(archive.Path)); + } + private async Task SelectDestinationAsync() { - CommonDialogService.Open_FileOpenDialog(MainWindow.Instance.WindowHandle, true, [], Environment.SpecialFolder.Desktop, out var filePath); + bool result = CommonDialogService.Open_FileOpenDialog(MainWindow.Instance.WindowHandle, true, [], Environment.SpecialFolder.Desktop, out var filePath); + if (!result) + return; DestinationFolder = await StorageHelpers.ToStorageItem(filePath); DestinationFolderPath = (DestinationFolder is not null) ? DestinationFolder.Path : DefaultDestinationFolderPath(); } - private string DefaultDestinationFolderPath() + private void RefreshEncodingOptions() { - return Path.Combine(Path.GetDirectoryName(archive.Path), Path.GetFileNameWithoutExtension(archive.Path)); + if (detectedEncoding != null) + { + EncodingOptions = EncodingItem.Defaults + .Prepend(new EncodingItem( + detectedEncoding, + string.Format(Strings.EncodingDetected.GetLocalizedResource(), detectedEncoding.EncodingName) + )) + .ToArray(); + } + else + { + EncodingOptions = EncodingItem.Defaults; + } + SelectedEncoding = EncodingOptions.FirstOrDefault(); + } + + // Public Methods + public void UpdateSuggestions(string query) + { + var allItems = UserSettingsService.GeneralSettingsService.PreviousArchiveExtractionLocations; + if (allItems is null) + return; + + var filtered = allItems + .Where(item => item.StartsWith(query, StringComparison.OrdinalIgnoreCase)) + .ToList(); + + // Only update if results changed to prevent flickering + if (!filtered.SequenceEqual(PreviousExtractionLocations)) + { + PreviousExtractionLocations.Clear(); + foreach (var item in filtered) + PreviousExtractionLocations.Add(item); + } } } -} +} \ No newline at end of file diff --git a/src/Files.App/ViewModels/Dialogs/DynamicDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/DynamicDialogViewModel.cs index ec3c7cf49af7..be18888b0f82 100644 --- a/src/Files.App/ViewModels/Dialogs/DynamicDialogViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/DynamicDialogViewModel.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -9,7 +9,7 @@ namespace Files.App.ViewModels.Dialogs { - public sealed class DynamicDialogViewModel : ObservableObject, IDisposable + public sealed partial class DynamicDialogViewModel : ObservableObject, IDisposable { #region Public Properties diff --git a/src/Files.App/ViewModels/Dialogs/ElevateConfirmDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/ElevateConfirmDialogViewModel.cs index 6bda9586d11c..17813724a57c 100644 --- a/src/Files.App/ViewModels/Dialogs/ElevateConfirmDialogViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/ElevateConfirmDialogViewModel.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.ViewModels.Dialogs { - public sealed class ElevateConfirmDialogViewModel : ObservableObject + public sealed partial class ElevateConfirmDialogViewModel : ObservableObject { } } diff --git a/src/Files.App/ViewModels/Dialogs/FileSystemDialog/BaseFileSystemDialogItemViewModel.cs b/src/Files.App/ViewModels/Dialogs/FileSystemDialog/BaseFileSystemDialogItemViewModel.cs index d30d2b5cfd66..1c7f3abca38d 100644 --- a/src/Files.App/ViewModels/Dialogs/FileSystemDialog/BaseFileSystemDialogItemViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/FileSystemDialog/BaseFileSystemDialogItemViewModel.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.Shared.Utils; using System.IO; diff --git a/src/Files.App/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogConflictItemViewModel.cs b/src/Files.App/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogConflictItemViewModel.cs index fec4ba818baf..a73f44352f73 100644 --- a/src/Files.App/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogConflictItemViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogConflictItemViewModel.cs @@ -1,12 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Files.App.Data.Enums; using System.IO; namespace Files.App.ViewModels.Dialogs.FileSystemDialog { - public sealed class FileSystemDialogConflictItemViewModel : BaseFileSystemDialogItemViewModel, IFileSystemDialogConflictItemViewModel + public sealed partial class FileSystemDialogConflictItemViewModel : BaseFileSystemDialogItemViewModel, IFileSystemDialogConflictItemViewModel { private string? _DestinationDisplayName; public string? DestinationDisplayName diff --git a/src/Files.App/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogDefaultItemViewModel.cs b/src/Files.App/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogDefaultItemViewModel.cs index df0f9ebedcdf..1efe03ae0318 100644 --- a/src/Files.App/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogDefaultItemViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogDefaultItemViewModel.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.ViewModels.Dialogs.FileSystemDialog { - public sealed class FileSystemDialogDefaultItemViewModel : BaseFileSystemDialogItemViewModel + public sealed partial class FileSystemDialogDefaultItemViewModel : BaseFileSystemDialogItemViewModel { } } diff --git a/src/Files.App/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogViewModel.cs index c211b0a93b50..9de1dbedcf24 100644 --- a/src/Files.App/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogViewModel.cs @@ -1,13 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.App.Data.Enums; -using Files.Shared.Extensions; -using System.Text; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.ViewModels.Dialogs.FileSystemDialog { - public sealed class FileSystemDialogViewModel : BaseDialogViewModel, IRecipient + public sealed partial class FileSystemDialogViewModel : BaseDialogViewModel, IRecipient { private readonly IUserSettingsService _userSettingsService; @@ -149,14 +145,14 @@ public static FileSystemDialogViewModel GetDialogViewModel(FileSystemDialogMode if (dialogMode.ConflictsExist) { - titleText = "ConflictingItemsDialogTitle".GetLocalizedFormatResource(totalCount); + titleText = Strings.ConflictingItemsDialogTitle.GetLocalizedFormatResource(totalCount); descriptionText = nonConflictingItems.Count > 0 - ? "ConflictingItemsDialogSubtitleConflictsNonConflicts".GetLocalizedFormatResource(conflictingItems.Count, nonConflictingItems.Count) - : "ConflictingItemsDialogSubtitleConflicts".GetLocalizedFormatResource(conflictingItems.Count); + ? Strings.ConflictingItemsDialogSubtitleConflictsNonConflicts.GetLocalizedFormatResource(conflictingItems.Count, nonConflictingItems.Count) + : Strings.ConflictingItemsDialogSubtitleConflicts.GetLocalizedFormatResource(conflictingItems.Count); - primaryButtonText = "ConflictingItemsDialogPrimaryButtonText".ToLocalized(); - secondaryButtonText = "Cancel".ToLocalized(); + primaryButtonText = Strings.ConflictingItemsDialogPrimaryButtonText.ToLocalized(); + secondaryButtonText = Strings.Cancel.ToLocalized(); } else { @@ -164,33 +160,33 @@ public static FileSystemDialogViewModel GetDialogViewModel(FileSystemDialogMode { case FilesystemOperationType.Copy: { - titleText = "CopyItemsDialogTitle".GetLocalizedFormatResource(totalCount); + titleText = Strings.CopyItemsDialogTitle.GetLocalizedFormatResource(totalCount); - descriptionText = "CopyItemsDialogSubtitle".GetLocalizedFormatResource(totalCount); - primaryButtonText = "Copy".ToLocalized(); - secondaryButtonText = "Cancel".ToLocalized(); + descriptionText = Strings.CopyItemsDialogSubtitle.GetLocalizedFormatResource(totalCount); + primaryButtonText = Strings.Copy.ToLocalized(); + secondaryButtonText = Strings.Cancel.ToLocalized(); break; } case FilesystemOperationType.Move: { - titleText = "MoveItemsDialogTitle".GetLocalizedFormatResource(totalCount); + titleText = Strings.MoveItemsDialogTitle.GetLocalizedFormatResource(totalCount); - descriptionText = "MoveItemsDialogSubtitle".GetLocalizedFormatResource(totalCount); - primaryButtonText = "MoveItemsDialogPrimaryButtonText".ToLocalized(); - secondaryButtonText = "Cancel".ToLocalized(); + descriptionText = Strings.MoveItemsDialogSubtitle.GetLocalizedFormatResource(totalCount); + primaryButtonText = Strings.MoveItemsDialogPrimaryButtonText.ToLocalized(); + secondaryButtonText = Strings.Cancel.ToLocalized(); break; } case FilesystemOperationType.Delete: { - titleText = "DeleteItemsDialogTitle".GetLocalizedFormatResource(totalCount); + titleText = Strings.DeleteItemsDialogTitle.GetLocalizedFormatResource(totalCount); - descriptionText = "DeleteItemsDialogSubtitle".GetLocalizedFormatResource(totalCount); - primaryButtonText = "Delete".ToLocalized(); - secondaryButtonText = "Cancel".ToLocalized(); + descriptionText = Strings.DeleteItemsDialogSubtitle.GetLocalizedFormatResource(totalCount); + primaryButtonText = Strings.Delete.ToLocalized(); + secondaryButtonText = Strings.Cancel.ToLocalized(); isInDeleteMode = true; diff --git a/src/Files.App/ViewModels/Dialogs/FileSystemDialog/IFileSystemDialogConflictItemViewModel.cs b/src/Files.App/ViewModels/Dialogs/FileSystemDialog/IFileSystemDialogConflictItemViewModel.cs index e9506d6c4756..a03d3b339105 100644 --- a/src/Files.App/ViewModels/Dialogs/FileSystemDialog/IFileSystemDialogConflictItemViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/FileSystemDialog/IFileSystemDialogConflictItemViewModel.cs @@ -1,7 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.App.Data.Enums; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.ViewModels.Dialogs.FileSystemDialog { diff --git a/src/Files.App/ViewModels/Dialogs/FileTooLargeDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/FileTooLargeDialogViewModel.cs index 00d6eab283ea..5e2ae9669e54 100644 --- a/src/Files.App/ViewModels/Dialogs/FileTooLargeDialogViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/FileTooLargeDialogViewModel.cs @@ -1,14 +1,14 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.ViewModels.Dialogs { - public sealed class FileTooLargeDialogViewModel: ObservableObject + public sealed partial class FileTooLargeDialogViewModel : ObservableObject { public IEnumerable Paths { get; private set; } - public FileTooLargeDialogViewModel(IEnumerable paths) - { + public FileTooLargeDialogViewModel(IEnumerable paths) + { Paths = paths; } } diff --git a/src/Files.App/ViewModels/Dialogs/GitHubLoginDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/GitHubLoginDialogViewModel.cs index 787afc11f11d..ecf7619296e2 100644 --- a/src/Files.App/ViewModels/Dialogs/GitHubLoginDialogViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/GitHubLoginDialogViewModel.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Windows.Input; namespace Files.App.ViewModels.Dialogs { - public sealed class GitHubLoginDialogViewModel : ObservableObject + public sealed partial class GitHubLoginDialogViewModel : ObservableObject { private const string URL = "https://github.com/login/device"; diff --git a/src/Files.App/ViewModels/Dialogs/IDialog.cs b/src/Files.App/ViewModels/Dialogs/IDialog.cs index 2852cc543a26..ffeaafdf6dce 100644 --- a/src/Files.App/ViewModels/Dialogs/IDialog.cs +++ b/src/Files.App/ViewModels/Dialogs/IDialog.cs @@ -1,7 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.App.Data.Enums; +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.ViewModels.Dialogs { diff --git a/src/Files.App/ViewModels/Dialogs/ReleaseNotesDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/ReleaseNotesDialogViewModel.cs deleted file mode 100644 index 0d63596b9fd3..000000000000 --- a/src/Files.App/ViewModels/Dialogs/ReleaseNotesDialogViewModel.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -namespace Files.App.ViewModels.Dialogs -{ - public sealed class ReleaseNotesDialogViewModel : ObservableObject - { - private string _ReleaseNotesMadrkdown = string.Empty; - public string ReleaseNotesMadrkdown - { - get => _ReleaseNotesMadrkdown; - set => SetProperty(ref _ReleaseNotesMadrkdown, value); - } - - public ReleaseNotesDialogViewModel(string releaseNotesMadrkdown) - { - ReleaseNotesMadrkdown = releaseNotesMadrkdown; - } - } -} diff --git a/src/Files.App/ViewModels/Dialogs/RenameDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/RenameDialogViewModel.cs deleted file mode 100644 index a95864c5dda2..000000000000 --- a/src/Files.App/ViewModels/Dialogs/RenameDialogViewModel.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -namespace Files.App.ViewModels.Dialogs -{ - class RenameDialogViewModel : ObservableObject - { - private bool isNameInvalid; - public bool IsNameInvalid - { - get => isNameInvalid; - set => SetProperty(ref isNameInvalid, value); - } - } -} diff --git a/src/Files.App/ViewModels/Dialogs/ReorderSidebarItemsDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/ReorderSidebarItemsDialogViewModel.cs index d8df0b682af3..f4f90ffc4ba1 100644 --- a/src/Files.App/ViewModels/Dialogs/ReorderSidebarItemsDialogViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/ReorderSidebarItemsDialogViewModel.cs @@ -1,22 +1,22 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Windows.Input; namespace Files.App.ViewModels.Dialogs { - public sealed class ReorderSidebarItemsDialogViewModel : ObservableObject + public sealed partial class ReorderSidebarItemsDialogViewModel : ObservableObject { private readonly IQuickAccessService quickAccessService = Ioc.Default.GetRequiredService(); - public string HeaderText = "ReorderSidebarItemsDialogText".GetLocalizedResource(); + public string HeaderText = Strings.ReorderSidebarItemsDialogText.GetLocalizedResource(); public ICommand PrimaryButtonCommand { get; private set; } public ObservableCollection SidebarPinnedFolderItems = new(App.QuickAccessManager.Model._PinnedFolderItems .Where(x => x is LocationItem loc && loc.Section is SectionType.Pinned && !loc.IsHeader) .Cast()); - public ReorderSidebarItemsDialogViewModel() + public ReorderSidebarItemsDialogViewModel() { //App.Logger.LogWarning(string.Join(", ", SidebarPinnedFolderItems.Select(x => x.Path))); PrimaryButtonCommand = new RelayCommand(SaveChanges); diff --git a/src/Files.App/ViewModels/Dialogs/SettingsDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/SettingsDialogViewModel.cs index a058523c4401..ac9dc60a3b0c 100644 --- a/src/Files.App/ViewModels/Dialogs/SettingsDialogViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/SettingsDialogViewModel.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.ViewModels.Dialogs { - public sealed class SettingsDialogViewModel : ObservableObject + public sealed partial class SettingsDialogViewModel : ObservableObject { } } diff --git a/src/Files.App/ViewModels/HomeViewModel.cs b/src/Files.App/ViewModels/HomeViewModel.cs index 05bcde789625..48558d2f65d5 100644 --- a/src/Files.App/ViewModels/HomeViewModel.cs +++ b/src/Files.App/ViewModels/HomeViewModel.cs @@ -1,12 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using Microsoft.UI.Xaml; using System.Windows.Input; namespace Files.App.ViewModels { - public sealed class HomeViewModel : ObservableObject, IDisposable + public sealed partial class HomeViewModel : ObservableObject, IDisposable { // Dependency injections @@ -16,15 +15,89 @@ public sealed class HomeViewModel : ObservableObject, IDisposable public ObservableCollection WidgetItems { get; } = []; + public bool ShowQuickAccessWidget + { + get => UserSettingsService.GeneralSettingsService.ShowQuickAccessWidget; + set + { + if (value != UserSettingsService.GeneralSettingsService.ShowQuickAccessWidget) + UserSettingsService.GeneralSettingsService.ShowQuickAccessWidget = value; + } + } + + public bool ShowDrivesWidget + { + get => UserSettingsService.GeneralSettingsService.ShowDrivesWidget; + set + { + if (value != UserSettingsService.GeneralSettingsService.ShowDrivesWidget) + UserSettingsService.GeneralSettingsService.ShowDrivesWidget = value; + } + } + + public bool ShowNetworkLocationsWidget + { + get => UserSettingsService.GeneralSettingsService.ShowNetworkLocationsWidget; + set + { + if (value != UserSettingsService.GeneralSettingsService.ShowNetworkLocationsWidget) + UserSettingsService.GeneralSettingsService.ShowNetworkLocationsWidget = value; + } + } + + public bool ShowFileTagsWidget + { + get => UserSettingsService.GeneralSettingsService.ShowFileTagsWidget; + set + { + if (value != UserSettingsService.GeneralSettingsService.ShowFileTagsWidget) + UserSettingsService.GeneralSettingsService.ShowFileTagsWidget = value; + } + } + + public bool ShowRecentFilesWidget + { + get => UserSettingsService.GeneralSettingsService.ShowRecentFilesWidget; + set + { + if (value != UserSettingsService.GeneralSettingsService.ShowRecentFilesWidget) + UserSettingsService.GeneralSettingsService.ShowRecentFilesWidget = value; + } + } + // Commands - public ICommand HomePageLoadedCommand { get; } + public ICommand ReloadWidgetsCommand { get; } // Constructor public HomeViewModel() { - HomePageLoadedCommand = new RelayCommand(ExecuteHomePageLoadedCommand); + ReloadWidgetsCommand = new AsyncRelayCommand(ExecuteReloadWidgetsCommand); + + UserSettingsService.GeneralSettingsService.PropertyChanged += GeneralSettingsService_PropertyChanged; + } + + private void GeneralSettingsService_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + switch (e.PropertyName) + { + case nameof(IGeneralSettingsService.ShowQuickAccessWidget): + OnPropertyChanged(nameof(ShowQuickAccessWidget)); + break; + case nameof(IGeneralSettingsService.ShowDrivesWidget): + OnPropertyChanged(nameof(ShowDrivesWidget)); + break; + case nameof(IGeneralSettingsService.ShowNetworkLocationsWidget): + OnPropertyChanged(nameof(ShowNetworkLocationsWidget)); + break; + case nameof(IGeneralSettingsService.ShowFileTagsWidget): + OnPropertyChanged(nameof(ShowFileTagsWidget)); + break; + case nameof(IGeneralSettingsService.ShowRecentFilesWidget): + OnPropertyChanged(nameof(ShowRecentFilesWidget)); + break; + } } // Methods @@ -106,7 +179,7 @@ private void ReloadWidgets() public void RefreshWidgetList() { - for (int i = 0; i < WidgetItems.Count; i++) + for (int i = WidgetItems.Count - 1; i >= 0; i--) { if (!WidgetItems[i].WidgetItemModel.IsWidgetSettingEnabled) RemoveWidgetAt(i); @@ -115,6 +188,15 @@ public void RefreshWidgetList() ReloadWidgets(); } + public async Task RefreshWidgetProperties() + { + await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(async () => + { + foreach (var viewModel in WidgetItems.Select(x => x.WidgetItemModel).ToList()) + await viewModel.RefreshWidgetAsync(); + }); + } + private bool InsertWidget(WidgetContainerItem widgetModel, int atIndex) { // The widget must not be null and must implement IWidgetItemModel @@ -131,11 +213,17 @@ private bool InsertWidget(WidgetContainerItem widgetModel, int atIndex) if (atIndex > WidgetItems.Count) { - WidgetItems.Add(widgetModel); + MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => + { + WidgetItems.Add(widgetModel); + }); } else { - WidgetItems.Insert(atIndex, widgetModel); + MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => + { + WidgetItems.Insert(atIndex, widgetModel); + }); } return true; @@ -143,7 +231,10 @@ private bool InsertWidget(WidgetContainerItem widgetModel, int atIndex) public bool CanAddWidget(string widgetName) { - return !(WidgetItems.Any((item) => item.WidgetItemModel.WidgetName == widgetName)); + return MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => + { + return !(WidgetItems.Any((item) => item.WidgetItemModel.WidgetName == widgetName)); + }).GetAwaiter().GetResult(); } private void RemoveWidgetAt(int index) @@ -153,42 +244,58 @@ private void RemoveWidgetAt(int index) return; } - WidgetItems[index].Dispose(); - WidgetItems.RemoveAt(index); + MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => + { + WidgetItems[index].Dispose(); + WidgetItems.RemoveAt(index); + }); } public void RemoveWidget() where TWidget : IWidgetViewModel { - int indexToRemove = -1; - - for (int i = 0; i < WidgetItems.Count; i++) + MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => { - if (typeof(TWidget).IsAssignableFrom(WidgetItems[i].WidgetControl.GetType())) + int indexToRemove = -1; + + for (int i = 0; i < WidgetItems.Count; i++) { - // Found matching types - indexToRemove = i; - break; + if (typeof(TWidget).IsAssignableFrom(WidgetItems[i].WidgetControl.GetType())) + { + // Found matching types + indexToRemove = i; + break; + } } - } - RemoveWidgetAt(indexToRemove); + if (indexToRemove >= 0) + { + WidgetItems[indexToRemove].Dispose(); + WidgetItems.RemoveAt(indexToRemove); + } + }); } // Command methods - private void ExecuteHomePageLoadedCommand(RoutedEventArgs? e) + private async Task ExecuteReloadWidgetsCommand() { ReloadWidgets(); + await RefreshWidgetProperties(); } // Disposer public void Dispose() { - for (int i = 0; i < WidgetItems.Count; i++) - WidgetItems[i].Dispose(); + UserSettingsService.GeneralSettingsService.PropertyChanged -= GeneralSettingsService_PropertyChanged; + + MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => + { + for (int i = 0; i < WidgetItems.Count; i++) + WidgetItems[i].Dispose(); - WidgetItems.Clear(); + WidgetItems.Clear(); + }); } } } diff --git a/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs b/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs index 927caece1572..a670e781927b 100644 --- a/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs +++ b/src/Files.App/ViewModels/Layouts/BaseLayoutViewModel.cs @@ -1,9 +1,12 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. +using Microsoft.Extensions.Logging; using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; using System.IO; +using System.Runtime.InteropServices; using System.Windows.Input; using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer.DragDrop; @@ -15,9 +18,10 @@ namespace Files.App.ViewModels.Layouts /// /// Represents ViewModel for . /// - public sealed class BaseLayoutViewModel : IDisposable + public sealed partial class BaseLayoutViewModel : IDisposable { - protected ICommandManager Commands { get; } = Ioc.Default.GetRequiredService(); + private ICommandManager Commands { get; } = Ioc.Default.GetRequiredService(); + private ILogger? Logger { get; } = Ioc.Default.GetRequiredService>(); private readonly IShellPage _associatedInstance; @@ -47,7 +51,7 @@ public BaseLayoutViewModel(IShellPage associatedInstance, ItemManipulationModel private void CreateNewFile(ShellNewEntry f) { - UIFilesystemHelpers.CreateFileFromDialogResultTypeAsync(AddItemDialogItemType.File, f, _associatedInstance); + _ = UIFilesystemHelpers.CreateFileFromDialogResultTypeAsync(AddItemDialogItemType.File, f, _associatedInstance); } private async Task ItemPointerPressedAsync(PointerRoutedEventArgs e) @@ -61,9 +65,9 @@ private async Task ItemPointerPressedAsync(PointerRoutedEventArgs e) _associatedInstance.SlimContentPage.IsMiddleClickToScrollEnabled = true; if (Item.IsShortcut) - await NavigationHelpers.OpenPathInNewTab(((e.OriginalSource as FrameworkElement)?.DataContext as ShortcutItem)?.TargetPath ?? Item.ItemPath, false); + await NavigationHelpers.OpenPathInNewTab(((e.OriginalSource as FrameworkElement)?.DataContext as IShortcutItem)?.TargetPath ?? Item.ItemPath); else - await NavigationHelpers.OpenPathInNewTab(Item.ItemPath, false); + await NavigationHelpers.OpenPathInNewTab(Item.ItemPath); } } @@ -76,10 +80,10 @@ private void PointerWheelChanged(PointerRoutedEventArgs e) // Mouse wheel down if (delta < 0) - Commands.LayoutIncreaseSize.ExecuteAsync(); + Commands.LayoutDecreaseSize.ExecuteAsync(); // Mouse wheel up else if (delta > 0) - Commands.LayoutDecreaseSize.ExecuteAsync(); + Commands.LayoutIncreaseSize.ExecuteAsync(); e.Handled = true; } @@ -97,16 +101,34 @@ public async Task DragOverAsync(DragEventArgs e) return; } - _itemManipulationModel.ClearSelection(); + // Check if ShellViewModel is initialized to prevent race condition + if (_associatedInstance.ShellViewModel is null) + { + e.AcceptedOperation = DataPackageOperation.None; + deferral.Complete(); + return; + } if (FilesystemHelpers.HasDraggedStorageItems(e.DataView)) { e.Handled = true; - var draggedItems = await FilesystemHelpers.GetDraggedStorageItems(e.DataView); + var workingDirectory = _associatedInstance.ShellViewModel.WorkingDirectory.TrimPath(); + var folderName = Path.IsPathRooted(workingDirectory) && Path.GetPathRoot(workingDirectory) == workingDirectory ? Path.GetPathRoot(workingDirectory) : Path.GetFileName(workingDirectory); - var pwd = _associatedInstance.ShellViewModel.WorkingDirectory.TrimPath(); - var folderName = Path.IsPathRooted(pwd) && Path.GetPathRoot(pwd) == pwd ? Path.GetPathRoot(pwd) : Path.GetFileName(pwd); + if (e.DataView.Contains(StandardDataFormats.Uri) && await e.DataView.GetUriAsync() is { } uri) + { + if (GitHelpers.IsValidRepoUrl(uri.ToString())) + { + e.DragUIOverride.IsCaptionVisible = true; + e.DragUIOverride.Caption = string.Format(Strings.CloneToFolderCaptionText.GetLocalizedResource(), folderName); + e.AcceptedOperation = DataPackageOperation.Copy; + deferral.Complete(); + return; + } + } + + var draggedItems = await FilesystemHelpers.GetDraggedStorageItems(e.DataView); // As long as one file doesn't already belong to this folder if (_associatedInstance.InstanceViewModel.IsPageTypeSearchResults || draggedItems.Any() && draggedItems.AreItemsAlreadyInFolder(_associatedInstance.ShellViewModel.WorkingDirectory)) @@ -119,47 +141,61 @@ public async Task DragOverAsync(DragEventArgs e) } else { - e.DragUIOverride.IsCaptionVisible = true; - if (pwd.StartsWith(Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.Ordinal)) - { - e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalizedResource(), folderName); - // Some applications such as Edge can't raise the drop event by the Move flag (#14008), so we set the Copy flag as well. - e.AcceptedOperation = DataPackageOperation.Move | DataPackageOperation.Copy; - } - else if (e.Modifiers.HasFlag(DragDropModifiers.Alt) || e.Modifiers.HasFlag(DragDropModifiers.Control | DragDropModifiers.Shift)) - { - e.DragUIOverride.Caption = string.Format("LinkToFolderCaptionText".GetLocalizedResource(), folderName); - e.AcceptedOperation = DataPackageOperation.Link; - } - else if (e.Modifiers.HasFlag(DragDropModifiers.Control)) + try { - e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), folderName); - e.AcceptedOperation = DataPackageOperation.Copy; - } - else if (e.Modifiers.HasFlag(DragDropModifiers.Shift)) - { - e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalizedResource(), folderName); - // Some applications such as Edge can't raise the drop event by the Move flag (#14008), so we set the Copy flag as well. - e.AcceptedOperation = DataPackageOperation.Move | DataPackageOperation.Copy; - } - else if (draggedItems.Any(x => - x.Item is ZipStorageFile || - x.Item is ZipStorageFolder) || - ZipStorageFolder.IsZipPath(pwd)) - { - e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), folderName); - e.AcceptedOperation = DataPackageOperation.Copy; - } - else if (draggedItems.AreItemsInSameDrive(_associatedInstance.ShellViewModel.WorkingDirectory)) - { - e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalizedResource(), folderName); - // Some applications such as Edge can't raise the drop event by the Move flag (#14008), so we set the Copy flag as well. - e.AcceptedOperation = DataPackageOperation.Move | DataPackageOperation.Copy; + e.DragUIOverride.IsCaptionVisible = true; + if (e.DataView.Properties.TryGetValue("Files_ActionBinder", out var actionBinder) && actionBinder is "Files_ShelfBinder") + { + e.DragUIOverride.Caption = string.Format(Strings.LinkToFolderCaptionText.GetLocalizedResource(), folderName); + e.AcceptedOperation = DataPackageOperation.Link; + } + else if (workingDirectory.StartsWith(Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.Ordinal)) + { + e.DragUIOverride.Caption = string.Format(Strings.MoveToFolderCaptionText.GetLocalizedResource(), folderName); + // Some applications such as Edge can't raise the drop event by the Move flag (#14008), so we set the Copy flag as well. + e.AcceptedOperation = DataPackageOperation.Move | DataPackageOperation.Copy; + } + else if (e.Modifiers.HasFlag(DragDropModifiers.Alt) || e.Modifiers.HasFlag(DragDropModifiers.Control | DragDropModifiers.Shift)) + { + e.DragUIOverride.Caption = string.Format(Strings.LinkToFolderCaptionText.GetLocalizedResource(), folderName); + e.AcceptedOperation = DataPackageOperation.Link; + } + else if (e.Modifiers.HasFlag(DragDropModifiers.Control)) + { + e.DragUIOverride.Caption = string.Format(Strings.CopyToFolderCaptionText.GetLocalizedResource(), folderName); + e.AcceptedOperation = DataPackageOperation.Copy; + } + else if (e.Modifiers.HasFlag(DragDropModifiers.Shift)) + { + e.DragUIOverride.Caption = string.Format(Strings.MoveToFolderCaptionText.GetLocalizedResource(), folderName); + // Some applications such as Edge can't raise the drop event by the Move flag (#14008), so we set the Copy flag as well. + e.AcceptedOperation = DataPackageOperation.Move | DataPackageOperation.Copy; + } + else if (draggedItems.Any(x => + x.Item is ZipStorageFile || + x.Item is ZipStorageFolder) || + ZipStorageFolder.IsZipPath(workingDirectory)) + { + e.DragUIOverride.Caption = string.Format(Strings.CopyToFolderCaptionText.GetLocalizedResource(), folderName); + e.AcceptedOperation = DataPackageOperation.Copy; + } + else if (draggedItems.AreItemsInSameDrive(_associatedInstance.ShellViewModel.WorkingDirectory)) + { + e.DragUIOverride.Caption = string.Format(Strings.MoveToFolderCaptionText.GetLocalizedResource(), folderName); + // Some applications such as Edge can't raise the drop event by the Move flag (#14008), so we set the Copy flag as well. + e.AcceptedOperation = DataPackageOperation.Move | DataPackageOperation.Copy; + } + else + { + e.DragUIOverride.Caption = string.Format(Strings.CopyToFolderCaptionText.GetLocalizedResource(), folderName); + e.AcceptedOperation = DataPackageOperation.Copy; + } + + _itemManipulationModel.ClearSelection(); } - else + catch (COMException ex) when (ex.Message.Contains("RPC server is unavailable")) { - e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), folderName); - e.AcceptedOperation = DataPackageOperation.Copy; + Logger?.LogDebug(ex, ex.Message); } } } @@ -170,25 +206,62 @@ x.Item is ZipStorageFile || public async Task DropAsync(DragEventArgs e) { e.Handled = true; + if (e.DataView.Contains(StandardDataFormats.Uri) && await e.DataView.GetUriAsync() is { } uri) + { + if (GitHelpers.IsValidRepoUrl(uri.ToString())) + { + Commands.GitClone.Execute(uri.ToString()); + return; + } + } - if (FilesystemHelpers.HasDraggedStorageItems(e.DataView)) + // Check if ShellViewModel is initialized to prevent race condition + if (_associatedInstance.ShellViewModel is null) + { + e.AcceptedOperation = DataPackageOperation.None; + return; + } + + var deferral = e.GetDeferral(); + try { - var deferral = e.GetDeferral(); + if (!FilesystemHelpers.HasDraggedStorageItems(e.DataView)) + return; - try + if (e.DataView.Properties.TryGetValue("Files_ActionBinder", out var actionBinder) && actionBinder is "Files_ShelfBinder") { - await _associatedInstance.FilesystemHelpers.PerformOperationTypeAsync(e.AcceptedOperation, e.DataView, _associatedInstance.ShellViewModel.WorkingDirectory, false, true); - await _associatedInstance.RefreshIfNoWatcherExistsAsync(); + if (e.OriginalSource is not UIElement uiElement) + return; + + var pwd = _associatedInstance.ShellViewModel.WorkingDirectory.TrimPath(); + var folderName = Path.IsPathRooted(pwd) && Path.GetPathRoot(pwd) == pwd ? Path.GetPathRoot(pwd) : Path.GetFileName(pwd); + var menuFlyout = new MenuFlyout() + { + Items = + { + new MenuFlyoutItem() { Text = string.Format(Strings.CopyToFolderCaptionText.GetLocalizedResource(), folderName), Command = new AsyncRelayCommand(async ct => + await _associatedInstance.FilesystemHelpers.PerformOperationTypeAsync(DataPackageOperation.Copy, e.DataView, _associatedInstance.ShellViewModel.WorkingDirectory, false, true)) }, + new MenuFlyoutItem() { Text = string.Format(Strings.MoveToFolderCaptionText.GetLocalizedResource(), folderName), Command = new AsyncRelayCommand(async ct => + await _associatedInstance.FilesystemHelpers.PerformOperationTypeAsync(DataPackageOperation.Move, e.DataView, _associatedInstance.ShellViewModel.WorkingDirectory, false, true)) } + } + }; + + menuFlyout.ShowAt(uiElement, e.GetPosition(uiElement)); } - finally + else { - deferral.Complete(); + await _associatedInstance.FilesystemHelpers.PerformOperationTypeAsync(e.AcceptedOperation, e.DataView, _associatedInstance.ShellViewModel.WorkingDirectory, false, true); + await _associatedInstance.RefreshIfNoWatcherExistsAsync(); } } + finally + { + deferral.Complete(); + } } public void Dispose() { } } -} +} \ No newline at end of file diff --git a/src/Files.App/ViewModels/MainPageViewModel.cs b/src/Files.App/ViewModels/MainPageViewModel.cs index c8d66ea19573..589f25fce816 100644 --- a/src/Files.App/ViewModels/MainPageViewModel.cs +++ b/src/Files.App/ViewModels/MainPageViewModel.cs @@ -1,29 +1,35 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Media.Imaging; using Microsoft.UI.Xaml.Navigation; using System.Windows.Input; +using Windows.Services.Store; using Windows.System; -using Microsoft.UI.Xaml.Controls; +using WinRT.Interop; namespace Files.App.ViewModels { /// /// Represents ViewModel of . /// - public sealed class MainPageViewModel : ObservableObject + public sealed partial class MainPageViewModel : ObservableObject { // Dependency injections private IAppearanceSettingsService AppearanceSettingsService { get; } = Ioc.Default.GetRequiredService(); + private IGeneralSettingsService GeneralSettingsService { get; } = Ioc.Default.GetRequiredService(); private INetworkService NetworkService { get; } = Ioc.Default.GetRequiredService(); private IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetRequiredService(); private IResourcesService ResourcesService { get; } = Ioc.Default.GetRequiredService(); private DrivesViewModel DrivesViewModel { get; } = Ioc.Default.GetRequiredService(); + public ShelfViewModel ShelfViewModel { get; } = Ioc.Default.GetRequiredService(); + + private readonly IContentPageContext context = Ioc.Default.GetRequiredService(); // Properties @@ -33,6 +39,20 @@ public sealed class MainPageViewModel : ObservableObject public ITabBar? MultitaskingControl { get; set; } + private bool _IsSidebarPaneOpen; + public bool IsSidebarPaneOpen + { + get => _IsSidebarPaneOpen; + set => SetProperty(ref _IsSidebarPaneOpen, value); + } + + private bool _IsSidebarPaneOpenToggleButtonVisible; + public bool IsSidebarPaneOpenToggleButtonVisible + { + get => _IsSidebarPaneOpenToggleButtonVisible; + set => SetProperty(ref _IsSidebarPaneOpenToggleButtonVisible, value); + } + private TabBarItem? selectedTabItem; public TabBarItem? SelectedTabItem { @@ -61,16 +81,36 @@ public bool ShouldPreviewPaneBeDisplayed set => SetProperty(ref shouldPreviewPaneBeDisplayed, value); } + public bool ShowShelfPane + => GeneralSettingsService.ShowShelfPane && AppLifecycleHelper.AppEnvironment is AppEnvironment.Dev; + public Stretch AppThemeBackgroundImageFit => AppearanceSettingsService.AppThemeBackgroundImageFit; public float AppThemeBackgroundImageOpacity => AppearanceSettingsService.AppThemeBackgroundImageOpacity; - public ImageSource? AppThemeBackgroundImageSource => - string.IsNullOrEmpty(AppearanceSettingsService.AppThemeBackgroundImageSource) - ? null - : new BitmapImage(new Uri(AppearanceSettingsService.AppThemeBackgroundImageSource, UriKind.RelativeOrAbsolute)); + public ImageSource? AppThemeBackgroundImageSource + { + get + { + if (string.IsNullOrWhiteSpace(AppearanceSettingsService.AppThemeBackgroundImageSource)) + return null; + + if (!Uri.TryCreate(AppearanceSettingsService.AppThemeBackgroundImageSource, UriKind.RelativeOrAbsolute, out Uri? validUri)) + return null; + + try + { + return new BitmapImage(validUri); + } + catch (Exception) + { + // Catch potential errors + return null; + } + } + } public VerticalAlignment AppThemeBackgroundImageVerticalAlignment => AppearanceSettingsService.AppThemeBackgroundImageVerticalAlignment; @@ -78,19 +118,59 @@ public VerticalAlignment AppThemeBackgroundImageVerticalAlignment public HorizontalAlignment AppThemeBackgroundImageHorizontalAlignment => AppearanceSettingsService.AppThemeBackgroundImageHorizontalAlignment; - public bool ShowToolbar - => AppearanceSettingsService.ShowToolbar; + public bool ShowToolbar => + AppearanceSettingsService.ShowToolbar && + context.PageType is not ContentPageTypes.Home && + context.PageType is not ContentPageTypes.ReleaseNotes && + context.PageType is not ContentPageTypes.Settings; + + public bool ShowStatusBar => + AppearanceSettingsService.ShowStatusBar && + context.PageType is not ContentPageTypes.Home && + context.PageType is not ContentPageTypes.ReleaseNotes && + context.PageType is not ContentPageTypes.Settings; + + public bool ShowReviewPrompt + { + get + { + var isTargetEnvironment = AppLifecycleHelper.AppEnvironment is AppEnvironment.StoreStable or AppEnvironment.StorePreview; + var hasClickedReviewPrompt = UserSettingsService.ApplicationSettingsService.HasClickedReviewPrompt; + var launchCountReached = AppLifecycleHelper.TotalLaunchCount == 30; + + return isTargetEnvironment && !hasClickedReviewPrompt && launchCountReached; + } + } + + public bool ShowSponsorPrompt + { + get + { + var isTargetEnvironment = AppLifecycleHelper.AppEnvironment is AppEnvironment.Dev or AppEnvironment.SideloadStable or AppEnvironment.SideloadPreview; + var hasClickedSponsorPrompt = UserSettingsService.ApplicationSettingsService.HasClickedSponsorPrompt; + var launchCountReached = AppLifecycleHelper.TotalLaunchCount == 30; + return isTargetEnvironment && !hasClickedSponsorPrompt && launchCountReached; + } + } // Commands public ICommand NavigateToNumberedTabKeyboardAcceleratorCommand { get; } + public ICommand ReviewAppCommand { get; } + public ICommand DismissReviewPromptCommand { get; } + public ICommand SponsorCommand { get; } + public ICommand DismissSponsorPromptCommand { get; } // Constructor public MainPageViewModel() { NavigateToNumberedTabKeyboardAcceleratorCommand = new RelayCommand(ExecuteNavigateToNumberedTabKeyboardAcceleratorCommand); + ReviewAppCommand = new RelayCommand(ExecuteReviewAppCommand); + DismissReviewPromptCommand = new RelayCommand(ExecuteDismissReviewPromptCommand); + SponsorCommand = new RelayCommand(ExecuteSponsorCommand); + DismissSponsorPromptCommand = new RelayCommand(ExecuteDismissSponsorPromptCommand); AppearanceSettingsService.PropertyChanged += (s, e) => { @@ -114,6 +194,30 @@ public MainPageViewModel() case nameof(AppearanceSettingsService.ShowToolbar): OnPropertyChanged(nameof(ShowToolbar)); break; + case nameof(AppearanceSettingsService.ShowStatusBar): + OnPropertyChanged(nameof(ShowStatusBar)); + break; + } + }; + + context.PropertyChanged += (s, e) => + { + switch (e.PropertyName) + { + case nameof(context.PageType): + OnPropertyChanged(nameof(ShowToolbar)); + OnPropertyChanged(nameof(ShowStatusBar)); + break; + } + }; + + GeneralSettingsService.PropertyChanged += (s, e) => + { + switch (e.PropertyName) + { + case nameof(GeneralSettingsService.ShowShelfPane): + OnPropertyChanged(nameof(ShowShelfPane)); + break; } }; } @@ -140,9 +244,9 @@ public async Task OnNavigatedToAsync(NavigationEventArgs e) // add last session tabs to closed tabs stack if those tabs are not about to be opened if (!UserSettingsService.AppSettingsService.RestoreTabsOnStartup && !UserSettingsService.GeneralSettingsService.ContinueLastSessionOnStartUp && UserSettingsService.GeneralSettingsService.LastSessionTabList != null) { - var items = new TabBarItemParameter[UserSettingsService.GeneralSettingsService.LastSessionTabList.Count]; - for (int i = 0; i < items.Length; i++) - items[i] = TabBarItemParameter.Deserialize(UserSettingsService.GeneralSettingsService.LastSessionTabList[i]); + var items = UserSettingsService.GeneralSettingsService.LastSessionTabList + .Where(tab => !string.IsNullOrEmpty(tab)) + .Select(tab => TabBarItemParameter.Deserialize(tab)).ToArray(); BaseTabBar.PushRecentTab(items); } @@ -235,6 +339,37 @@ await Task.WhenAll( // Command methods + private async void ExecuteReviewAppCommand() + { + UserSettingsService.ApplicationSettingsService.HasClickedReviewPrompt = true; + OnPropertyChanged(nameof(ShowReviewPrompt)); + + try + { + var storeContext = StoreContext.GetDefault(); + InitializeWithWindow.Initialize(storeContext, MainWindow.Instance.WindowHandle); + await storeContext.RequestRateAndReviewAppAsync(); + } + catch (Exception) { } + } + + private void ExecuteDismissReviewPromptCommand() + { + UserSettingsService.ApplicationSettingsService.HasClickedReviewPrompt = true; + } + + private async void ExecuteSponsorCommand() + { + UserSettingsService.ApplicationSettingsService.HasClickedSponsorPrompt = true; + OnPropertyChanged(nameof(ShowSponsorPrompt)); + await Launcher.LaunchUriAsync(new Uri(Constants.ExternalUrl.SupportUsUrl)).AsTask(); + } + + private void ExecuteDismissSponsorPromptCommand() + { + UserSettingsService.ApplicationSettingsService.HasClickedSponsorPrompt = true; + } + private async void ExecuteNavigateToNumberedTabKeyboardAcceleratorCommand(KeyboardAcceleratorInvokedEventArgs? e) { var indexToSelect = e!.KeyboardAccelerator.Key switch @@ -259,8 +394,8 @@ private async void ExecuteNavigateToNumberedTabKeyboardAcceleratorCommand(Keyboa // Small delay for the UI to load await Task.Delay(500); - // Refocus on the file list - (SelectedTabItem?.TabItemContent as Control)?.Focus(FocusState.Programmatic); + // Focus the content of the selected tab item (needed for keyboard navigation) + context.ShellPage!.PaneHolder.FocusActivePane(); } e.Handled = true; diff --git a/src/Files.App/ViewModels/Properties/BasePropertiesPage.cs b/src/Files.App/ViewModels/Properties/BasePropertiesPage.cs index ab81ba6ece79..6b62a3f40348 100644 --- a/src/Files.App/ViewModels/Properties/BasePropertiesPage.cs +++ b/src/Files.App/ViewModels/Properties/BasePropertiesPage.cs @@ -1,5 +1,5 @@ -// Copyright(c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright(c) Files Community +// Licensed under the MIT License. using Files.Shared.Helpers; using Microsoft.UI.Xaml; @@ -39,8 +39,8 @@ protected override void OnNavigatedTo(NavigationEventArgs e) var props = new DriveProperties(ViewModel, drive, AppInstance); BaseProperties = props; - ViewModel.CleanupVisibility = props.Drive.Type != DriveType.Network; - ViewModel.FormatVisibility = !(props.Drive.Type == DriveType.Network || string.Equals(props.Drive.Path, $@"{Environment.GetEnvironmentVariable("SystemDrive")}\", StringComparison.OrdinalIgnoreCase)); + ViewModel.CleanupVisibility = props.Drive.Type != DriveType.Network && props.Drive.Type != DriveType.CloudDrive; + ViewModel.FormatVisibility = !(props.Drive.Type == DriveType.Network || props.Drive.Type == DriveType.CloudDrive || string.Equals(props.Drive.Path, $@"{Constants.UserEnvironmentPaths.SystemDrivePath}\", StringComparison.OrdinalIgnoreCase)); ViewModel.CleanupDriveCommand = new AsyncRelayCommand(() => StorageSenseHelper.OpenStorageSenseAsync(props.Drive.Path)); ViewModel.FormatDriveCommand = new RelayCommand(async () => { @@ -86,7 +86,8 @@ protected override void OnNavigatedTo(NavigationEventArgs e) string[] extensions = [ - "BitmapFiles".GetLocalizedResource(), "*.bmp", + Strings.ImageFiles.GetLocalizedResource(), "*.bmp;*.jpg;*.jpeg;*.png", + Strings.BitmapFiles.GetLocalizedResource(), "*.bmp", "JPEG", "*.jpg;*.jpeg", "PNG", "*.png", ]; diff --git a/src/Files.App/ViewModels/Properties/CompatibilityViewModel.cs b/src/Files.App/ViewModels/Properties/CompatibilityViewModel.cs index c5ed13885e91..41935ce228d8 100644 --- a/src/Files.App/ViewModels/Properties/CompatibilityViewModel.cs +++ b/src/Files.App/ViewModels/Properties/CompatibilityViewModel.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Windows.Input; @@ -8,7 +8,7 @@ namespace Files.App.ViewModels.Properties /// /// Represents view model of . /// - public sealed class CompatibilityViewModel : ObservableObject + public sealed partial class CompatibilityViewModel : ObservableObject { // Dependency injections @@ -81,30 +81,30 @@ public string SelectedHighDpiOverride public CompatibilityViewModel(ListedItem item) { - ItemPath = item is ShortcutItem shortcutItem ? shortcutItem.TargetPath : item.ItemPath; + ItemPath = item is IShortcutItem shortcutItem ? shortcutItem.TargetPath : item.ItemPath; CompatibilityOptions = WindowsCompatibilityService.GetCompatibilityOptionsForPath(ItemPath); - CompatibilityModes.Add(WindowsCompatModeKind.None, "None".GetLocalizedResource()); + CompatibilityModes.Add(WindowsCompatModeKind.None, Strings.None.GetLocalizedResource()); CompatibilityModes.Add(WindowsCompatModeKind.WindowsVista, "Windows Vista"); CompatibilityModes.Add(WindowsCompatModeKind.WindowsVistaSP1, "Windows Vista (Service Pack 1)"); CompatibilityModes.Add(WindowsCompatModeKind.WindowsVistaSP2, "Windows Vista (Service Pack 2)"); CompatibilityModes.Add(WindowsCompatModeKind.Windows7, "Windows 7"); CompatibilityModes.Add(WindowsCompatModeKind.Windows8, "Windows 8"); - ReducedColorModes.Add(WindowsCompatReducedColorModeKind.None, "CompatibilityNoReducedColor".GetLocalizedResource()); - ReducedColorModes.Add(WindowsCompatReducedColorModeKind.Color8Bit, "CompatibilityReducedColorModeColor8bit".GetLocalizedResource()); - ReducedColorModes.Add(WindowsCompatReducedColorModeKind.Color16Bit, "CompatibilityReducedColorModeColor16bit".GetLocalizedResource()); + ReducedColorModes.Add(WindowsCompatReducedColorModeKind.None, Strings.CompatibilityNoReducedColor.GetLocalizedResource()); + ReducedColorModes.Add(WindowsCompatReducedColorModeKind.Color8Bit, Strings.CompatibilityReducedColorModeColor8bit.GetLocalizedResource()); + ReducedColorModes.Add(WindowsCompatReducedColorModeKind.Color16Bit, Strings.CompatibilityReducedColorModeColor16bit.GetLocalizedResource()); - HighDpiOptions.Add(WindowsCompatDPIOptionKind.None, "CompatibilityDoNotAdjustDPI".GetLocalizedResource()); - HighDpiOptions.Add(WindowsCompatDPIOptionKind.UseDPIOnLogin, "CompatibilityOnWindowsLogin".GetLocalizedResource()); - HighDpiOptions.Add(WindowsCompatDPIOptionKind.UseDPIOnProgramStart, "CompatibilityOnProgramStart".GetLocalizedResource()); + HighDpiOptions.Add(WindowsCompatDPIOptionKind.None, Strings.CompatibilityDoNotAdjustDPI.GetLocalizedResource()); + HighDpiOptions.Add(WindowsCompatDPIOptionKind.UseDPIOnLogin, Strings.CompatibilityOnWindowsLogin.GetLocalizedResource()); + HighDpiOptions.Add(WindowsCompatDPIOptionKind.UseDPIOnProgramStart, Strings.CompatibilityOnProgramStart.GetLocalizedResource()); - HighDpiOverrides.Add(WindowsCompatDpiOverrideKind.None, "CompatibilityDoNotOverrideDPI".GetLocalizedResource()); - HighDpiOverrides.Add(WindowsCompatDpiOverrideKind.Advanced, "Advanced".GetLocalizedResource()); - HighDpiOverrides.Add(WindowsCompatDpiOverrideKind.Application, "Application".GetLocalizedResource()); - HighDpiOverrides.Add(WindowsCompatDpiOverrideKind.System, "System".GetLocalizedResource()); - HighDpiOverrides.Add(WindowsCompatDpiOverrideKind.SystemAdvanced, "CompatibilitySystemEnhanced".GetLocalizedResource()); + HighDpiOverrides.Add(WindowsCompatDpiOverrideKind.None, Strings.CompatibilityDoNotOverrideDPI.GetLocalizedResource()); + HighDpiOverrides.Add(WindowsCompatDpiOverrideKind.Advanced, Strings.Advanced.GetLocalizedResource()); + HighDpiOverrides.Add(WindowsCompatDpiOverrideKind.Application, Strings.Application.GetLocalizedResource()); + HighDpiOverrides.Add(WindowsCompatDpiOverrideKind.System, Strings.System.GetLocalizedResource()); + HighDpiOverrides.Add(WindowsCompatDpiOverrideKind.SystemAdvanced, Strings.CompatibilitySystemEnhanced.GetLocalizedResource()); RunTroubleshooterCommand = new AsyncRelayCommand(ExecuteRunTroubleshooterCommand); } diff --git a/src/Files.App/ViewModels/Properties/CustomizationViewModel.cs b/src/Files.App/ViewModels/Properties/CustomizationViewModel.cs index 1b3138051609..b6869ec059a5 100644 --- a/src/Files.App/ViewModels/Properties/CustomizationViewModel.cs +++ b/src/Files.App/ViewModels/Properties/CustomizationViewModel.cs @@ -1,13 +1,10 @@ -using CommunityToolkit.WinUI; -using Files.App.Utils.Shell; +using Microsoft.UI.Windowing; using System.IO; -using Windows.Storage.Pickers; -using Microsoft.UI.Windowing; using System.Windows.Input; namespace Files.App.ViewModels.Properties { - public sealed class CustomizationViewModel : ObservableObject + public sealed partial class CustomizationViewModel : ObservableObject { private ICommonDialogService CommonDialogService { get; } = Ioc.Default.GetRequiredService(); @@ -24,13 +21,29 @@ private static string DefaultIconDllFilePath public readonly bool IsShortcut; - public ObservableCollection DllIcons { get; } + public ObservableCollection DllIcons { get; } = []; private string _IconResourceItemPath; public string IconResourceItemPath { get => _IconResourceItemPath; - set => SetProperty(ref _IconResourceItemPath, value); + set + { + if (SetProperty(ref _IconResourceItemPath, value)) + { + DllIcons.Clear(); + + if (Path.Exists(_IconResourceItemPath)) + { + var icons = Win32Helper.ExtractIconsFromDLL(_IconResourceItemPath); + if (icons?.Count is null or 0) + return; + + foreach (var item in icons) + DllIcons.Add(item); + } + } + } } private IconFileInfo? _SelectedDllIcon; @@ -64,10 +77,6 @@ public CustomizationViewModel(IShellPage appInstance, BaseProperties basePropert IsShortcut = item.IsShortcut; _selectedItemPath = item.ItemPath; - DllIcons = []; - - // Get default - LoadIconsForPath(IconResourceItemPath); RestoreDefaultIconCommand = new RelayCommand(ExecuteRestoreDefaultIconCommand); OpenFilePickerCommand = new RelayCommand(ExecuteOpenFilePickerCommand); @@ -86,14 +95,16 @@ private void ExecuteOpenFilePickerCommand() string[] extensions = [ - "ApplicationExtension".GetLocalizedResource(), "*.dll", - "Application".GetLocalizedResource(), "*.exe", - "IcoFileCapitalized".GetLocalizedResource(), "*.ico", + Strings.IconFiles.GetLocalizedResource(), "*.dll;*.exe;*.ico;*.icl", + Strings.ApplicationExtension.GetLocalizedResource(), "*.dll", + Strings.Application.GetLocalizedResource(), "*.exe", + Strings.IcoFileCapitalized.GetLocalizedResource(), "*.ico", + Strings.IclFileCapitalized.GetLocalizedResource(), "*.icl ", ]; var result = CommonDialogService.Open_FileOpenDialog(hWnd, false, extensions, Environment.SpecialFolder.MyComputer, out var filePath); if (result) - LoadIconsForPath(filePath); + IconResourceItemPath = filePath; } public async Task UpdateIcon() @@ -126,18 +137,5 @@ await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => return true; } - - private void LoadIconsForPath(string path) - { - IconResourceItemPath = path; - DllIcons.Clear(); - - var icons = Win32Helper.ExtractIconsFromDLL(path); - if (icons?.Count is null or 0) - return; - - foreach(var item in icons) - DllIcons.Add(item); - } } } diff --git a/src/Files.App/ViewModels/Properties/HashesViewModel.cs b/src/Files.App/ViewModels/Properties/HashesViewModel.cs index 157edfec52b2..f23631094043 100644 --- a/src/Files.App/ViewModels/Properties/HashesViewModel.cs +++ b/src/Files.App/ViewModels/Properties/HashesViewModel.cs @@ -1,16 +1,22 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.Shared.Helpers; +using Microsoft.UI.Windowing; +using Microsoft.UI.Xaml.Controls; using System.IO; +using System.Security.Cryptography; using System.Windows.Input; namespace Files.App.ViewModels.Properties { - public sealed class HashesViewModel : ObservableObject, IDisposable + public sealed partial class HashesViewModel : ObservableObject, IDisposable { + private ICommonDialogService CommonDialogService { get; } = Ioc.Default.GetRequiredService(); private IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetService()!; + private readonly AppWindow _appWindow; + private HashInfoItem _selectedItem; public HashInfoItem SelectedItem { @@ -23,16 +29,48 @@ public HashInfoItem SelectedItem public Dictionary ShowHashes { get; private set; } public ICommand ToggleIsEnabledCommand { get; private set; } + public ICommand CompareFileCommand { get; private set; } private ListedItem _item; private CancellationTokenSource _cancellationTokenSource; - public HashesViewModel(ListedItem item) + private string _hashInput; + public string HashInput + { + get => _hashInput; + set + { + SetProperty(ref _hashInput, value); + + OnHashInputTextChanged(); + OnPropertyChanged(nameof(IsInfoBarOpen)); + } + } + + private InfoBarSeverity _infoBarSeverity; + public InfoBarSeverity InfoBarSeverity + { + get => _infoBarSeverity; + set => SetProperty(ref _infoBarSeverity, value); + } + + private string _infoBarTitle; + public string InfoBarTitle + { + get => _infoBarTitle; + set => SetProperty(ref _infoBarTitle, value); + } + + public bool IsInfoBarOpen + => !string.IsNullOrEmpty(HashInput); + + public HashesViewModel(ListedItem item, AppWindow appWindow) { ToggleIsEnabledCommand = new RelayCommand(ToggleIsEnabled); _item = item; + _appWindow = appWindow; _cancellationTokenSource = new(); Hashes = @@ -55,6 +93,8 @@ public HashesViewModel(ListedItem item) ShowHashes.TryAdd("SHA512", false); Hashes.Where(x => ShowHashes[x.Algorithm]).ForEach(x => ToggleIsEnabledCommand.Execute(x.Algorithm)); + + CompareFileCommand = new RelayCommand(async () => await OnCompareFileAsync()); } private void ToggleIsEnabled(string? algorithm) @@ -71,7 +111,7 @@ private void ToggleIsEnabled(string? algorithm) // Don't calculate hashes for online files if (_item.SyncStatusUI.SyncStatus is CloudDriveSyncStatus.FileOnline or CloudDriveSyncStatus.FolderOnline) { - hashInfoItem.HashValue = "CalculationOnlineFileHashError".GetLocalizedResource(); + hashInfoItem.HashValue = Strings.CalculationOnlineFileHashError.GetLocalizedResource(); return; } @@ -83,7 +123,7 @@ private void ToggleIsEnabled(string? algorithm) { try { - using (var stream = File.OpenRead(_item.ItemPath)) + await using (var stream = File.OpenRead(_item.ItemPath)) { hashInfoItem.HashValue = hashInfoItem.Algorithm switch { @@ -106,11 +146,11 @@ private void ToggleIsEnabled(string? algorithm) catch (IOException) { // File is currently open - hashInfoItem.HashValue = "CalculationErrorFileIsOpen".GetLocalizedResource(); + hashInfoItem.HashValue = Strings.CalculationErrorFileIsOpen.GetLocalizedResource(); } catch (Exception) { - hashInfoItem.HashValue = "CalculationError".GetLocalizedResource(); + hashInfoItem.HashValue = Strings.CalculationError.GetLocalizedResource(); } finally { @@ -120,6 +160,52 @@ private void ToggleIsEnabled(string? algorithm) } } + public string FindMatchingAlgorithm(string hash) + { + if (string.IsNullOrEmpty(hash)) + return string.Empty; + + return Hashes.FirstOrDefault(h => h.HashValue?.Equals(hash, StringComparison.OrdinalIgnoreCase) == true)?.Algorithm ?? string.Empty; + } + + private static Task CalculateFileHashAsync(string filePath) + { + return Task.Run(() => + { + using var stream = File.OpenRead(filePath); + return Convert.ToHexStringLower(MD5.HashData(stream)); + }); + } + + private void OnHashInputTextChanged() + { + string matchingAlgorithm = FindMatchingAlgorithm(HashInput); + + InfoBarSeverity = string.IsNullOrEmpty(matchingAlgorithm) + ? InfoBarSeverity.Error + : InfoBarSeverity.Success; + + InfoBarTitle = string.IsNullOrEmpty(matchingAlgorithm) + ? Strings.HashesDoNotMatch.GetLocalizedResource() + : string.Format(Strings.HashesMatch.GetLocalizedResource(), matchingAlgorithm); + } + + private async Task OnCompareFileAsync() + { + var hWnd = Microsoft.UI.Win32Interop.GetWindowFromWindowId(_appWindow.Id); + + var result = CommonDialogService.Open_FileOpenDialog( + hWnd, + false, + [], + Environment.SpecialFolder.Desktop, + out var filePath); + + HashInput = result && filePath != null + ? await CalculateFileHashAsync(filePath) + : string.Empty; + } + public void Dispose() { _cancellationTokenSource.Cancel(); diff --git a/src/Files.App/ViewModels/Properties/Items/BaseProperties.cs b/src/Files.App/ViewModels/Properties/Items/BaseProperties.cs index 3af11e5162eb..c204b74af287 100644 --- a/src/Files.App/ViewModels/Properties/Items/BaseProperties.cs +++ b/src/Files.App/ViewModels/Properties/Items/BaseProperties.cs @@ -1,10 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Dispatching; using System.IO; using Windows.Storage.FileProperties; -using static Files.App.Helpers.Win32Helper; using FileAttributes = System.IO.FileAttributes; namespace Files.App.ViewModels.Properties @@ -122,8 +121,8 @@ await Dispatcher.EnqueueOrInvokeAsync(() => public void SetItemsCountString() { ViewModel.FilesAndFoldersCountString = ViewModel.LocationsCount > 0 - ? "PropertiesFilesAndFoldersAndLocationsCount".GetLocalizedFormatResource(ViewModel.FilesCount, ViewModel.FoldersCount, ViewModel.LocationsCount) - : "PropertiesFilesAndFoldersCountString".GetLocalizedFormatResource(ViewModel.FilesCount, ViewModel.FoldersCount); + ? Strings.PropertiesFilesAndFoldersAndLocationsCount.GetLocalizedFormatResource(ViewModel.FilesCount, ViewModel.FoldersCount, ViewModel.LocationsCount) + : Strings.PropertiesFilesAndFoldersCountString.GetLocalizedFormatResource(ViewModel.FilesCount, ViewModel.FoldersCount); } } } diff --git a/src/Files.App/ViewModels/Properties/Items/CombinedFileProperties.cs b/src/Files.App/ViewModels/Properties/Items/CombinedFileProperties.cs index 836d7ef0038d..6ea1ac913791 100644 --- a/src/Files.App/ViewModels/Properties/Items/CombinedFileProperties.cs +++ b/src/Files.App/ViewModels/Properties/Items/CombinedFileProperties.cs @@ -1,5 +1,5 @@ -// Copyright(c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright(c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Dispatching; @@ -17,7 +17,8 @@ public CombinedFileProperties( public async Task GetSystemFilePropertiesAsync() { - var queries = await Task.WhenAll(List.AsParallel().Select(async item => { + var queries = await Task.WhenAll(List.AsParallel().Select(async item => + { BaseStorageFile file = await FilesystemTasks.Wrap(() => StorageFileExtensions.DangerousGetFileFromPathAsync(item.ItemPath)); if (file is null) { @@ -27,10 +28,13 @@ public async Task GetSystemFilePropertiesAsync() var list = await FileProperty.RetrieveAndInitializePropertiesAsync(file); - list.Find(x => x.ID == "address").Value = - await LocationHelpers.GetAddressFromCoordinatesAsync((double?)list.Find( - x => x.Property == "System.GPS.LatitudeDecimal").Value, - (double?)list.Find(x => x.Property == "System.GPS.LongitudeDecimal").Value); + var latitude = list.Find(x => x.Property == "System.GPS.LatitudeDecimal")?.Value as double?; + var longitude = list.Find(x => x.Property == "System.GPS.LongitudeDecimal")?.Value as double?; + var addressItem = list.Find(x => x.ID == "address"); + + if (latitude.HasValue && longitude.HasValue && addressItem != null) + addressItem.Value = await LocationHelpers.GetAddressFromCoordinatesAsync(latitude.Value, longitude.Value); + return list .Where(fileProp => !(fileProp.Value is null && fileProp.IsReadOnly)) @@ -60,8 +64,8 @@ await LocationHelpers.GetAddressFromCoordinatesAsync((double?)list.Find( else if (props.Where(x => x.Property == prop.Property).Any(x => !Equals(x.Value, prop.Value))) { // Has multiple values - prop.Value = prop.IsReadOnly ? "MultipleValues".GetLocalizedResource() : null; - prop.PlaceholderText = "MultipleValues".GetLocalizedResource(); + prop.Value = prop.IsReadOnly ? Strings.MultipleValues.GetLocalizedResource() : null; + prop.PlaceholderText = Strings.MultipleValues.GetLocalizedResource(); } } } diff --git a/src/Files.App/ViewModels/Properties/Items/CombinedProperties.cs b/src/Files.App/ViewModels/Properties/Items/CombinedProperties.cs index 7dc2590be7a5..da6721fcb94d 100644 --- a/src/Files.App/ViewModels/Properties/Items/CombinedProperties.cs +++ b/src/Files.App/ViewModels/Properties/Items/CombinedProperties.cs @@ -1,14 +1,6 @@ -using Files.App.Extensions; -using Files.App.Utils; -using Files.App.Helpers; using Microsoft.Extensions.Logging; using Microsoft.UI.Dispatching; -using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Windows.Storage; namespace Files.App.ViewModels.Properties @@ -41,52 +33,74 @@ public sealed override void GetBaseProperties() if (List.All(x => x.ItemType.Equals(List.First().ItemType))) { - ViewModel.ItemType = string.Format("PropertiesDriveItemTypesEquals".GetLocalizedResource(), List.First().ItemType); + ViewModel.ItemType = string.Format(Strings.PropertiesDriveItemTypesEquals.GetLocalizedResource(), List.First().ItemType); } else { - ViewModel.ItemType = "PropertiesDriveItemTypeDifferent".GetLocalizedResource(); + ViewModel.ItemType = Strings.PropertiesDriveItemTypeDifferent.GetLocalizedResource(); } var itemsPath = List.Select(Item => (Item as RecycleBinItem)?.ItemOriginalFolder ?? (Path.IsPathRooted(Item.ItemPath) ? Path.GetDirectoryName(Item.ItemPath) : Item.ItemPath)); if (itemsPath.Distinct().Count() == 1) - ViewModel.ItemLocation = string.Format("PropertiesCombinedItemLocation".GetLocalizedResource(), itemsPath.First()); + ViewModel.ItemLocation = string.Format(Strings.PropertiesCombinedItemLocation.GetLocalizedResource(), itemsPath.First()); } } public override async Task GetSpecialPropertiesAsync() { - if (List.All(x => x.PrimaryItemAttribute == StorageItemTypes.File)) + bool allFiles = true, allReadOnly = true, allNotReadOnly = true, allHidden = true, allNotHidden = true; + bool allCompressed = true, allNotCompressed = true, anyCanCompress = false; + foreach (var x in List) { - var fileAttributesReadOnly = List.Select(x => Win32Helper.HasFileAttribute(x.ItemPath, System.IO.FileAttributes.ReadOnly)); - if (fileAttributesReadOnly.All(x => x)) + allFiles &= x.PrimaryItemAttribute == StorageItemTypes.File; + var fileAttributes = Win32Helper.GetFileAttributes(x.ItemPath); + bool isReadOnly = fileAttributes.HasFlag(System.IO.FileAttributes.ReadOnly); + allReadOnly &= isReadOnly; + allNotReadOnly &= !isReadOnly; + bool isHidden = fileAttributes.HasFlag(System.IO.FileAttributes.Hidden); + allHidden &= isHidden; + allNotHidden &= !isHidden; + bool isCompressed = fileAttributes.HasFlag(System.IO.FileAttributes.Compressed); + allCompressed &= isCompressed; + allNotCompressed &= !isCompressed; + anyCanCompress |= Win32Helper.CanCompressContent(x.ItemPath); + } + + if (allFiles) + { + if (allReadOnly) ViewModel.IsReadOnly = true; - else if (!fileAttributesReadOnly.Any(x => x)) + else if (allNotReadOnly) ViewModel.IsReadOnly = false; else ViewModel.IsReadOnly = null; } - var fileAttributesHidden = List.Select(x => Win32Helper.HasFileAttribute(x.ItemPath, System.IO.FileAttributes.Hidden)); - if (fileAttributesHidden.All(x => x)) + if (allHidden) ViewModel.IsHidden = true; - else if (!fileAttributesHidden.Any(x => x)) + else if (allNotHidden) ViewModel.IsHidden = false; else ViewModel.IsHidden = null; + ViewModel.CanCompressContent = anyCanCompress; + if (allCompressed) + ViewModel.IsContentCompressed = true; + else if (allNotCompressed) + ViewModel.IsContentCompressed = false; + else + ViewModel.IsContentCompressed = null; + ViewModel.LastSeparatorVisibility = false; ViewModel.ItemSizeVisibility = true; - ViewModel.FilesCount += List.Where(x => x.PrimaryItemAttribute == StorageItemTypes.File || x.IsArchive).ToList().Count; - ViewModel.FoldersCount += List.Where(x => x.PrimaryItemAttribute == StorageItemTypes.Folder && !x.IsArchive).ToList().Count; + ViewModel.FilesCount += List.Count(x => x.PrimaryItemAttribute == StorageItemTypes.File || x.IsArchive); + ViewModel.FoldersCount += List.Count(x => x.PrimaryItemAttribute == StorageItemTypes.Folder && !x.IsArchive); - long totalSize = 0; long filesSize = List.Where(x => x.PrimaryItemAttribute == StorageItemTypes.File).Sum(x => x.FileSizeBytes); long foldersSize = 0; - long totalSizeOnDisk = 0; long filesSizeOnDisk = List.Where(x => x.PrimaryItemAttribute == StorageItemTypes.File && x.SyncStatusUI.SyncStatus is not CloudDriveSyncStatus.FileOnline and not CloudDriveSyncStatus.FolderOnline) .Sum(x => Win32Helper.GetFileSizeOnDisk(x.ItemPath) ?? 0); @@ -104,11 +118,11 @@ CloudDriveSyncStatus.FolderOnline or CloudDriveSyncStatus.FolderOfflinePartial) continue; - var fileSizeTask = Task.Run(() => CalculateFolderSizeAsync(item.ItemPath, TokenSource.Token)); + var folderSizeTask = Task.Run(() => CalculateFolderSizeAsync(item.ItemPath, TokenSource.Token)); try { - var folderSize = await fileSizeTask; + var folderSize = await folderSizeTask; foldersSize += folderSize.size; foldersSizeOnDisk += folderSize.sizeOnDisk; } @@ -122,15 +136,15 @@ CloudDriveSyncStatus.FolderOnline or ViewModel.ItemSizeProgressVisibility = false; ViewModel.ItemSizeOnDiskProgressVisibility = false; - totalSize = filesSize + foldersSize; + long totalSize = filesSize + foldersSize; ViewModel.ItemSize = totalSize.ToLongSizeString(); - totalSizeOnDisk = filesSizeOnDisk + foldersSizeOnDisk; + long totalSizeOnDisk = filesSizeOnDisk + foldersSizeOnDisk; ViewModel.ItemSizeOnDisk = totalSizeOnDisk.ToLongSizeString(); SetItemsCountString(); } - private void ViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + private async void ViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { switch (e.PropertyName) { @@ -170,6 +184,18 @@ private void ViewModel_PropertyChanged(object sender, System.ComponentModel.Prop } break; + + case "IsContentCompressed": + { + var isCompressed = ViewModel.IsContentCompressed ?? false; + var items = List.Select(x => x.ItemPath).ToList(); + await Task.Run(() => + { + foreach (var path in items) + Win32Helper.SetCompressionAttributeIoctl(path, isCompressed); + }); + } + break; } } } diff --git a/src/Files.App/ViewModels/Properties/Items/DriveProperties.cs b/src/Files.App/ViewModels/Properties/Items/DriveProperties.cs index 750ac63b8ed0..719a9212afce 100644 --- a/src/Files.App/ViewModels/Properties/Items/DriveProperties.cs +++ b/src/Files.App/ViewModels/Properties/Items/DriveProperties.cs @@ -1,11 +1,4 @@ -using Files.App.Data.Items; -using Files.App.Extensions; -using Files.App.Utils; -using Files.App.Helpers; using Microsoft.Extensions.Logging; -using System; -using System.Threading.Tasks; -using Windows.Storage.FileProperties; namespace Files.App.ViewModels.Properties { @@ -39,12 +32,13 @@ public override void GetBaseProperties() ViewModel.OriginalItemName = Drive.Text; // NOTE: If DriveType enum changes, the corresponding resource keys should change too - ViewModel.ItemType = string.Format("DriveType{0}", Drive.Type).GetLocalizedResource(); + ViewModel.ItemType = $"DriveType{Drive.Type}".GetLocalizedResource(); } public async override Task GetSpecialPropertiesAsync() { ViewModel.ItemAttributesVisibility = false; + var item = await FilesystemTasks.Wrap(() => DriveHelpers.GetRootFromPathAsync(Drive.Path)); BaseStorageFolder diskRoot = await FilesystemTasks.Wrap(() => StorageFileExtensions.DangerousGetFolderFromPathAsync(Drive.Path, item)); @@ -77,6 +71,22 @@ public async override Task GetSpecialPropertiesAsync() return; } + try + { + var syncRootStatus = await SyncRootHelpers.GetSyncRootQuotaAsync(Drive.Path); + if (syncRootStatus.Success) + { + ViewModel.DriveCapacityValue = syncRootStatus.Capacity; + ViewModel.DriveUsedSpaceValue = syncRootStatus.Used; + ViewModel.DriveFreeSpaceValue = syncRootStatus.Capacity - syncRootStatus.Used; + return; + } + } + catch (Exception e) + { + App.Logger.LogWarning(e, "Failed to get sync root quota for path: {Path}", Drive.Path); + } + try { string freeSpace = "System.FreeSpace"; diff --git a/src/Files.App/ViewModels/Properties/Items/FileProperties.cs b/src/Files.App/ViewModels/Properties/Items/FileProperties.cs index ba6b63447c53..368fb1564dde 100644 --- a/src/Files.App/ViewModels/Properties/Items/FileProperties.cs +++ b/src/Files.App/ViewModels/Properties/Items/FileProperties.cs @@ -1,5 +1,5 @@ -// Copyright(c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright(c) Files Community +// Licensed under the MIT License. using Files.Shared.Helpers; using Microsoft.UI.Dispatching; @@ -45,27 +45,29 @@ public override void GetBaseProperties() ViewModel.CustomIconSource = Item.CustomIconSource; ViewModel.LoadFileIcon = Item.LoadFileIcon; ViewModel.IsDownloadedFile = Win32Helper.ReadStringFromFile($"{Item.ItemPath}:Zone.Identifier") is not null; - ViewModel.IsEditAlbumCoverVisible = + ViewModel.IsEditAlbumCoverVisible = FileExtensionHelpers.IsVideoFile(Item.FileExtension) || FileExtensionHelpers.IsAudioFile(Item.FileExtension); if (!Item.IsShortcut) return; - var shortcutItem = (ShortcutItem)Item; + var shortcutItem = (IShortcutItem)Item; var isApplication = FileExtensionHelpers.IsExecutableFile(shortcutItem.TargetPath) || FileExtensionHelpers.IsMsiFile(shortcutItem.TargetPath); - ViewModel.ShortcutItemType = isApplication ? "Application".GetLocalizedResource() : - Item.IsLinkItem ? "PropertiesShortcutTypeLink".GetLocalizedResource() : "File".GetLocalizedResource(); + ViewModel.ShortcutItemType = isApplication ? Strings.Application.GetLocalizedResource() : + Item.IsLinkItem ? Strings.PropertiesShortcutTypeLink.GetLocalizedResource() : Strings.File.GetLocalizedResource(); ViewModel.ShortcutItemPath = shortcutItem.TargetPath; ViewModel.IsShortcutItemPathReadOnly = shortcutItem.IsSymLink; ViewModel.ShortcutItemWorkingDir = shortcutItem.WorkingDirectory; ViewModel.ShortcutItemWorkingDirVisibility = Item.IsLinkItem || shortcutItem.IsSymLink ? false : true; ViewModel.ShortcutItemArguments = shortcutItem.Arguments; + ViewModel.ShowWindowCommand = shortcutItem.ShowWindowCommand; ViewModel.ShortcutItemArgumentsVisibility = Item.IsLinkItem || shortcutItem.IsSymLink ? false : true; + ViewModel.ShortcutItemWindowArgsVisibility = Item.IsLinkItem || shortcutItem.IsSymLink ? false : true; if (isApplication) ViewModel.RunAsAdmin = shortcutItem.RunAsAdmin; @@ -76,7 +78,6 @@ public override void GetBaseProperties() { if (Item.IsLinkItem) { - var tmpItem = (ShortcutItem)Item; await Win32Helper.InvokeWin32ComponentAsync(ViewModel.ShortcutItemPath, AppInstance, ViewModel.ShortcutItemArguments, ViewModel.RunAsAdmin, ViewModel.ShortcutItemWorkingDir); } else @@ -93,19 +94,25 @@ await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync( public override async Task GetSpecialPropertiesAsync() { - ViewModel.IsReadOnly = Win32Helper.HasFileAttribute( - Item.ItemPath, System.IO.FileAttributes.ReadOnly); - ViewModel.IsHidden = Win32Helper.HasFileAttribute( - Item.ItemPath, System.IO.FileAttributes.Hidden); - + // Check if item is on device (not online) + var isOnDevice = Item.SyncStatusUI.SyncStatus is not CloudDriveSyncStatus.FileOnline and not CloudDriveSyncStatus.FolderOnline; + + // Set basic file attributes + FileAttributes fileAttributes = Win32Helper.GetFileAttributes(Item.ItemPath); + ViewModel.IsReadOnly = fileAttributes.HasFlag(FileAttributes.ReadOnly); + ViewModel.IsHidden = fileAttributes.HasFlag(FileAttributes.Hidden); + ViewModel.CanCompressContent = Win32Helper.CanCompressContent(Item.ItemPath); ViewModel.ItemSizeVisibility = true; ViewModel.ItemSize = Item.FileSizeBytes.ToLongSizeString(); - // Only load the size for items on the device - if (Item.SyncStatusUI.SyncStatus is not CloudDriveSyncStatus.FileOnline and not CloudDriveSyncStatus.FolderOnline) - ViewModel.ItemSizeOnDisk = Win32Helper.GetFileSizeOnDisk(Item.ItemPath)?.ToLongSizeString() ?? - string.Empty; + // Only check the compressed attribute and size on disk for items on the device + if (isOnDevice) + { + ViewModel.IsContentCompressed = fileAttributes.HasFlag(FileAttributes.Compressed); + ViewModel.ItemSizeOnDisk = Win32Helper.GetFileSizeOnDisk(Item.ItemPath)?.ToLongSizeString() ?? string.Empty; + } + // Load icon var result = await FileThumbnailHelper.GetIconAsync( Item.ItemPath, Constants.ShellIconSizes.ExtraLarge, @@ -119,18 +126,21 @@ public override async Task GetSpecialPropertiesAsync() ViewModel.LoadFileIcon = true; } + // Handle shortcut properties if (Item.IsShortcut) { ViewModel.ItemCreatedTimestampReal = Item.ItemDateCreatedReal; ViewModel.ItemAccessedTimestampReal = Item.ItemDateAccessedReal; - if (Item.IsLinkItem || string.IsNullOrWhiteSpace(((ShortcutItem)Item).TargetPath)) + + if (Item.IsLinkItem || string.IsNullOrWhiteSpace(((IShortcutItem)Item).TargetPath)) { // Can't show any other property return; } } - string filePath = (Item as ShortcutItem)?.TargetPath ?? Item.ItemPath; + // Get file for further processing + string filePath = (Item as IShortcutItem)?.TargetPath ?? Item.ItemPath; BaseStorageFile file = await AppInstance.ShellViewModel.GetFileFromPathAsync(filePath); // Couldn't access the file and can't load any other properties @@ -141,17 +151,20 @@ public override async Task GetSpecialPropertiesAsync() if (Item.IsShortcut) return; - if (Item.SyncStatusUI.SyncStatus is not CloudDriveSyncStatus.FileOnline and not CloudDriveSyncStatus.FolderOnline) - if (FileExtensionHelpers.IsBrowsableZipFile(Item.FileExtension, out _)) - if (await ZipStorageFolder.FromPathAsync(Item.ItemPath) is ZipStorageFolder zipFolder) - { - var uncompressedSize = await zipFolder.GetUncompressedSize(); - ViewModel.UncompressedItemSize = uncompressedSize.ToLongSizeString(); - ViewModel.UncompressedItemSizeBytes = uncompressedSize; - } + // Load uncompressed size for browsable zip files on device + if (isOnDevice && FileExtensionHelpers.IsBrowsableZipFile(Item.FileExtension, out _)) + { + if (await ZipStorageFolder.FromPathAsync(Item.ItemPath) is ZipStorageFolder zipFolder) + { + var uncompressedSize = await zipFolder.GetUncompressedSize(); + ViewModel.UncompressedItemSize = uncompressedSize.ToLongSizeString(); + ViewModel.UncompressedItemSizeBytes = uncompressedSize; + } + } + // Get other properties if available if (file.Properties is not null) - GetOtherPropertiesAsync(file.Properties); + _ = GetOtherPropertiesAsync(file.Properties); } public async Task GetSystemFilePropertiesAsync() @@ -279,21 +292,26 @@ private async void ViewModel_PropertyChanged(object sender, System.ComponentMode if (ViewModel.IsHidden is not null) { if ((bool)ViewModel.IsHidden) - Win32Helper.SetFileAttribute(Item.ItemPath, System.IO.FileAttributes.Hidden); + Win32Helper.SetFileAttribute(Item.ItemPath, System.IO.FileAttributes.Hidden); else Win32Helper.UnsetFileAttribute(Item.ItemPath, System.IO.FileAttributes.Hidden); } break; + case nameof(ViewModel.IsContentCompressed): + Win32Helper.SetCompressionAttributeIoctl(Item.ItemPath, ViewModel.IsContentCompressed ?? false); + break; + case nameof(ViewModel.RunAsAdmin): case nameof(ViewModel.ShortcutItemPath): case nameof(ViewModel.ShortcutItemWorkingDir): + case nameof(ViewModel.ShowWindowCommand): case nameof(ViewModel.ShortcutItemArguments): if (string.IsNullOrWhiteSpace(ViewModel.ShortcutItemPath)) return; - await FileOperationsHelpers.CreateOrUpdateLinkAsync(Item.ItemPath, ViewModel.ShortcutItemPath, ViewModel.ShortcutItemArguments, ViewModel.ShortcutItemWorkingDir, ViewModel.RunAsAdmin); + await FileOperationsHelpers.CreateOrUpdateLinkAsync(Item.ItemPath, ViewModel.ShortcutItemPath, ViewModel.ShortcutItemArguments, ViewModel.ShortcutItemWorkingDir, ViewModel.RunAsAdmin, ViewModel.ShowWindowCommand); break; } diff --git a/src/Files.App/ViewModels/Properties/Items/FileProperty.cs b/src/Files.App/ViewModels/Properties/Items/FileProperty.cs index 6d2a9207ed2f..196fd06e3738 100644 --- a/src/Files.App/ViewModels/Properties/Items/FileProperty.cs +++ b/src/Files.App/ViewModels/Properties/Items/FileProperty.cs @@ -1,5 +1,5 @@ -// Copyright(c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright(c) Files Community +// Licensed under the MIT License. using Files.App.Converters; using Microsoft.UI.Xaml; @@ -12,7 +12,7 @@ namespace Files.App.ViewModels.Properties /// /// This class is represents a system file property from the Windows.Storage API /// - public sealed class FileProperty : ObservableObject + public sealed partial class FileProperty : ObservableObject { /// /// The name to display @@ -231,7 +231,7 @@ private IValueConverter GetConverter() { try { - return EnumeratedList.TryGetValue(Convert.ToInt32(Value), out var value) ? + return EnumeratedList.TryGetValue(Convert.ToInt32(Value), out var value) ? value.GetLocalizedResource() : null; } catch diff --git a/src/Files.App/ViewModels/Properties/Items/FilePropertySection.cs b/src/Files.App/ViewModels/Properties/Items/FilePropertySection.cs index eee8bb2d1d62..2bcd674810f4 100644 --- a/src/Files.App/ViewModels/Properties/Items/FilePropertySection.cs +++ b/src/Files.App/ViewModels/Properties/Items/FilePropertySection.cs @@ -1,13 +1,11 @@ -using Files.App.Extensions; using Microsoft.UI.Xaml; -using System.Collections.Generic; namespace Files.App.ViewModels.Properties { /// /// This class is used for grouping file properties into sections so that it can be used as a grouped ListView data source /// - public sealed class FilePropertySection : List + public sealed partial class FilePropertySection : List { public FilePropertySection(IEnumerable items) : base(items) @@ -22,7 +20,7 @@ public string Title => Key.GetLocalizedResource(); public int Priority - => sectionPriority.ContainsKey(Key) ? sectionPriority[Key] : 0; + => sectionPriority.TryGetValue(Key, out int priority) ? priority : 0; /// /// This list sets the priorities for the sections diff --git a/src/Files.App/ViewModels/Properties/Items/FolderProperties.cs b/src/Files.App/ViewModels/Properties/Items/FolderProperties.cs index 4f50516fe32a..dcabf9d1e989 100644 --- a/src/Files.App/ViewModels/Properties/Items/FolderProperties.cs +++ b/src/Files.App/ViewModels/Properties/Items/FolderProperties.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.Extensions.Logging; using Microsoft.UI.Dispatching; @@ -10,6 +10,8 @@ namespace Files.App.ViewModels.Properties { internal sealed class FolderProperties : BaseProperties { + private readonly IStorageTrashBinService StorageTrashBinService = Ioc.Default.GetRequiredService(); + public ListedItem Item { get; } public FolderProperties( @@ -46,16 +48,17 @@ public override void GetBaseProperties() ViewModel.LoadFileIcon = Item.LoadFileIcon; ViewModel.ContainsFilesOrFolders = Item.ContainsFilesOrFolders; - if (Item.IsShortcut) + if (Item.IsShortcut && Item is IShortcutItem shortcutItem) { - var shortcutItem = (ShortcutItem)Item; - ViewModel.ShortcutItemType = "Folder".GetLocalizedResource(); + ViewModel.ShortcutItemType = Strings.Folder.GetLocalizedResource(); ViewModel.ShortcutItemPath = shortcutItem.TargetPath; ViewModel.IsShortcutItemPathReadOnly = false; ViewModel.ShortcutItemWorkingDir = shortcutItem.WorkingDirectory; + ViewModel.ShowWindowCommand = shortcutItem.ShowWindowCommand; ViewModel.ShortcutItemWorkingDirVisibility = false; ViewModel.ShortcutItemArguments = shortcutItem.Arguments; ViewModel.ShortcutItemArgumentsVisibility = false; + ViewModel.ShortcutItemWindowArgsVisibility = false; ViewModel.ShortcutItemOpenLinkCommand = new RelayCommand(async () => { await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync( @@ -71,15 +74,17 @@ await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync( public async override Task GetSpecialPropertiesAsync() { - ViewModel.IsHidden = Win32Helper.HasFileAttribute( - Item.ItemPath, System.IO.FileAttributes.Hidden); + var fileAttributes = Win32Helper.GetFileAttributes(Item.ItemPath); + ViewModel.IsHidden = fileAttributes.HasFlag(FileAttributes.Hidden); + ViewModel.CanCompressContent = Win32Helper.CanCompressContent(Item.ItemPath); + ViewModel.IsContentCompressed = fileAttributes.HasFlag(FileAttributes.Compressed); var result = await FileThumbnailHelper.GetIconAsync( Item.ItemPath, Constants.ShellIconSizes.ExtraLarge, true, IconOptions.UseCurrentScale); - + if (result is not null) { ViewModel.IconData = result; @@ -99,31 +104,31 @@ public async override Task GetSpecialPropertiesAsync() ViewModel.ItemCreatedTimestampReal = Item.ItemDateCreatedReal; ViewModel.ItemAccessedTimestampReal = Item.ItemDateAccessedReal; - if (Item.IsLinkItem || string.IsNullOrWhiteSpace(((ShortcutItem)Item).TargetPath)) + if (Item.IsLinkItem || string.IsNullOrWhiteSpace(((IShortcutItem)Item).TargetPath)) { // Can't show any other property return; } } - string folderPath = (Item as ShortcutItem)?.TargetPath ?? Item.ItemPath; + string folderPath = (Item as IShortcutItem)?.TargetPath ?? Item.ItemPath; BaseStorageFolder storageFolder = await AppInstance.ShellViewModel.GetFolderFromPathAsync(folderPath); if (storageFolder is not null) { ViewModel.ItemCreatedTimestampReal = storageFolder.DateCreated; if (storageFolder.Properties is not null) - GetOtherPropertiesAsync(storageFolder.Properties); + _ = GetOtherPropertiesAsync(storageFolder.Properties); // Only load the size for items on the device - if (Item.SyncStatusUI.SyncStatus is not CloudDriveSyncStatus.FileOnline and not + if (Item.SyncStatusUI.SyncStatus is not CloudDriveSyncStatus.FileOnline and not CloudDriveSyncStatus.FolderOnline and not CloudDriveSyncStatus.FolderOfflinePartial) - GetFolderSizeAsync(storageFolder.Path, TokenSource.Token); + _ = GetFolderSizeAsync(storageFolder.Path, TokenSource.Token); } else if (Item.ItemPath.Equals(Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.OrdinalIgnoreCase)) { - var recycleBinQuery = Win32Helper.QueryRecycleBin(); + var recycleBinQuery = StorageTrashBinService.QueryRecycleBin(); if (recycleBinQuery.BinSize is long binSize) { ViewModel.ItemSizeBytes = binSize; @@ -153,7 +158,7 @@ CloudDriveSyncStatus.FolderOnline and not } else { - GetFolderSizeAsync(folderPath, TokenSource.Token); + _ = GetFolderSizeAsync(folderPath, TokenSource.Token); } } @@ -199,7 +204,7 @@ private async void ViewModel_PropertyChanged(object sender, System.ComponentMode { switch (e.PropertyName) { - case "IsHidden": + case nameof(ViewModel.IsHidden): if (ViewModel.IsHidden is not null) { if ((bool)ViewModel.IsHidden) @@ -209,15 +214,20 @@ private async void ViewModel_PropertyChanged(object sender, System.ComponentMode } break; - case "ShortcutItemPath": - case "ShortcutItemWorkingDir": - case "ShortcutItemArguments": - var tmpItem = (ShortcutItem)Item; + case nameof(ViewModel.IsContentCompressed): + Win32Helper.SetCompressionAttributeIoctl(Item.ItemPath, ViewModel.IsContentCompressed ?? false); + break; + + case nameof(ViewModel.ShortcutItemPath): + case nameof(ViewModel.ShortcutItemWorkingDir): + case nameof(ViewModel.ShowWindowCommand): + case nameof(ViewModel.ShortcutItemArguments): + var shortcutItem = (IShortcutItem)Item; if (string.IsNullOrWhiteSpace(ViewModel.ShortcutItemPath)) return; - await FileOperationsHelpers.CreateOrUpdateLinkAsync(Item.ItemPath, ViewModel.ShortcutItemPath, ViewModel.ShortcutItemArguments, ViewModel.ShortcutItemWorkingDir, tmpItem.RunAsAdmin); + await FileOperationsHelpers.CreateOrUpdateLinkAsync(Item.ItemPath, ViewModel.ShortcutItemPath, ViewModel.ShortcutItemArguments, ViewModel.ShortcutItemWorkingDir, shortcutItem.RunAsAdmin, ViewModel.ShowWindowCommand); break; } } diff --git a/src/Files.App/ViewModels/Properties/Items/IFileProperties.cs b/src/Files.App/ViewModels/Properties/Items/IFileProperties.cs index adbc988257d9..7b9ecaa23249 100644 --- a/src/Files.App/ViewModels/Properties/Items/IFileProperties.cs +++ b/src/Files.App/ViewModels/Properties/Items/IFileProperties.cs @@ -1,5 +1,5 @@ -// Copyright(c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright(c) Files Community +// Licensed under the MIT License. namespace Files.App.ViewModels.Properties { diff --git a/src/Files.App/ViewModels/Properties/Items/LibraryProperties.cs b/src/Files.App/ViewModels/Properties/Items/LibraryProperties.cs index ad087f23c621..e21f5b30b833 100644 --- a/src/Files.App/ViewModels/Properties/Items/LibraryProperties.cs +++ b/src/Files.App/ViewModels/Properties/Items/LibraryProperties.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.Extensions.Logging; using Microsoft.UI.Dispatching; @@ -27,7 +27,7 @@ public void UpdateLibrary(LibraryItem library) { Library = library; GetBaseProperties(); - GetSpecialPropertiesAsync(); + _ = GetSpecialPropertiesAsync(); } public override void GetBaseProperties() @@ -46,15 +46,17 @@ public override void GetBaseProperties() public async override Task GetSpecialPropertiesAsync() { - ViewModel.IsReadOnly = Win32Helper.HasFileAttribute(Library.ItemPath, System.IO.FileAttributes.ReadOnly); - ViewModel.IsHidden = Win32Helper.HasFileAttribute(Library.ItemPath, System.IO.FileAttributes.Hidden); + var fileAttributes = Win32Helper.GetFileAttributes(Library.ItemPath); + ViewModel.IsReadOnly = fileAttributes.HasFlag(System.IO.FileAttributes.ReadOnly); + ViewModel.IsHidden = fileAttributes.HasFlag(System.IO.FileAttributes.Hidden); + ViewModel.CanCompressContent = false; var result = await FileThumbnailHelper.GetIconAsync( Library.ItemPath, Constants.ShellIconSizes.ExtraLarge, true, IconOptions.UseCurrentScale); - + if (result is not null) { ViewModel.IconData = result; @@ -68,7 +70,7 @@ public async override Task GetSpecialPropertiesAsync() ViewModel.ItemCreatedTimestampReal = libraryFile.DateCreated; if (libraryFile.Properties is not null) { - GetOtherPropertiesAsync(libraryFile.Properties); + await GetOtherPropertiesAsync(libraryFile.Properties); } } @@ -96,11 +98,11 @@ public async override Task GetSpecialPropertiesAsync() { ViewModel.ContainsFilesOrFolders = true; ViewModel.LocationsCount = storageFolders.Count; - GetLibrarySizeAsync(storageFolders, TokenSource.Token); + await GetLibrarySizeAsync(storageFolders, TokenSource.Token); } else { - ViewModel.FilesAndFoldersCountString = "LibraryNoLocations/Text".GetLocalizedResource(); + ViewModel.FilesAndFoldersCountString = Strings.LibraryNoLocations_Text.GetLocalizedResource(); } } diff --git a/src/Files.App/ViewModels/Properties/MainPropertiesViewModel.cs b/src/Files.App/ViewModels/Properties/MainPropertiesViewModel.cs index 7f74a375cb67..c08207483881 100644 --- a/src/Files.App/ViewModels/Properties/MainPropertiesViewModel.cs +++ b/src/Files.App/ViewModels/Properties/MainPropertiesViewModel.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.Views.Properties; using Microsoft.UI.Windowing; @@ -9,7 +9,7 @@ namespace Files.App.ViewModels.Properties { - public sealed class MainPropertiesViewModel : ObservableObject + public sealed partial class MainPropertiesViewModel : ObservableObject { public CancellationTokenSource ChangedPropertiesCancellationTokenSource { get; } @@ -33,14 +33,15 @@ public NavigationViewItemButtonStyleItem SelectedNavigationViewItem var page = value.ItemType switch { - PropertiesNavigationViewItemType.General => typeof(GeneralPage), - PropertiesNavigationViewItemType.Shortcut => typeof(ShortcutPage), - PropertiesNavigationViewItemType.Library => typeof(LibraryPage), - PropertiesNavigationViewItemType.Details => typeof(DetailsPage), - PropertiesNavigationViewItemType.Security => typeof(SecurityPage), + PropertiesNavigationViewItemType.General => typeof(GeneralPage), + PropertiesNavigationViewItemType.Shortcut => typeof(ShortcutPage), + PropertiesNavigationViewItemType.Library => typeof(LibraryPage), + PropertiesNavigationViewItemType.Details => typeof(DetailsPage), + PropertiesNavigationViewItemType.Security => typeof(SecurityPage), PropertiesNavigationViewItemType.Customization => typeof(CustomizationPage), PropertiesNavigationViewItemType.Compatibility => typeof(CompatibilityPage), PropertiesNavigationViewItemType.Hashes => typeof(HashesPage), + PropertiesNavigationViewItemType.Signatures => typeof(SignaturesPage), _ => typeof(GeneralPage), }; diff --git a/src/Files.App/ViewModels/Properties/SecurityAdvancedViewModel.cs b/src/Files.App/ViewModels/Properties/SecurityAdvancedViewModel.cs index 82e85c16d1c6..367e8b4aaee5 100644 --- a/src/Files.App/ViewModels/Properties/SecurityAdvancedViewModel.cs +++ b/src/Files.App/ViewModels/Properties/SecurityAdvancedViewModel.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml; using Windows.Storage; @@ -7,7 +7,7 @@ namespace Files.App.ViewModels.Properties { - public sealed class SecurityAdvancedViewModel : ObservableObject + public sealed partial class SecurityAdvancedViewModel : ObservableObject { private readonly IStorageSecurityService StorageSecurityService = Ioc.Default.GetRequiredService(); @@ -69,7 +69,7 @@ public AccessControlEntry? SelectedAccessControlEntry if (SetProperty(ref _SelectedAccessControlEntry, value)) { - if(value is not null) + if (value is not null) value.IsSelected = true; OnPropertyChanged(nameof(IsDeleteAccessControlEntryButtonEnabled)); @@ -143,7 +143,8 @@ public SecurityAdvancedViewModel(PropertiesPageNavigationParameter parameter) _path = defaultlistedItem.ItemPath; _isFolder = defaultlistedItem.PrimaryItemAttribute == StorageItemTypes.Folder && !defaultlistedItem.IsShortcut; break; - }; + } + ; LoadShieldIconResource(); @@ -179,15 +180,15 @@ private void LoadAccessControlEntry() if (error is WIN32_ERROR.ERROR_ACCESS_DENIED) { - ErrorMessage = - "SecurityRequireReadPermissions".GetLocalizedResource() + + ErrorMessage = + Strings.SecurityRequireReadPermissions.GetLocalizedResource() + "\r\n\r\n" + - "SecuritySuggestToTakeOwnership".GetLocalizedResource(); + Strings.SecuritySuggestToTakeOwnership.GetLocalizedResource(); } else { ErrorMessage = - "SecurityUnableToDisplayPermissions".GetLocalizedResource() + + Strings.SecurityUnableToDisplayPermissions.GetLocalizedResource() + "\r\n\r\n" + error.ToString(); } diff --git a/src/Files.App/ViewModels/Properties/SecurityViewModel.cs b/src/Files.App/ViewModels/Properties/SecurityViewModel.cs index 9aa333129d12..30da282c7d7d 100644 --- a/src/Files.App/ViewModels/Properties/SecurityViewModel.cs +++ b/src/Files.App/ViewModels/Properties/SecurityViewModel.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using CommunityToolkit.WinUI; using Microsoft.UI.Xaml; @@ -8,7 +8,7 @@ namespace Files.App.ViewModels.Properties { - public sealed class SecurityViewModel : ObservableObject + public sealed partial class SecurityViewModel : ObservableObject { private readonly IStorageSecurityService StorageSecurityService = Ioc.Default.GetRequiredService(); @@ -36,8 +36,8 @@ SelectedAccessControlEntry is not null && public string SelectedItemHeaderText => SelectedAccessControlEntry is null - ? "Permissions".GetLocalizedResource() - : string.Format("SecurityPermissionsHeaderText".GetLocalizedResource(), SelectedAccessControlEntry?.Principal.DisplayName); + ? Strings.Permissions.GetLocalizedResource() + : string.Format(Strings.SecurityPermissionsHeaderText.GetLocalizedResource(), SelectedAccessControlEntry?.Principal.DisplayName); private AccessControlList _AccessControlList; public AccessControlList AccessControlList @@ -89,7 +89,8 @@ public SecurityViewModel(PropertiesPageNavigationParameter parameter) _path = defaultlistedItem.ItemPath; _isFolder = defaultlistedItem.PrimaryItemAttribute == StorageItemTypes.Folder && !defaultlistedItem.IsShortcut; break; - }; + } + ; var error = StorageSecurityService.GetAcl(_path, _isFolder, out _AccessControlList); _SelectedAccessControlEntry = AccessControlList.AccessControlEntries.FirstOrDefault(); @@ -98,8 +99,8 @@ public SecurityViewModel(PropertiesPageNavigationParameter parameter) { DisplayElements = false; ErrorMessage = error is WIN32_ERROR.ERROR_ACCESS_DENIED - ? "SecurityRequireReadPermissions".GetLocalizedResource() + "\r\n" + "SecurityClickAdvancedPermissions".GetLocalizedResource() - : "SecurityUnableToDisplayPermissions".GetLocalizedResource(); + ? Strings.SecurityRequireReadPermissions.GetLocalizedResource() + "\r\n" + Strings.SecurityClickAdvancedPermissions.GetLocalizedResource() + : Strings.SecurityUnableToDisplayPermissions.GetLocalizedResource(); } else { diff --git a/src/Files.App/ViewModels/Properties/SignaturesViewModel.cs b/src/Files.App/ViewModels/Properties/SignaturesViewModel.cs new file mode 100644 index 000000000000..4dc1200a76e0 --- /dev/null +++ b/src/Files.App/ViewModels/Properties/SignaturesViewModel.cs @@ -0,0 +1,37 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Files.App.Utils.Signatures; +using Microsoft.UI.Windowing; +using Windows.Win32.Foundation; + +namespace Files.App.ViewModels.Properties +{ + public sealed partial class SignaturesViewModel : ObservableObject, IDisposable + { + private CancellationTokenSource _cancellationTokenSource; + + public ObservableCollection Signatures { get; set; } + + public bool NoSignatureFound => Signatures.Count == 0; + + public SignaturesViewModel(ListedItem item, AppWindow appWindow) + { + _cancellationTokenSource = new(); + Signatures = new(); + var hWnd = new HWND(Microsoft.UI.Win32Interop.GetWindowFromWindowId(appWindow.Id)); + Signatures.CollectionChanged += (s, e) => OnPropertyChanged(nameof(NoSignatureFound)); + DigitalSignaturesUtil.LoadItemSignatures( + item.ItemPath, + Signatures, + hWnd, + _cancellationTokenSource.Token + ); + } + + public void Dispose() + { + _cancellationTokenSource.Cancel(); + } + } +} diff --git a/src/Files.App/ViewModels/ReleaseNotesViewModel.cs b/src/Files.App/ViewModels/ReleaseNotesViewModel.cs new file mode 100644 index 000000000000..d0f341cee219 --- /dev/null +++ b/src/Files.App/ViewModels/ReleaseNotesViewModel.cs @@ -0,0 +1,15 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.ViewModels +{ + public sealed partial class ReleaseNotesViewModel : ObservableObject + { + public string BlogPostUrl => + Constants.ExternalUrl.ReleaseNotesUrl; + + public ReleaseNotesViewModel() + { + } + } +} diff --git a/src/Files.App/ViewModels/Settings/AboutViewModel.cs b/src/Files.App/ViewModels/Settings/AboutViewModel.cs index e6b490f8b320..d663801d951b 100644 --- a/src/Files.App/ViewModels/Settings/AboutViewModel.cs +++ b/src/Files.App/ViewModels/Settings/AboutViewModel.cs @@ -1,7 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using CommunityToolkit.WinUI.Helpers; using Microsoft.Win32; using System.Windows.Input; using Windows.ApplicationModel; @@ -14,12 +13,17 @@ namespace Files.App.ViewModels.Settings /// /// Represents view model of . /// - public sealed class AboutViewModel : ObservableObject + public sealed partial class AboutViewModel : ObservableObject { + // Dependency injections + + private IGeneralSettingsService GeneralSettingsService { get; } = Ioc.Default.GetRequiredService(); + + // Properties public string Version - => string.Format($"{"SettingsAboutVersionTitle".GetLocalizedResource()} {AppVersion.Major}.{AppVersion.Minor}.{AppVersion.Build}.{AppVersion.Revision}"); + => string.Format($"{Strings.SettingsAboutVersionTitle.GetLocalizedResource()} {AppVersion.Major}.{AppVersion.Minor}.{AppVersion.Build}.{AppVersion.Revision}"); public string AppName => Package.Current.DisplayName; @@ -33,6 +37,7 @@ public PackageVersion AppVersion public ICommand CopyAppVersionCommand { get; } public ICommand CopyWindowsVersionCommand { get; } + public ICommand CopyUserIDCommand { get; } public ICommand SupportUsCommand { get; } public ICommand OpenLogLocationCommand { get; } public ICommand OpenDocumentationCommand { get; } @@ -66,18 +71,20 @@ public AboutViewModel() new ("https://github.com/microsoft/WindowsAppSDK", "WindowsAppSDK"), new ("https://github.com/microsoft/microsoft-ui-xaml", "WinUI 3"), new ("https://github.com/microsoft/Win2D", "Win2D"), - new ("https://github.com/CommunityToolkit/WindowsCommunityToolkit", "Windows Community Toolkit 7.x"), + new ("https://github.com/CommunityToolkit/Windows", "Windows Community Toolkit"), new ("https://github.com/mono/taglib-sharp", "TagLibSharp"), new ("https://github.com/Tulpep/Active-Directory-Object-Picker", "ActiveDirectoryObjectPicker"), - new ("https://github.com/dotMorten/WinUIEx", "WinUIEx"), - new ("https://github.com/dahall/Vanara", "Vanara"), new ("https://github.com/PowerShell/MMI", "MMI"), new ("https://github.com/microsoft/CsWin32", "CsWin32"), new ("https://github.com/microsoft/CsWinRT", "CsWinRT"), + new ("https://github.com/GihanSoft/NaturalStringComparer", "NaturalStringComparer"), + new ("https://github.com/dongle-the-gadget/GuidRVAGen", "Dongle.GuidRVAGen"), + new ("https://github.com/leeqwind/PESignAnalyzer", "PESignAnalyzer"), ]; CopyAppVersionCommand = new RelayCommand(CopyAppVersion); CopyWindowsVersionCommand = new RelayCommand(CopyWindowsVersion); + CopyUserIDCommand = new RelayCommand(CopyUserID); SupportUsCommand = new AsyncRelayCommand(SupportUs); OpenDocumentationCommand = new AsyncRelayCommand(DoOpenDocumentation); OpenDiscordCommand = new AsyncRelayCommand(DoOpenDiscord); @@ -100,7 +107,7 @@ private async Task OpenLogLocation() using var subkey = Registry.ClassesRoot.OpenSubKey(@"Folder\shell\open\command"); var command = (string?)subkey?.GetValue(string.Empty); - // Close the settings dialog if Files is the deault file manager + // Close the settings dialog if Files is the default file manager if (!string.IsNullOrEmpty(command) && command.Contains("Files.App.Launcher.exe")) UIHelpers.CloseAllDialogs(); @@ -164,6 +171,17 @@ public void CopyWindowsVersion() }); } + public void CopyUserID() + { + SafetyExtensions.IgnoreExceptions(() => + { + DataPackage dataPackage = new DataPackage(); + dataPackage.RequestedOperation = DataPackageOperation.Copy; + dataPackage.SetText(GetUserID()); + Clipboard.SetContent(dataPackage); + }); + } + public Task SupportUs() { return Launcher.LaunchUriAsync(new Uri(Constants.ExternalUrl.SupportUsUrl)).AsTask(); @@ -171,12 +189,18 @@ public Task SupportUs() public string GetAppVersion() { - return string.Format($"{AppVersion.Major}.{AppVersion.Minor}.{AppVersion.Build}.{AppVersion.Revision}"); + return $"{AppVersion.Major}.{AppVersion.Minor}.{AppVersion.Build}.{AppVersion.Revision}"; } public string GetWindowsVersion() { - return SystemInformation.Instance.OperatingSystemVersion.ToString(); + ulong v = ulong.Parse(Windows.System.Profile.AnalyticsInfo.VersionInfo.DeviceFamilyVersion); + return $"{(v >> 48) & 0xFFFF}.{(v >> 32) & 0xFFFF}.{(v >> 16) & 0xFFFF}.{v & 0xFFFF}"; + } + + public string GetUserID() + { + return GeneralSettingsService.UserId; } public string GetVersionsQueryString() @@ -184,6 +208,7 @@ public string GetVersionsQueryString() var query = System.Web.HttpUtility.ParseQueryString(string.Empty); query["files_version"] = GetAppVersion(); query["windows_version"] = GetWindowsVersion(); + query["user_id"] = GetUserID(); return query.ToString() ?? string.Empty; } } diff --git a/src/Files.App/ViewModels/Settings/ActionsViewModel.cs b/src/Files.App/ViewModels/Settings/ActionsViewModel.cs index 9bbb5b3e6807..6967086783ea 100644 --- a/src/Files.App/ViewModels/Settings/ActionsViewModel.cs +++ b/src/Files.App/ViewModels/Settings/ActionsViewModel.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Windows.Input; @@ -8,7 +8,7 @@ namespace Files.App.ViewModels.Settings /// /// Represents view model of . /// - public sealed class ActionsViewModel : ObservableObject + public sealed partial class ActionsViewModel : ObservableObject { // Dependency injections @@ -20,6 +20,13 @@ public sealed class ActionsViewModel : ObservableObject public ObservableCollection ValidActionItems { get; } = []; public ObservableCollection AllActionItems { get; } = []; + private ObservableCollection _FilteredActionItems; + public ObservableCollection FilteredActionItems + { + get { return _FilteredActionItems; } + set { SetProperty(ref _FilteredActionItems, value); } + } + private bool _IsResetAllConfirmationTeachingTipOpened; public bool IsResetAllConfirmationTeachingTipOpened { @@ -69,6 +76,8 @@ public ModifiableActionItem? SelectedActionItem set => SetProperty(ref _SelectedActionItem, value); } + private string _currentFilterQuery = string.Empty; + // Commands public ICommand LoadAllActionsCommand { get; set; } @@ -148,6 +157,8 @@ await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => } } }); + + FilteredActionItems = new ObservableCollection(ValidActionItems); } private void ExecuteShowAddNewKeyBindingBlockCommand() @@ -246,6 +257,9 @@ ActionsSettingsService.ActionsV2 is not null // Add to existing list ValidActionItems.Insert(0, selectedNewItem); + + // Refresh filtered list + FilterItems(_currentFilterQuery); } private void ExecuteShowRestoreDefaultsConfirmationCommand() @@ -431,6 +445,28 @@ ActionsSettingsService.ActionsV2 is not null // Exit edit mode item.IsInEditMode = false; ValidActionItems.Remove(item); + + // Refresh filtered list + FilterItems(_currentFilterQuery); + } + + public void FilterItems(string query) + { + // Store the current filter query for later use + _currentFilterQuery = query ?? string.Empty; + + if (string.IsNullOrEmpty(query)) + { + FilteredActionItems = new ObservableCollection(ValidActionItems); + } + else + { + FilteredActionItems = new ObservableCollection( + ValidActionItems.Where(item => + item.CommandLabel.Contains(query, StringComparison.OrdinalIgnoreCase) || + item.CommandDescription.Contains(query, StringComparison.OrdinalIgnoreCase)) + ); + } } } -} +} \ No newline at end of file diff --git a/src/Files.App/ViewModels/Settings/AdvancedViewModel.cs b/src/Files.App/ViewModels/Settings/AdvancedViewModel.cs index 4b37eb66c8fd..93c5a9ffaf86 100644 --- a/src/Files.App/ViewModels/Settings/AdvancedViewModel.cs +++ b/src/Files.App/ViewModels/Settings/AdvancedViewModel.cs @@ -1,23 +1,25 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.Extensions.Logging; using Microsoft.Win32; using SevenZip; using System.IO; +using System.Runtime.InteropServices; using System.Text; using System.Windows.Input; using Windows.ApplicationModel; using Windows.Storage; using Windows.Storage.Pickers; -using Windows.System; +using Windows.Win32.Storage.FileSystem; namespace Files.App.ViewModels.Settings { - public sealed class AdvancedViewModel : ObservableObject + public sealed partial class AdvancedViewModel : ObservableObject { private IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetRequiredService(); private ICommonDialogService CommonDialogService { get; } = Ioc.Default.GetRequiredService(); + public ICommandManager Commands { get; } = Ioc.Default.GetRequiredService(); private readonly IFileTagsSettingsService fileTagsSettingsService = Ioc.Default.GetRequiredService(); @@ -25,7 +27,6 @@ public sealed class AdvancedViewModel : ObservableObject public ICommand SetAsOpenFileDialogCommand { get; } public ICommand ExportSettingsCommand { get; } public ICommand ImportSettingsCommand { get; } - public ICommand OpenSettingsJsonCommand { get; } public AsyncRelayCommand OpenFilesOnWindowsStartupCommand { get; } @@ -38,22 +39,11 @@ public AdvancedViewModel() SetAsOpenFileDialogCommand = new AsyncRelayCommand(SetAsOpenFileDialogAsync); ExportSettingsCommand = new AsyncRelayCommand(ExportSettingsAsync); ImportSettingsCommand = new AsyncRelayCommand(ImportSettingsAsync); - OpenSettingsJsonCommand = new AsyncRelayCommand(OpenSettingsJsonAsync); OpenFilesOnWindowsStartupCommand = new AsyncRelayCommand(OpenFilesOnWindowsStartupAsync); _ = DetectOpenFilesAtStartupAsync(); } - private async Task OpenSettingsJsonAsync() - { - await SafetyExtensions.IgnoreExceptions(async () => - { - var settingsJsonFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appdata:///local/settings/user_settings.json")); - if (!await Launcher.LaunchFileAsync(settingsJsonFile)) - await ContextMenu.InvokeVerb("open", settingsJsonFile.Path); - }); - } - private async Task SetAsDefaultExplorerAsync() { // Make sure IsSetAsDefaultFileManager is updated @@ -65,7 +55,7 @@ private async Task SetAsDefaultExplorerAsync() var destFolder = Path.Combine(ApplicationData.Current.LocalFolder.Path, "FilesOpenDialog"); Directory.CreateDirectory(destFolder); - foreach (var file in Directory.GetFiles(Path.Combine(Package.Current.InstalledLocation.Path, "Files.App", "Assets", "FilesOpenDialog"))) + foreach (var file in Directory.GetFiles(Path.Combine(Package.Current.InstalledLocation.Path, "Assets", "FilesOpenDialog"))) { if (!SafetyExtensions.IgnoreExceptions(() => File.Copy(file, Path.Combine(destFolder, Path.GetFileName(file)), true), App.Logger)) { @@ -78,7 +68,7 @@ private async Task SetAsDefaultExplorerAsync() var dataPath = Environment.ExpandEnvironmentVariables("%LocalAppData%\\Files"); if (IsSetAsDefaultFileManager) { - if (!await Win32Helper.RunPowershellCommandAsync($"-command \"New-Item -Force -Path '{dataPath}' -ItemType Directory; Copy-Item -Filter *.* -Path '{destFolder}\\*' -Recurse -Force -Destination '{dataPath}'\"", false)) + if (!await Win32Helper.RunPowershellCommandAsync($"-command \"New-Item -Force -Path '{dataPath}' -ItemType Directory; Copy-Item -Filter *.* -Path '{destFolder}\\*' -Recurse -Force -Destination '{dataPath}'; 'files-dev' | Out-File -Encoding utf8 -Force -FilePath '{dataPath}\\Branch.txt'\"", PowerShellExecutionOptions.Hidden)) { // Error copying files await DetectResult(); @@ -87,7 +77,7 @@ private async Task SetAsDefaultExplorerAsync() } else { - await Win32Helper.RunPowershellCommandAsync($"-command \"Remove-Item -Path '{dataPath}' -Recurse -Force\"", false); + await Win32Helper.RunPowershellCommandAsync($"-command \"Remove-Item -Path '{dataPath}' -Recurse -Force\"", PowerShellExecutionOptions.Hidden); } try @@ -125,7 +115,7 @@ private async Task SetAsOpenFileDialogAsync() var destFolder = Path.Combine(ApplicationData.Current.LocalFolder.Path, "FilesOpenDialog"); Directory.CreateDirectory(destFolder); - foreach (var file in Directory.GetFiles(Path.Combine(Package.Current.InstalledLocation.Path, "Files.App", "Assets", "FilesOpenDialog"))) + foreach (var file in Directory.GetFiles(Path.Combine(Package.Current.InstalledLocation.Path, "Assets", "FilesOpenDialog"))) { if (!SafetyExtensions.IgnoreExceptions(() => File.Copy(file, Path.Combine(destFolder, Path.GetFileName(file)), true), App.Logger)) { @@ -159,8 +149,10 @@ private async Task SetAsOpenFileDialogAsync() private async Task ImportSettingsAsync() { - string[] extensions = ["ZipFileCapitalized".GetLocalizedResource(), "*.zip"]; - CommonDialogService.Open_FileOpenDialog(MainWindow.Instance.WindowHandle, false, extensions, Environment.SpecialFolder.Desktop, out var filePath); + string[] extensions = [Strings.ZipFileCapitalized.GetLocalizedResource(), "*.zip"]; + bool result = CommonDialogService.Open_FileOpenDialog(MainWindow.Instance.WindowHandle, false, extensions, Environment.SpecialFolder.Desktop, out var filePath); + if (!result) + return; try { @@ -197,14 +189,16 @@ private async Task ImportSettingsAsync() { App.Logger.LogWarning(ex, "Error importing settings"); UIHelpers.CloseAllDialogs(); - await DialogDisplayHelper.ShowDialogAsync("SettingsImportErrorTitle".GetLocalizedResource(), "SettingsImportErrorDescription".GetLocalizedResource()); + await DialogDisplayHelper.ShowDialogAsync(Strings.SettingsImportErrorTitle.GetLocalizedResource(), Strings.SettingsImportErrorDescription.GetLocalizedResource()); } } private async Task ExportSettingsAsync() { - string[] extensions = ["ZipFileCapitalized".GetLocalizedResource(), "*.zip" ]; - CommonDialogService.Open_FileSaveDialog(MainWindow.Instance.WindowHandle, false, extensions, Environment.SpecialFolder.Desktop, out var filePath); + string[] extensions = [Strings.ZipFileCapitalized.GetLocalizedResource(), "*.zip"]; + bool result = CommonDialogService.Open_FileSaveDialog(MainWindow.Instance.WindowHandle, false, extensions, Environment.SpecialFolder.Desktop, out var filePath); + if (!result) + return; if (!filePath.EndsWith(".zip", StringComparison.OrdinalIgnoreCase)) filePath += ".zip"; @@ -213,7 +207,7 @@ private async Task ExportSettingsAsync() { var handle = Win32PInvoke.CreateFileFromAppW( filePath, - Win32PInvoke.GENERIC_READ | Win32PInvoke.GENERIC_WRITE, + (uint)(FILE_ACCESS_RIGHTS.FILE_GENERIC_READ | FILE_ACCESS_RIGHTS.FILE_GENERIC_WRITE), Win32PInvoke.FILE_SHARE_READ | Win32PInvoke.FILE_SHARE_WRITE, nint.Zero, Win32PInvoke.CREATE_NEW, @@ -287,7 +281,7 @@ public bool IsSetAsOpenFileDialog set => SetProperty(ref isSetAsOpenFileDialog, value); } - public bool CanShowSetAsOpenFileDialog + public bool IsAppEnvironmentDev { get => AppLifecycleHelper.AppEnvironment is AppEnvironment.Dev; } @@ -334,6 +328,34 @@ public bool LeaveAppRunning } } + public bool ShowSystemTrayIcon + { + get => UserSettingsService.GeneralSettingsService.ShowSystemTrayIcon; + set + { + if (value != UserSettingsService.GeneralSettingsService.ShowSystemTrayIcon) + { + UserSettingsService.GeneralSettingsService.ShowSystemTrayIcon = value; + + OnPropertyChanged(); + } + } + } + + // TODO remove when feature is marked as stable + public bool ShowFlattenOptions + { + get => UserSettingsService.GeneralSettingsService.ShowFlattenOptions; + set + { + if (value == UserSettingsService.GeneralSettingsService.ShowFlattenOptions) + return; + + UserSettingsService.GeneralSettingsService.ShowFlattenOptions = value; + OnPropertyChanged(); + } + } + public async Task OpenFilesOnWindowsStartupAsync() { var stateMode = await ReadState(); @@ -349,12 +371,19 @@ public async Task OpenFilesOnWindowsStartupAsync() if (state != OpenOnWindowsStartup) { - StartupTask startupTask = await StartupTask.GetAsync("3AA55462-A5FA-4933-88C4-712D0B6CDEBB"); - if (OpenOnWindowsStartup) - await startupTask.RequestEnableAsync(); - else - startupTask.Disable(); - await DetectOpenFilesAtStartupAsync(); + try + { + StartupTask startupTask = await StartupTask.GetAsync("3AA55462-A5FA-4933-88C4-712D0B6CDEBB"); + if (OpenOnWindowsStartup) + await startupTask.RequestEnableAsync(); + else + startupTask.Disable(); + await DetectOpenFilesAtStartupAsync(); + } + catch (COMException ex) + { + App.Logger?.LogWarning(ex, "RPC server unavailable, returning default state"); + } } } @@ -389,8 +418,16 @@ public async Task DetectOpenFilesAtStartupAsync() public async Task ReadState() { - var state = await StartupTask.GetAsync("3AA55462-A5FA-4933-88C4-712D0B6CDEBB"); - return state.State; + try + { + var state = await StartupTask.GetAsync("3AA55462-A5FA-4933-88C4-712D0B6CDEBB"); + return state.State; + } + catch (COMException ex) + { + App.Logger?.LogWarning(ex, "RPC server unavailable, returning default state"); + return StartupTaskState.Disabled; + } } } -} +} \ No newline at end of file diff --git a/src/Files.App/ViewModels/Settings/AppearanceViewModel.cs b/src/Files.App/ViewModels/Settings/AppearanceViewModel.cs index 3232123500d6..b88bcb3edcb7 100644 --- a/src/Files.App/ViewModels/Settings/AppearanceViewModel.cs +++ b/src/Files.App/ViewModels/Settings/AppearanceViewModel.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using CommunityToolkit.WinUI.Helpers; using Microsoft.UI.Xaml; @@ -8,7 +8,7 @@ namespace Files.App.ViewModels.Settings { - public sealed class AppearanceViewModel : ObservableObject + public sealed partial class AppearanceViewModel : ObservableObject { private IAppThemeModeService AppThemeModeService { get; } = Ioc.Default.GetRequiredService(); private ICommonDialogService CommonDialogService { get; } = Ioc.Default.GetRequiredService(); @@ -24,6 +24,8 @@ public sealed class AppearanceViewModel : ObservableObject public Dictionary ImageHorizontalAlignmentTypes { get; private set; } = []; + public Dictionary StatusCenterVisibilityOptions { get; private set; } = []; + public ObservableCollection AppThemeResources { get; } public ICommand SelectImageCommand { get; } @@ -37,16 +39,16 @@ public AppearanceViewModel(IUserSettingsService userSettingsService, IResourcesS Themes = [ - "Default".GetLocalizedResource(), - "LightTheme".GetLocalizedResource(), - "DarkTheme".GetLocalizedResource() + Strings.UseSystemSetting.GetLocalizedResource(), + Strings.LightTheme.GetLocalizedResource(), + Strings.DarkTheme.GetLocalizedResource() ]; - BackdropMaterialTypes.Add(BackdropMaterialType.Solid, "None".GetLocalizedResource()); - BackdropMaterialTypes.Add(BackdropMaterialType.Acrylic, "Acrylic".GetLocalizedResource()); - BackdropMaterialTypes.Add(BackdropMaterialType.ThinAcrylic, "ThinAcrylic".GetLocalizedResource()); - BackdropMaterialTypes.Add(BackdropMaterialType.Mica, "Mica".GetLocalizedResource()); - BackdropMaterialTypes.Add(BackdropMaterialType.MicaAlt, "MicaAlt".GetLocalizedResource()); + BackdropMaterialTypes.Add(BackdropMaterialType.Solid, Strings.None.GetLocalizedResource()); + BackdropMaterialTypes.Add(BackdropMaterialType.Acrylic, Strings.Acrylic.GetLocalizedResource()); + BackdropMaterialTypes.Add(BackdropMaterialType.ThinAcrylic, Strings.ThinAcrylic.GetLocalizedResource()); + BackdropMaterialTypes.Add(BackdropMaterialType.Mica, Strings.Mica.GetLocalizedResource()); + BackdropMaterialTypes.Add(BackdropMaterialType.MicaAlt, Strings.MicaAlt.GetLocalizedResource()); selectedBackdropMaterial = BackdropMaterialTypes[UserSettingsService.AppearanceSettingsService.AppThemeBackdropMaterial]; @@ -54,28 +56,33 @@ public AppearanceViewModel(IUserSettingsService userSettingsService, IResourcesS // Background image fit options - ImageStretchTypes.Add(Stretch.None, "None".GetLocalizedResource()); - ImageStretchTypes.Add(Stretch.Fill, "Fill".GetLocalizedResource()); - ImageStretchTypes.Add(Stretch.Uniform, "Uniform".GetLocalizedResource()); - ImageStretchTypes.Add(Stretch.UniformToFill, "UniformToFill".GetLocalizedResource()); + ImageStretchTypes.Add(Stretch.None, Strings.None.GetLocalizedResource()); + ImageStretchTypes.Add(Stretch.Fill, Strings.Fill.GetLocalizedResource()); + ImageStretchTypes.Add(Stretch.Uniform, Strings.Uniform.GetLocalizedResource()); + ImageStretchTypes.Add(Stretch.UniformToFill, Strings.UniformToFill.GetLocalizedResource()); SelectedImageStretchType = ImageStretchTypes[UserSettingsService.AppearanceSettingsService.AppThemeBackgroundImageFit]; // Background image allignment options // VerticalAlignment - ImageVerticalAlignmentTypes.Add(VerticalAlignment.Top, "Top".GetLocalizedResource()); - ImageVerticalAlignmentTypes.Add(VerticalAlignment.Center, "Center".GetLocalizedResource()); - ImageVerticalAlignmentTypes.Add(VerticalAlignment.Bottom, "Bottom".GetLocalizedResource()); + ImageVerticalAlignmentTypes.Add(VerticalAlignment.Top, Strings.Top.GetLocalizedResource()); + ImageVerticalAlignmentTypes.Add(VerticalAlignment.Center, Strings.Center.GetLocalizedResource()); + ImageVerticalAlignmentTypes.Add(VerticalAlignment.Bottom, Strings.Bottom.GetLocalizedResource()); SelectedImageVerticalAlignmentType = ImageVerticalAlignmentTypes[UserSettingsService.AppearanceSettingsService.AppThemeBackgroundImageVerticalAlignment]; // HorizontalAlignment - ImageHorizontalAlignmentTypes.Add(HorizontalAlignment.Left, "Left".GetLocalizedResource()); - ImageHorizontalAlignmentTypes.Add(HorizontalAlignment.Center, "Center".GetLocalizedResource()); - ImageHorizontalAlignmentTypes.Add(HorizontalAlignment.Right, "Right".GetLocalizedResource()); + ImageHorizontalAlignmentTypes.Add(HorizontalAlignment.Left, Strings.Left.GetLocalizedResource()); + ImageHorizontalAlignmentTypes.Add(HorizontalAlignment.Center, Strings.Center.GetLocalizedResource()); + ImageHorizontalAlignmentTypes.Add(HorizontalAlignment.Right, Strings.Right.GetLocalizedResource()); SelectedImageHorizontalAlignmentType = ImageHorizontalAlignmentTypes[UserSettingsService.AppearanceSettingsService.AppThemeBackgroundImageHorizontalAlignment]; UpdateSelectedResource(); + // StatusCenterVisibility + StatusCenterVisibilityOptions.Add(StatusCenterVisibility.Always, Strings.Always.GetLocalizedResource()); + StatusCenterVisibilityOptions.Add(StatusCenterVisibility.DuringOngoingFileOperations, Strings.DuringOngoingFileOperations.GetLocalizedResource()); + SelectedStatusCenterVisibilityOption = StatusCenterVisibilityOptions[UserSettingsService.AppearanceSettingsService.StatusCenterVisibility]; + SelectImageCommand = new RelayCommand(SelectBackgroundImage); RemoveImageCommand = new RelayCommand(RemoveBackgroundImage); } @@ -87,7 +94,8 @@ private void SelectBackgroundImage() { string[] extensions = [ - "BitmapFiles".GetLocalizedResource(), "*.bmp;*.dib", + Strings.ImageFiles.GetLocalizedResource(), "*.bmp;*.dib;*.jpg;*.jpeg;*.jpe;*.jfif;*.gif;*.tif;*.tiff;*.png;*.heic;*.hif;*.webp", + Strings.BitmapFiles.GetLocalizedResource(), "*.bmp;*.dib", "JPEG", "*.jpg;*.jpeg;*.jpe;*.jfif", "GIF", "*.gif", "TIFF", "*.tif;*.tiff", @@ -120,13 +128,13 @@ private void UpdateSelectedResource() if (!AppThemeResources.Any(p => p.BackgroundColor == themeBackgroundColor)) { // Remove current value before adding a new one - if (AppThemeResources.Last().Name == "Custom".GetLocalizedResource()) + if (AppThemeResources.Last().Name == Strings.Custom.GetLocalizedResource()) AppThemeResources.Remove(AppThemeResources.Last()); var appThemeBackgroundColor = new AppThemeResourceItem { BackgroundColor = themeBackgroundColor, - Name = "Custom".GetLocalizedResource(), + Name = Strings.Custom.GetLocalizedResource(), }; AppThemeResources.Add(appThemeBackgroundColor); @@ -181,11 +189,11 @@ public string AppThemeBackgroundColor // Apply the updated background resource try { - ResourcesService.SetAppThemeBackgroundColor(ColorHelper.ToColor(value).FromWindowsColor()); + ResourcesService.SetAppThemeBackgroundColor(value.ToColor()); } catch { - ResourcesService.SetAppThemeBackgroundColor(ColorHelper.ToColor("#00000000").FromWindowsColor()); + ResourcesService.SetAppThemeBackgroundColor("#00000000".ToColor()); } ResourcesService.ApplyResources(); @@ -288,6 +296,20 @@ public bool ShowToolbar } } + public bool ShowStatusBar + { + get => UserSettingsService.AppearanceSettingsService.ShowStatusBar; + set + { + if (value != UserSettingsService.AppearanceSettingsService.ShowStatusBar) + { + UserSettingsService.AppearanceSettingsService.ShowStatusBar = value; + + OnPropertyChanged(); + } + } + } + public bool ShowTabActions { get => UserSettingsService.AppearanceSettingsService.ShowTabActions; @@ -301,5 +323,37 @@ public bool ShowTabActions } } } + + public bool ShowShelfPaneToggleButton + { + get => UserSettingsService.AppearanceSettingsService.ShowShelfPaneToggleButton; + set + { + if (value != UserSettingsService.AppearanceSettingsService.ShowShelfPaneToggleButton) + { + UserSettingsService.AppearanceSettingsService.ShowShelfPaneToggleButton = value; + + OnPropertyChanged(); + } + } + } + + private string selectedStatusCenterVisibilityOption; + public string SelectedStatusCenterVisibilityOption + { + get => selectedStatusCenterVisibilityOption; + set + { + if (SetProperty(ref selectedStatusCenterVisibilityOption, value)) + { + UserSettingsService.AppearanceSettingsService.StatusCenterVisibility = StatusCenterVisibilityOptions.First(e => e.Value == value).Key; + } + } + } + + public bool IsAppEnvironmentDev + { + get => AppLifecycleHelper.AppEnvironment is AppEnvironment.Dev; + } } } diff --git a/src/Files.App/ViewModels/Settings/DevToolsViewModel.cs b/src/Files.App/ViewModels/Settings/DevToolsViewModel.cs index 809509da5a6a..75fda57a8da9 100644 --- a/src/Files.App/ViewModels/Settings/DevToolsViewModel.cs +++ b/src/Files.App/ViewModels/Settings/DevToolsViewModel.cs @@ -1,18 +1,25 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. +using System.IO; using System.Windows.Input; namespace Files.App.ViewModels.Settings { - public sealed class DevToolsViewModel : ObservableObject + public sealed partial class DevToolsViewModel : ObservableObject { - protected readonly IFileTagsSettingsService FileTagsSettingsService = Ioc.Default.GetRequiredService(); - protected readonly IDevToolsSettingsService DevToolsSettingsService = Ioc.Default.GetRequiredService(); + private readonly IFileTagsSettingsService FileTagsSettingsService = Ioc.Default.GetRequiredService(); + private readonly IDevToolsSettingsService DevToolsSettingsService = Ioc.Default.GetRequiredService(); + private readonly ICommonDialogService CommonDialogService = Ioc.Default.GetRequiredService(); public Dictionary OpenInIDEOptions { get; private set; } = []; public ICommand RemoveCredentialsCommand { get; } public ICommand ConnectToGitHubCommand { get; } + public ICommand StartEditingIDECommand { get; } + public ICommand CancelIDEChangesCommand { get; } + public ICommand SaveIDEChangesCommand { get; } + public ICommand OpenFilePickerForIDECommand { get; } + public ICommand TestIDECommand { get; } // Enabled when there are saved credentials private bool _IsLogoutEnabled; @@ -22,17 +29,84 @@ public bool IsLogoutEnabled set => SetProperty(ref _IsLogoutEnabled, value); } + private bool _IsEditingIDEConfig; + public bool IsEditingIDEConfig + { + get => _IsEditingIDEConfig; + set => SetProperty(ref _IsEditingIDEConfig, value); + } + + public bool CanSaveIDEChanges => + IsIDENameValid && IsIDEPathValid; + + private bool _IsIDEPathValid; + public bool IsIDEPathValid + { + get => _IsIDEPathValid; + set => SetProperty(ref _IsIDEPathValid, value); + } + + private bool _IsIDENameValid; + public bool IsIDENameValid + { + get => _IsIDENameValid; + set => SetProperty(ref _IsIDENameValid, value); + } + + private string _IDEPath; + public string IDEPath + { + get => _IDEPath; + set + { + if (SetProperty(ref _IDEPath, value)) + { + IsIDEPathValid = + !string.IsNullOrWhiteSpace(value) && + !value.Contains('\"') && + !value.Contains('\'') && + CheckPathExists(); + + OnPropertyChanged(nameof(CanSaveIDEChanges)); + } + } + } + + private string _IDEName; + public string IDEName + { + get => _IDEName; + set + { + if (SetProperty(ref _IDEName, value)) + { + IsIDENameValid = !string.IsNullOrEmpty(value); + OnPropertyChanged(nameof(CanSaveIDEChanges)); + } + } + } + public DevToolsViewModel() { // Open in IDE options - OpenInIDEOptions.Add(OpenInIDEOption.GitRepos, "GitRepos".GetLocalizedResource()); - OpenInIDEOptions.Add(OpenInIDEOption.AllLocations, "AllLocations".GetLocalizedResource()); + OpenInIDEOptions.Add(OpenInIDEOption.GitRepos, Strings.GitRepos.GetLocalizedResource()); + OpenInIDEOptions.Add(OpenInIDEOption.AllLocations, Strings.AllLocations.GetLocalizedResource()); SelectedOpenInIDEOption = OpenInIDEOptions[DevToolsSettingsService.OpenInIDEOption]; + IDEPath = DevToolsSettingsService.IDEPath; + IDEName = DevToolsSettingsService.IDEName; + IsIDEPathValid = true; + IsIDENameValid = true; + IsLogoutEnabled = GitHelpers.GetSavedCredentials() != string.Empty; RemoveCredentialsCommand = new RelayCommand(DoRemoveCredentials); ConnectToGitHubCommand = new RelayCommand(DoConnectToGitHubAsync); + CancelIDEChangesCommand = new RelayCommand(DoCancelIDEChanges); + SaveIDEChangesCommand = new RelayCommand(DoSaveIDEChanges); + StartEditingIDECommand = new RelayCommand(DoStartEditingIDE); + OpenFilePickerForIDECommand = new RelayCommand(DoOpenFilePickerForIDE); + TestIDECommand = new RelayCommand(DoTestIDE); } private string selectedOpenInIDEOption; @@ -62,5 +136,65 @@ public async void DoConnectToGitHubAsync() await GitHelpers.RequireGitAuthenticationAsync(); } + + private void DoCancelIDEChanges() + { + IsEditingIDEConfig = false; + IDEPath = DevToolsSettingsService.IDEPath; + IDEName = DevToolsSettingsService.IDEName; + IsIDEPathValid = true; + IsIDENameValid = true; + } + + private void DoSaveIDEChanges() + { + IsEditingIDEConfig = false; + IsIDEPathValid = true; + IsIDENameValid = true; + DevToolsSettingsService.IDEPath = IDEPath; + DevToolsSettingsService.IDEName = IDEName; + } + + private void DoStartEditingIDE() + { + IsEditingIDEConfig = true; + } + + private void DoOpenFilePickerForIDE() + { + var res = CommonDialogService.Open_FileOpenDialog( + MainWindow.Instance.WindowHandle, + false, + ["*.exe;*.bat;*.cmd;*.ahk"], + Environment.SpecialFolder.ProgramFiles, + out var filePath + ); + + if (res) + IDEPath = filePath; + } + + private async void DoTestIDE() + { + IsIDEPathValid = await Win32Helper.RunPowershellCommandAsync( + $"& \'{IDEPath}\'", + PowerShellExecutionOptions.Hidden + ); + } + + private bool CheckPathExists() + { + if (Path.Exists(IDEPath)) + return true; + + var paths = Environment.GetEnvironmentVariable("PATH")?.Split(';'); + foreach (var path in paths ?? Array.Empty()) + { + if (Path.Exists(Path.Combine(path, IDEPath))) + return true; + } + + return false; + } } } diff --git a/src/Files.App/ViewModels/Settings/FoldersViewModel.cs b/src/Files.App/ViewModels/Settings/FoldersViewModel.cs index b610a287ccfe..b8df7b7b45fd 100644 --- a/src/Files.App/ViewModels/Settings/FoldersViewModel.cs +++ b/src/Files.App/ViewModels/Settings/FoldersViewModel.cs @@ -1,17 +1,29 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.ViewModels.Settings { - public sealed class FoldersViewModel : ObservableObject + public sealed partial class FoldersViewModel : ObservableObject { private IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetRequiredService(); + public Dictionary SizeUnitsOptions { get; private set; } = []; + public Dictionary OpenFoldersWithOneClickOptions { get; private set; } = []; public FoldersViewModel() { SelectedDeleteConfirmationPolicyIndex = (int)DeleteConfirmationPolicy; + + // Size unit format + SizeUnitsOptions.Add(SizeUnitTypes.BinaryUnits, Strings.Binary.GetLocalizedResource()); + SizeUnitsOptions.Add(SizeUnitTypes.DecimalUnits, Strings.Decimal.GetLocalizedResource()); + SizeUnitFormat = SizeUnitsOptions[UserSettingsService.FoldersSettingsService.SizeUnitFormat]; + + OpenFoldersWithOneClickOptions.Add(OpenFoldersWithOneClickEnum.OnlyInColumnsView, Strings.OnlyInColumnsView.GetLocalizedResource()); + OpenFoldersWithOneClickOptions.Add(OpenFoldersWithOneClickEnum.Always, Strings.Always.GetLocalizedResource()); + OpenFoldersWithOneClickOptions.Add(OpenFoldersWithOneClickEnum.Never, Strings.Never.GetLocalizedResource()); + SelectedOpenFoldersWithOneClickOption = OpenFoldersWithOneClickOptions[UserSettingsService.FoldersSettingsService.OpenFoldersWithOneClick]; } // Properties @@ -102,16 +114,15 @@ public bool OpenItemsWithOneClick } } - public bool ColumnLayoutOpenFoldersWithOneClick + private string selectedOpenFoldersWithOneClickOption; + public string SelectedOpenFoldersWithOneClickOption { - get => UserSettingsService.FoldersSettingsService.ColumnLayoutOpenFoldersWithOneClick; + get => selectedOpenFoldersWithOneClickOption; set { - if (value != UserSettingsService.FoldersSettingsService.ColumnLayoutOpenFoldersWithOneClick) + if (SetProperty(ref selectedOpenFoldersWithOneClickOption, value)) { - UserSettingsService.FoldersSettingsService.ColumnLayoutOpenFoldersWithOneClick = value; - - OnPropertyChanged(); + UserSettingsService.FoldersSettingsService.OpenFoldersWithOneClick = OpenFoldersWithOneClickOptions.First(e => e.Value == value).Key; } } } @@ -255,5 +266,18 @@ public bool ShowCheckboxesWhenSelectingItems } } } + + private string sizeUnitFormat; + public string SizeUnitFormat + { + get => sizeUnitFormat; + set + { + if (SetProperty(ref sizeUnitFormat, value)) + { + UserSettingsService.FoldersSettingsService.SizeUnitFormat = SizeUnitsOptions.First(e => e.Value == value).Key; + } + } + } } } diff --git a/src/Files.App/ViewModels/Settings/GeneralViewModel.cs b/src/Files.App/ViewModels/Settings/GeneralViewModel.cs index f82c9308d9ef..5e80e7c88c92 100644 --- a/src/Files.App/ViewModels/Settings/GeneralViewModel.cs +++ b/src/Files.App/ViewModels/Settings/GeneralViewModel.cs @@ -1,18 +1,15 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Collections.Specialized; -using System.Globalization; -using Windows.Globalization; using Windows.Storage; -using Windows.Storage.Pickers; using Windows.System; using static Files.App.Helpers.MenuFlyoutHelper; using DispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue; namespace Files.App.ViewModels.Settings { - public sealed class GeneralViewModel : ObservableObject, IDisposable + public sealed partial class GeneralViewModel : ObservableObject, IDisposable { private IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetRequiredService(); private ICommonDialogService CommonDialogService { get; } = Ioc.Default.GetRequiredService(); @@ -110,8 +107,8 @@ public GeneralViewModel() PagesOnStartupList.CollectionChanged += PagesOnStartupList_CollectionChanged; // ShellPaneArrangement - ShellPaneArrangementTypes.Add(ShellPaneArrangement.Horizontal, "Horizontal".GetLocalizedResource()); - ShellPaneArrangementTypes.Add(ShellPaneArrangement.Vertical, "Vertical".GetLocalizedResource()); + ShellPaneArrangementTypes.Add(ShellPaneArrangement.Vertical, Strings.Vertical.GetLocalizedResource()); + ShellPaneArrangementTypes.Add(ShellPaneArrangement.Horizontal, Strings.Horizontal.GetLocalizedResource()); SelectedShellPaneArrangementType = ShellPaneArrangementTypes[UserSettingsService.GeneralSettingsService.ShellPaneArrangementOption]; InitStartupSettingsRecentFoldersFlyout(); @@ -126,7 +123,7 @@ private async void DoRestartAsync() AppLifecycleHelper.SaveSessionTabs(); // Launches a new instance of Files - await Launcher.LaunchUriAsync(new Uri("files-uwp:")); + await Launcher.LaunchUriAsync(new Uri("files-dev:")); // Closes the current instance Process.GetCurrentProcess().Kill(); @@ -149,14 +146,14 @@ private void AddDateTimeOptions() private void InitStartupSettingsRecentFoldersFlyout() { - var recentsItem = new MenuFlyoutSubItemViewModel("JumpListRecentGroupHeader".GetLocalizedResource()); - recentsItem.Items.Add(new MenuFlyoutItemViewModel("Home".GetLocalizedResource()) + var recentsItem = new MenuFlyoutSubItemViewModel(Strings.JumpListRecentGroupHeader.GetLocalizedResource()); + recentsItem.Items.Add(new MenuFlyoutItemViewModel(Strings.Home.GetLocalizedResource()) { Command = AddPageCommand, CommandParameter = "Home", - Tooltip = "Home".GetLocalizedResource() + Tooltip = Strings.Home.GetLocalizedResource() }); - recentsItem.Items.Add(new MenuFlyoutItemViewModel("Browse".GetLocalizedResource()) { Command = AddPageCommand }); + recentsItem.Items.Add(new MenuFlyoutItemViewModel(Strings.Browse.GetLocalizedResource()) { Command = AddPageCommand }); } private void PagesOnStartupList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) @@ -278,6 +275,20 @@ public bool ShowCopyPath } } + public bool ShowCreateAlternateDataStream + { + get => UserSettingsService.GeneralSettingsService.ShowCreateAlternateDataStream; + set + { + if (value != UserSettingsService.GeneralSettingsService.ShowCreateAlternateDataStream) + { + UserSettingsService.GeneralSettingsService.ShowCreateAlternateDataStream = value; + + OnPropertyChanged(); + } + } + } + public bool ShowCreateShortcut { get => UserSettingsService.GeneralSettingsService.ShowCreateShortcut; @@ -306,6 +317,20 @@ public bool AlwaysOpenDualPaneInNewTab } } + public bool AlwaysSwitchToNewlyOpenedTab + { + get => UserSettingsService.GeneralSettingsService.AlwaysSwitchToNewlyOpenedTab; + set + { + if (value != UserSettingsService.GeneralSettingsService.AlwaysSwitchToNewlyOpenedTab) + { + UserSettingsService.GeneralSettingsService.AlwaysSwitchToNewlyOpenedTab = value; + + OnPropertyChanged(); + } + } + } + private void ChangePageAsync() { var result = CommonDialogService.Open_FileOpenDialog(MainWindow.Instance.WindowHandle, true, [], Environment.SpecialFolder.Desktop, out var filePath); @@ -322,7 +347,9 @@ private async Task AddPageAsync(string path = null) { if (string.IsNullOrWhiteSpace(path)) { - CommonDialogService.Open_FileOpenDialog(MainWindow.Instance.WindowHandle, true, [], Environment.SpecialFolder.Desktop, out var filePath); + bool result = CommonDialogService.Open_FileOpenDialog(MainWindow.Instance.WindowHandle, true, [], Environment.SpecialFolder.Desktop, out var filePath); + if (!result) + return; path = filePath; } @@ -332,7 +359,7 @@ private async Task AddPageAsync(string path = null) } public string DateFormatSample - => string.Format("DateFormatSample".GetLocalizedResource(), DateFormats[SelectedDateTimeFormatIndex].Sample1, DateFormats[SelectedDateTimeFormatIndex].Sample2); + => string.Format(Strings.DateFormatSample.GetLocalizedResource(), DateFormats[SelectedDateTimeFormatIndex].Sample1, DateFormats[SelectedDateTimeFormatIndex].Sample2); private DispatcherQueue dispatcherQueue; @@ -415,6 +442,32 @@ public bool MoveShellExtensionsToSubMenu } } + public bool ShowPinToSideBar + { + get => UserSettingsService.GeneralSettingsService.ShowPinToSideBar; + set + { + if (value != UserSettingsService.GeneralSettingsService.ShowPinToSideBar) + { + UserSettingsService.GeneralSettingsService.ShowPinToSideBar = value; + OnPropertyChanged(); + } + } + } + + public bool ShowPinToStart + { + get => UserSettingsService.GeneralSettingsService.ShowPinToStart; + set + { + if (value != UserSettingsService.GeneralSettingsService.ShowPinToStart) + { + UserSettingsService.GeneralSettingsService.ShowPinToStart = value; + OnPropertyChanged(); + } + } + } + public bool ShowEditTagsMenu { get => UserSettingsService.GeneralSettingsService.ShowEditTagsMenu; @@ -454,6 +507,20 @@ public bool ShowCompressionOptions } } + // TODO uncomment code when feature is marked as stable + //public bool ShowFlattenOptions + //{ + // get => UserSettingsService.GeneralSettingsService.ShowFlattenOptions; + // set + // { + // if (value == UserSettingsService.GeneralSettingsService.ShowFlattenOptions) + // return; + + // UserSettingsService.GeneralSettingsService.ShowFlattenOptions = value; + // OnPropertyChanged(); + // } + //} + public bool ShowSendToMenu { get => UserSettingsService.GeneralSettingsService.ShowSendToMenu; @@ -480,6 +547,19 @@ public bool ShowOpenInNewWindow } } + public bool ShowOpenTerminal + { + get => UserSettingsService.GeneralSettingsService.ShowOpenTerminal; + set + { + if (value != UserSettingsService.GeneralSettingsService.ShowOpenTerminal) + { + UserSettingsService.GeneralSettingsService.ShowOpenTerminal = value; + OnPropertyChanged(); + } + } + } + private string selectedShellPaneArrangementType; public string SelectedShellPaneArrangementType { @@ -493,6 +573,20 @@ public string SelectedShellPaneArrangementType } } + public bool EnableSmoothScrolling + { + get => UserSettingsService.GeneralSettingsService.EnableSmoothScrolling; + set + { + if (value != UserSettingsService.GeneralSettingsService.EnableSmoothScrolling) + { + UserSettingsService.GeneralSettingsService.EnableSmoothScrolling = value; + + OnPropertyChanged(); + } + } + } + public void Dispose() { if (!disposed) diff --git a/src/Files.App/ViewModels/Settings/LayoutViewModel.cs b/src/Files.App/ViewModels/Settings/LayoutViewModel.cs index 12edc1dd1a9e..f65656655edc 100644 --- a/src/Files.App/ViewModels/Settings/LayoutViewModel.cs +++ b/src/Files.App/ViewModels/Settings/LayoutViewModel.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.ViewModels.Settings { - public sealed class LayoutViewModel : ObservableObject + public sealed partial class LayoutViewModel : ObservableObject { private IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetRequiredService(); @@ -19,7 +19,7 @@ public LayoutViewModel() // Sorting options SelectedDefaultSortingIndex = UserSettingsService.LayoutSettingsService.DefaultSortOption == SortOption.FileTag ? FileTagSortingIndex : (int)UserSettingsService.LayoutSettingsService.DefaultSortOption; SelectedDefaultSortPriorityIndex = UserSettingsService.LayoutSettingsService.DefaultSortDirectoriesAlongsideFiles ? 2 : UserSettingsService.LayoutSettingsService.DefaultSortFilesFirst ? 1 : 0; - + // Grouping options SelectedDefaultGroupingIndex = UserSettingsService.LayoutSettingsService.DefaultGroupOption == GroupOption.FileTag ? FileTagGroupingIndex : (int)UserSettingsService.LayoutSettingsService.DefaultGroupOption; SelectedDefaultGroupByDateUnitIndex = (int)UserSettingsService.LayoutSettingsService.DefaultGroupByDateUnit; @@ -110,7 +110,7 @@ public bool SortInDescendingOrder } } - + private int selectedDefaultSortPriorityIndex; public int SelectedDefaultSortPriorityIndex { diff --git a/src/Files.App/ViewModels/Settings/TagsViewModel.cs b/src/Files.App/ViewModels/Settings/TagsViewModel.cs index 5c5ca49cba37..d650e9b6aef0 100644 --- a/src/Files.App/ViewModels/Settings/TagsViewModel.cs +++ b/src/Files.App/ViewModels/Settings/TagsViewModel.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using System.Windows.Input; namespace Files.App.ViewModels.Settings { - public sealed class TagsViewModel : ObservableObject + public sealed partial class TagsViewModel : ObservableObject { private readonly IFileTagsSettingsService fileTagsSettingsService = Ioc.Default.GetRequiredService(); @@ -102,7 +102,7 @@ public void DeleteExistingTag(ListedTagViewModel item) } } - public sealed class NewTagViewModel : ObservableObject + public sealed partial class NewTagViewModel : ObservableObject { private string name = string.Empty; public string Name diff --git a/src/Files.App/ViewModels/SettingsViewModel.cs b/src/Files.App/ViewModels/SettingsViewModel.cs deleted file mode 100644 index 916fc9f92ab5..000000000000 --- a/src/Files.App/ViewModels/SettingsViewModel.cs +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System.Diagnostics.CodeAnalysis; -using System.Reflection; -using System.Runtime.CompilerServices; -using Windows.Storage; - -namespace Files.App.ViewModels -{ - [Obsolete("Do not use this class as Settings store anymore, settings have been merged to IUserSettingsService.")] - public sealed class SettingsViewModel : ObservableObject - { - private readonly ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings; - - #region ReadAndSaveSettings - - public bool Set(TValue value, [CallerMemberName] string propertyName = null) - { - propertyName = - propertyName is not null && propertyName.StartsWith("set_", StringComparison.OrdinalIgnoreCase) ? - propertyName.Substring(4) : - propertyName; - - TValue originalValue = default; - - if (localSettings.Values.ContainsKey(propertyName)) - { - originalValue = Get(originalValue, propertyName); - - localSettings.Values[propertyName] = value; - if (!SetProperty(ref originalValue, value, propertyName)) - { - return false; - } - } - else - { - localSettings.Values[propertyName] = value; - } - - return true; - } - - public TValue Get<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TValue>(TValue defaultValue, [CallerMemberName] string propertyName = null) - { - var name = propertyName ?? throw new ArgumentNullException(nameof(propertyName), "Cannot store property of unnamed."); - - name = - name.StartsWith("get_", StringComparison.OrdinalIgnoreCase) ? - propertyName.Substring(4) : - propertyName; - - if (localSettings.Values.ContainsKey(name)) - { - var value = localSettings.Values[name]; - - if (value is not TValue tValue) - { - if (value is IConvertible) - { - tValue = (TValue)Convert.ChangeType(value, typeof(TValue)); - } - else - { - var valueType = value.GetType(); - var tryParse = typeof(TValue).GetMethod("TryParse", BindingFlags.Instance | BindingFlags.Public); - - if (tryParse is null) - { - return default; - } - - var stringValue = value.ToString(); - tValue = default; - - var tryParseDelegate = - (TryParseDelegate)Delegate.CreateDelegate(valueType, tryParse, false); - - tValue = (tryParseDelegate?.Invoke(stringValue, out tValue) ?? false) ? tValue : default; - } - - // Put the corrected value in settings - Set(tValue, propertyName); - - return tValue; - } - return tValue; - } - - localSettings.Values[propertyName] = defaultValue; - - return defaultValue; - } - - private delegate bool TryParseDelegate(string inValue, out TValue parsedValue); - - #endregion ReadAndSaveSettings - } -} diff --git a/src/Files.App/ViewModels/ShellViewModel.cs b/src/Files.App/ViewModels/ShellViewModel.cs index d491dae1aff4..69b1714dd86e 100644 --- a/src/Files.App/ViewModels/ShellViewModel.cs +++ b/src/Files.App/ViewModels/ShellViewModel.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.Services.SizeProvider; using Files.Shared.Helpers; @@ -18,7 +18,9 @@ using Windows.Storage; using Windows.Storage.FileProperties; using Windows.Storage.Search; +using Windows.Win32.System.SystemServices; using static Files.App.Helpers.Win32PInvoke; +using ByteSize = ByteSizeLib.ByteSize; using DispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue; using FileAttributes = System.IO.FileAttributes; @@ -27,7 +29,7 @@ namespace Files.App.ViewModels /// /// Represents view model of . /// - public sealed class ShellViewModel : ObservableObject, IDisposable + public sealed partial class ShellViewModel : ObservableObject, IDisposable { private readonly SemaphoreSlim enumFolderSemaphore; private readonly SemaphoreSlim getFileOrFolderSemaphore; @@ -40,7 +42,7 @@ public sealed class ShellViewModel : ObservableObject, IDisposable private readonly AsyncManualResetEvent gitChangedEvent; private readonly DispatcherQueue dispatcherQueue; private readonly JsonElement defaultJson = JsonSerializer.SerializeToElement("{}"); - private readonly string folderTypeTextLocalized = "Folder".GetLocalizedResource(); + private readonly string folderTypeTextLocalized = Strings.Folder.GetLocalizedResource(); private Task? aProcessQueueAction; private Task? gitProcessQueueAction; @@ -55,6 +57,9 @@ public sealed class ShellViewModel : ObservableObject, IDisposable private readonly IFileTagsSettingsService fileTagsSettingsService = Ioc.Default.GetRequiredService(); private readonly ISizeProvider folderSizeProvider = Ioc.Default.GetRequiredService(); private readonly IStorageCacheService fileListCache = Ioc.Default.GetRequiredService(); + private readonly IWindowsSecurityService WindowsSecurityService = Ioc.Default.GetRequiredService(); + private readonly IStorageTrashBinService StorageTrashBinService = Ioc.Default.GetRequiredService(); + private readonly IContentPageContext ContentPageContext = Ioc.Default.GetRequiredService(); // Only used for Binding and ApplyFilesAndFoldersChangesAsync, don't manipulate on this! public BulkConcurrentObservableCollection FilesAndFolders { get; } @@ -110,6 +115,27 @@ public HorizontalAlignment FolderBackgroundImageHorizontalAlignment private set => SetProperty(ref _FolderBackgroundImageHorizontalAlignment, value); } + private ImageSource? _FolderThumbnailImageSource; + public ImageSource? FolderThumbnailImageSource + { + get => _FolderThumbnailImageSource; + private set => SetProperty(ref _FolderThumbnailImageSource, value); + } + + private BitmapImage? _SearchIconBitmapImage; + public BitmapImage? SearchIconBitmapImage + { + get => _SearchIconBitmapImage; + private set => SetProperty(ref _SearchIconBitmapImage, value); + } + + + public bool ShowFilterHeader => + UserSettingsService.GeneralSettingsService.ShowFilterHeader && + WorkingDirectory != "Home" && + WorkingDirectory != "ReleaseNotes" && + WorkingDirectory != "Settings"; + private GitProperties _EnabledGitProperties; public GitProperties EnabledGitProperties { @@ -120,7 +146,7 @@ public GitProperties EnabledGitProperties { filesAndFolders.ToList().ForEach(async item => { - if (item is GitItem gitItem && + if (item is IGitItem gitItem && (!gitItem.StatusPropertiesInitialized && value is GitProperties.All or GitProperties.Status || !gitItem.CommitPropertiesInitialized && value is GitProperties.All or GitProperties.Commit)) { @@ -142,6 +168,10 @@ public GitProperties EnabledGitProperties private CancellationTokenSource loadPropsCTS; private CancellationTokenSource watcherCTS; private CancellationTokenSource searchCTS; + private CancellationTokenSource updateTagGroupCTS; + private CancellationTokenSource? filterDebounceCS; + + public event EventHandler FocusFilterHeader; public event EventHandler DirectoryInfoUpdated; @@ -172,6 +202,11 @@ public GitProperties EnabledGitProperties public event ItemLoadStatusChangedEventHandler ItemLoadStatusChanged; + public void InvokeFocusFilterHeader() + { + FocusFilterHeader?.Invoke(this, EventArgs.Empty); + } + public async Task SetWorkingDirectoryAsync(string? value) { if (string.IsNullOrWhiteSpace(value)) @@ -192,7 +227,7 @@ public async Task SetWorkingDirectoryAsync(string? value) else if (!Path.IsPathRooted(WorkingDirectory) || Path.GetPathRoot(WorkingDirectory) != Path.GetPathRoot(value)) workingRoot = await FilesystemTasks.Wrap(() => DriveHelpers.GetRootFromPathAsync(value)); - if (value == "Home") + if (value == "Home" || value == "ReleaseNotes" || value == "Settings") currentStorageFolder = null; else _ = Task.Run(() => jumpListService.AddFolderAsync(value)); @@ -208,7 +243,10 @@ public async Task SetWorkingDirectoryAsync(string? value) GitDirectory = GitHelpers.GetGitRepositoryPath(WorkingDirectory, pathRoot); IsValidGitDirectory = !string.IsNullOrEmpty((await GitHelpers.GetRepositoryHead(GitDirectory))?.Name); + _ = UpdateFolderThumbnailImageSource(); + OnPropertyChanged(nameof(WorkingDirectory)); + OnPropertyChanged(nameof(ShowFilterHeader)); } public async Task> GetFolderFromPathAsync(string value, CancellationToken cancellationToken = default) @@ -270,6 +308,11 @@ public EmptyTextType EmptyTextType set => SetProperty(ref emptyTextType, value); } + public async Task UpdateFolderThumbnailImageSource() + { + FolderThumbnailImageSource = await NavigationHelpers.GetIconForPathAsync(WorkingDirectory); + } + public async Task UpdateSortOptionStatusAsync() { OnPropertyChanged(nameof(IsSortedByName)); @@ -535,9 +578,9 @@ public ShellViewModel(LayoutPreferencesManager folderSettingsViewModel) fileTagsSettingsService.OnTagsUpdated += FileTagsSettingsService_OnSettingUpdated; folderSizeProvider.SizeChanged += FolderSizeProvider_SizeChanged; folderSettings.LayoutModeChangeRequested += LayoutModeChangeRequested; - RecycleBinManager.Default.RecycleBinItemCreated += RecycleBinItemCreatedAsync; - RecycleBinManager.Default.RecycleBinItemDeleted += RecycleBinItemDeletedAsync; - RecycleBinManager.Default.RecycleBinRefreshRequested += RecycleBinRefreshRequestedAsync; + StorageTrashBinService.Watcher.ItemAdded += RecycleBinItemCreatedAsync; + StorageTrashBinService.Watcher.ItemDeleted += RecycleBinItemDeletedAsync; + StorageTrashBinService.Watcher.RefreshRequested += RecycleBinRefreshRequestedAsync; } private async void LayoutModeChangeRequested(object? sender, LayoutModeEventArgs e) @@ -601,14 +644,14 @@ private async void FolderSizeProvider_SizeChanged(object? sender, Services.SizeP try { var matchingItem = filesAndFolders.ToList().FirstOrDefault(x => x.ItemPath == e.Path); - if (matchingItem is not null) + if (matchingItem is not null && (e.ValueState is not SizeChangedValueState.Intermediate || (long)e.NewSize > matchingItem.FileSizeBytes)) { await dispatcherQueue.EnqueueOrInvokeAsync(() => { if (e.ValueState is SizeChangedValueState.None) { matchingItem.FileSizeBytes = 0; - matchingItem.FileSize = "ItemSizeNotCalculated".GetLocalizedResource(); + matchingItem.FileSize = Strings.ItemSizeNotCalculated.GetLocalizedResource(); } else if (e.ValueState is SizeChangedValueState.Final || (long)e.NewSize > matchingItem.FileSizeBytes) { @@ -631,7 +674,7 @@ private async void FileTagsSettingsService_OnSettingUpdated(object? sender, Even { await dispatcherQueue.EnqueueOrInvokeAsync(() => { - if (WorkingDirectory != "Home") + if (WorkingDirectory != "Home" && WorkingDirectory != "ReleaseNotes" && WorkingDirectory != "Settings") RefreshItems(null); }); } @@ -649,9 +692,10 @@ private async void UserSettingsService_OnSettingChangedEvent(object? sender, Set case nameof(UserSettingsService.FoldersSettingsService.CalculateFolderSizes): case nameof(UserSettingsService.FoldersSettingsService.SelectFilesOnHover): case nameof(UserSettingsService.FoldersSettingsService.ShowCheckboxesWhenSelectingItems): + case nameof(UserSettingsService.FoldersSettingsService.SizeUnitFormat): await dispatcherQueue.EnqueueOrInvokeAsync(() => { - if (WorkingDirectory != "Home") + if (WorkingDirectory != "Home" && WorkingDirectory != "ReleaseNotes" && WorkingDirectory != "Settings") RefreshItems(null); }); break; @@ -670,6 +714,9 @@ await dispatcherQueue.EnqueueOrInvokeAsync(() => await OrderFilesAndFoldersAsync(); await ApplyFilesAndFoldersChangesAsync(); break; + case nameof(UserSettingsService.GeneralSettingsService.ShowFilterHeader): + OnPropertyChanged(nameof(ShowFilterHeader)); + break; } } @@ -702,14 +749,63 @@ public void CancelExtendedPropertiesLoadingForItem(ListedItem item) itemLoadQueue.TryUpdate(item.ItemPath, true, false); } - private bool IsSearchResults { get; set; } + private bool _isSearchResults; + public bool IsSearchResults + { + get => _isSearchResults; + set + { + if (SetProperty(ref _isSearchResults, value)) + { + if (!value) + SearchHeaderTitle = string.Empty; + } + } + } + + private string? _searchHeaderTitle; + public string? SearchHeaderTitle + { + get => _searchHeaderTitle; + set => SetProperty(ref _searchHeaderTitle, value); + } public void UpdateEmptyTextType() { EmptyTextType = FilesAndFolders.Count == 0 ? (IsSearchResults ? EmptyTextType.NoSearchResultsFound : EmptyTextType.FolderEmpty) : EmptyTextType.None; } - public string? FilesAndFoldersFilter { get; set; } + private string? _filesAndFoldersFilter; + public string? FilesAndFoldersFilter + { + get => _filesAndFoldersFilter; + set + { + if (SetProperty(ref _filesAndFoldersFilter, value)) + { + FilesAndFolderFilterUpdated(); + } + } + } + + private void FilesAndFolderFilterUpdated() + { + if (filterDebounceCS is not null) + { + filterDebounceCS.Cancel(); + filterDebounceCS.Dispose(); + } + + filterDebounceCS = new CancellationTokenSource(); + var token = filterDebounceCS.Token; + + _ = Task.Delay(250, token) + .ContinueWith(_ => ApplyFilesAndFoldersChangesAsync(), token, + TaskContinuationOptions.OnlyOnRanToCompletion, + TaskScheduler.Default) + .Unwrap(); + } + // Apply changes immediately after manipulating on filesAndFolders completed public async Task ApplyFilesAndFoldersChangesAsync() @@ -725,7 +821,7 @@ void ClearDisplay() DirectoryInfoUpdated?.Invoke(this, EventArgs.Empty); } - if (Win32Helper.IsHasThreadAccessPropertyPresent && dispatcherQueue.HasThreadAccess) + if (dispatcherQueue.HasThreadAccess) ClearDisplay(); else await dispatcherQueue.EnqueueOrInvokeAsync(ClearDisplay); @@ -792,6 +888,11 @@ await dispatcherQueue.EnqueueOrInvokeAsync(() => private Task RequestSelectionAsync(List itemsToSelect) { + // Don't notify if shell page is not the active pane (eg. Dual Pane) + // https://github.com/files-community/Files/issues/17427 + if (WorkingDirectory != ContentPageContext.ShellPage!.ShellViewModel.WorkingDirectory) + return Task.CompletedTask; + // Don't notify if there weren't listed items if (itemsToSelect is null || itemsToSelect.IsEmpty()) return Task.CompletedTask; @@ -813,7 +914,7 @@ void OrderEntries() folderSettings.SortDirectoriesAlongsideFiles, folderSettings.SortFilesFirst)); } - if (Win32Helper.IsHasThreadAccessPropertyPresent && dispatcherQueue.HasThreadAccess) + if (dispatcherQueue.HasThreadAccess) return Task.Run(OrderEntries); OrderEntries(); @@ -971,9 +1072,12 @@ private async Task GetShieldIcon() private async Task LoadThumbnailAsync(ListedItem item, CancellationToken cancellationToken) { var loadNonCachedThumbnail = false; - var thumbnailSize = folderSettings.GetRoundedIconSize(); + var thumbnailSize = LayoutSizeKindHelper.GetIconSize(folderSettings.LayoutMode); var returnIconOnly = UserSettingsService.FoldersSettingsService.ShowThumbnails == false || thumbnailSize < 48; + // TODO Remove this property when all the layouts can support different icon sizes + var useCurrentScale = folderSettings.LayoutMode == FolderLayoutModes.DetailsView || folderSettings.LayoutMode == FolderLayoutModes.ListView || folderSettings.LayoutMode == FolderLayoutModes.ColumnView || folderSettings.LayoutMode == FolderLayoutModes.CardsView; + byte[]? result = null; // Non-cached thumbnails take longer to generate @@ -986,10 +1090,10 @@ private async Task LoadThumbnailAsync(ListedItem item, CancellationToken cancell item.ItemPath, thumbnailSize, item.IsFolder, - IconOptions.ReturnThumbnailOnly | IconOptions.ReturnOnlyIfCached); + IconOptions.ReturnThumbnailOnly | IconOptions.ReturnOnlyIfCached | (useCurrentScale ? IconOptions.UseCurrentScale : IconOptions.None)); cancellationToken.ThrowIfCancellationRequested(); - loadNonCachedThumbnail = true; + loadNonCachedThumbnail = result is null; } if (result is null) @@ -999,7 +1103,7 @@ private async Task LoadThumbnailAsync(ListedItem item, CancellationToken cancell item.ItemPath, thumbnailSize, item.IsFolder, - IconOptions.ReturnIconOnly); + IconOptions.ReturnIconOnly | (useCurrentScale ? IconOptions.UseCurrentScale : IconOptions.None)); cancellationToken.ThrowIfCancellationRequested(); } @@ -1011,7 +1115,7 @@ private async Task LoadThumbnailAsync(ListedItem item, CancellationToken cancell item.ItemPath, thumbnailSize, item.IsFolder, - returnIconOnly ? IconOptions.ReturnIconOnly : IconOptions.None); + (returnIconOnly ? IconOptions.ReturnIconOnly : IconOptions.None) | (useCurrentScale ? IconOptions.UseCurrentScale : IconOptions.None)); cancellationToken.ThrowIfCancellationRequested(); } @@ -1057,7 +1161,7 @@ await dispatcherQueue.EnqueueOrInvokeAsync(async () => item.ItemPath, thumbnailSize, item.IsFolder, - IconOptions.ReturnThumbnailOnly); + IconOptions.ReturnThumbnailOnly | (useCurrentScale ? IconOptions.UseCurrentScale : IconOptions.None)); } finally { @@ -1125,7 +1229,7 @@ public async Task LoadExtendedItemPropertiesAsync(ListedItem item) cts.Token.ThrowIfCancellationRequested(); if (item.IsLibrary || item.PrimaryItemAttribute == StorageItemTypes.File || item.IsArchive) { - if (!item.IsShortcut && !item.IsHiddenItem && !FtpHelpers.IsFtpPath(item.ItemPath)) + if (!item.IsShortcut && !FtpHelpers.IsFtpPath(item.ItemPath)) { matchingStorageFile = await GetFileFromPathAsync(item.ItemPath, cts.Token); if (matchingStorageFile is not null) @@ -1135,7 +1239,8 @@ public async Task LoadExtendedItemPropertiesAsync(ListedItem item) var syncStatus = await CheckCloudDriveSyncStatusAsync(matchingStorageFile); var fileFRN = await FileTagsHelper.GetFileFRN(matchingStorageFile); var fileTag = FileTagsHelper.ReadFileTag(item.ItemPath); - var itemType = (item.ItemType == "Folder".GetLocalizedResource()) ? item.ItemType : matchingStorageFile.DisplayType; + var itemType = (item.ItemType == Strings.Folder.GetLocalizedResource()) ? item.ItemType : matchingStorageFile.DisplayType; + var extraProperties = await GetExtraProperties(matchingStorageFile); cts.Token.ThrowIfCancellationRequested(); @@ -1147,6 +1252,27 @@ await dispatcherQueue.EnqueueOrInvokeAsync(() => item.FileFRN = fileFRN; item.FileTags = fileTag; item.IsElevationRequired = CheckElevationRights(item); + item.ImageDimensions = extraProperties?.Result["System.Image.Dimensions"]?.ToString() ?? string.Empty; + item.FileVersion = extraProperties?.Result["System.FileVersion"]?.ToString() ?? string.Empty; + item.MediaDuration = ulong.TryParse(extraProperties?.Result["System.Media.Duration"]?.ToString(), out ulong duration) + ? TimeSpan.FromTicks((long)duration).ToString(@"hh\:mm\:ss") + : string.Empty; + + switch (true) + { + case var _ when !string.IsNullOrEmpty(item.ImageDimensions): + item.ContextualProperty = $"{Strings.PropertyDimensions.GetLocalizedResource()}: {item.ImageDimensions}"; + break; + case var _ when !string.IsNullOrEmpty(item.MediaDuration): + item.ContextualProperty = $"{Strings.PropertyDuration.GetLocalizedResource()}: {item.MediaDuration}"; + break; + case var _ when !string.IsNullOrEmpty(item.FileVersion): + item.ContextualProperty = $"{Strings.PropertyVersion.GetLocalizedResource()}: {item.FileVersion}"; + break; + default: + item.ContextualProperty = $"{Strings.Modified.GetLocalizedResource()}: {item.ItemDateModified}"; + break; + } }, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low); @@ -1181,7 +1307,9 @@ await dispatcherQueue.EnqueueOrInvokeAsync(() => var syncStatus = await CheckCloudDriveSyncStatusAsync(matchingStorageFolder); var fileFRN = await FileTagsHelper.GetFileFRN(matchingStorageFolder); var fileTag = FileTagsHelper.ReadFileTag(item.ItemPath); - var itemType = (item.ItemType == "Folder".GetLocalizedResource()) ? item.ItemType : matchingStorageFolder.DisplayType; + var itemType = (item.ItemType == Strings.Folder.GetLocalizedResource()) ? item.ItemType : matchingStorageFolder.DisplayType; + var extraProperties = await GetExtraProperties(matchingStorageFolder); + cts.Token.ThrowIfCancellationRequested(); await dispatcherQueue.EnqueueOrInvokeAsync(() => @@ -1191,6 +1319,30 @@ await dispatcherQueue.EnqueueOrInvokeAsync(() => item.SyncStatusUI = CloudDriveSyncStatusUI.FromCloudDriveSyncStatus(syncStatus); item.FileFRN = fileFRN; item.FileTags = fileTag; + + if (extraProperties is not null) + { + // Drive Storage Details + if (extraProperties.Result["System.SFGAOFlags"] is uint attributesRaw && + extraProperties.Result["System.Capacity"] is ulong capacityRaw && + extraProperties.Result["System.FreeSpace"] is ulong freeSpaceRaw && + ((SFGAO_FLAGS)attributesRaw).HasFlag(SFGAO_FLAGS.SFGAO_REMOVABLE) && + !((SFGAO_FLAGS)attributesRaw).HasFlag(SFGAO_FLAGS.SFGAO_FILESYSTEM)) + { + var maxSpace = ByteSize.FromBytes(capacityRaw); + var freeSpace = ByteSize.FromBytes(freeSpaceRaw); + + item.MaxSpace = maxSpace; + item.SpaceUsed = maxSpace - freeSpace; + item.FileSize = string.Format(Strings.DriveFreeSpaceAndCapacity.GetLocalizedResource(), freeSpace.ToSizeString(), maxSpace.ToSizeString()); + item.ShowDriveStorageDetails = true; + } + + } + else + { + item.ContextualProperty = $"{Strings.Modified.GetLocalizedResource()}: {item.ItemDateModified}"; + } }, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low); @@ -1260,20 +1412,50 @@ await SafetyExtensions.IgnoreExceptions(() => finally { itemLoadQueue.TryRemove(item.ItemPath, out _); + await RefreshTagGroups(); } } + public async Task RefreshTagGroups() + { + if (FilesAndFolders.IsGrouped && + folderSettings.DirectoryGroupOption is GroupOption.FileTag && + itemLoadQueue.IsEmpty()) + { + updateTagGroupCTS?.Cancel(); + updateTagGroupCTS = new(); + + await GroupOptionsUpdatedAsync(updateTagGroupCTS.Token); + } + } + + public Task UpdateItemsTags(Dictionary newTags) + { + return dispatcherQueue.EnqueueOrInvokeAsync(() => + { + int count = newTags.Count; + foreach (var item in FilesAndFolders) + { + if (newTags.TryGetValue(item.ItemPath, out var tags)) + { + item.FileTags = tags; + if (--count == 0) + break; + } + } + }, + Microsoft.UI.Dispatching.DispatcherQueuePriority.Low); + } + private bool CheckElevationRights(ListedItem item) { if (item.SyncStatusUI.LoadSyncStatus) return false; - return item.IsShortcut - ? ElevationHelpers.IsElevationRequired(((ShortcutItem)item).TargetPath) - : ElevationHelpers.IsElevationRequired(item.ItemPath); + return WindowsSecurityService.IsElevationRequired(item.IsShortcut ? ((IShortcutItem)item).TargetPath : item.ItemPath); } - public async Task LoadGitPropertiesAsync(GitItem gitItem) + public async Task LoadGitPropertiesAsync(IGitItem gitItem) { var getStatus = EnabledGitProperties is GitProperties.All or GitProperties.Status && !gitItem.StatusPropertiesInitialized; var getCommit = EnabledGitProperties is GitProperties.All or GitProperties.Commit && !gitItem.CommitPropertiesInitialized; @@ -1310,10 +1492,10 @@ await SafetyExtensions.IgnoreExceptions(() => { gitItem.UnmergedGitStatusIcon = gitItemModel.Status switch { - ChangeKind.Added => (Microsoft.UI.Xaml.Style)Microsoft.UI.Xaml.Application.Current.Resources["ColorIconGitAdded"], - ChangeKind.Deleted => (Microsoft.UI.Xaml.Style)Microsoft.UI.Xaml.Application.Current.Resources["ColorIconGitDeleted"], - ChangeKind.Modified => (Microsoft.UI.Xaml.Style)Microsoft.UI.Xaml.Application.Current.Resources["ColorIconGitModified"], - ChangeKind.Untracked => (Microsoft.UI.Xaml.Style)Microsoft.UI.Xaml.Application.Current.Resources["ColorIconGitUntracked"], + ChangeKind.Added => (Microsoft.UI.Xaml.Style)Microsoft.UI.Xaml.Application.Current.Resources["App.ThemedIcons.Status.Added"], + ChangeKind.Deleted => (Microsoft.UI.Xaml.Style)Microsoft.UI.Xaml.Application.Current.Resources["App.ThemedIcons.Status.Removed"], + ChangeKind.Modified => (Microsoft.UI.Xaml.Style)Microsoft.UI.Xaml.Application.Current.Resources["App.ThemedIcons.Status.Modified"], + ChangeKind.Untracked => (Microsoft.UI.Xaml.Style)Microsoft.UI.Xaml.Application.Current.Resources["App.ThemedIcons.Status.Removed"], _ => null, }; gitItem.UnmergedGitStatusName = gitItemModel.StatusHumanized; @@ -1390,7 +1572,7 @@ await dispatcherQueue.EnqueueOrInvokeAsync(async () => public void RefreshItems(string? previousDir, Action postLoadCallback = null) { - RapidAddItemsToCollectionAsync(WorkingDirectory, previousDir, postLoadCallback); + _ = RapidAddItemsToCollectionAsync(WorkingDirectory, previousDir, postLoadCallback); } private async Task RapidAddItemsToCollectionAsync(string path, string? previousDir, Action postLoadCallback) @@ -1497,7 +1679,7 @@ private async Task RapidAddItemsToCollectionAsync(string? path, LibraryItem? lib PageTypeUpdated?.Invoke(this, new PageTypeUpdatedEventArgs() { IsTypeCloudDrive = false, IsTypeRecycleBin = isRecycleBin }); currentStorageFolder ??= await FilesystemTasks.Wrap(() => StorageFileExtensions.DangerousGetFolderWithPathFromPathAsync(path)); if (!HasNoWatcher) - WatchForStorageFolderChangesAsync(currentStorageFolder?.Item); + _ = WatchForStorageFolderChangesAsync(currentStorageFolder?.Item); break; // Watch for changes using Win32 in Box Drive folder (#7428) and network drives (#5869) @@ -1526,6 +1708,8 @@ private async Task RapidAddItemsToCollectionAsync(string? path, LibraryItem? lib public void CloseWatcher() { + App.Logger.LogInformation($"CloseWatcher: aProcessQueueAction={aProcessQueueAction?.Status.ToString()}, gitProcessQueueAction={gitProcessQueueAction?.Status.ToString()}"); + watcher?.Dispose(); watcher = null; @@ -1547,12 +1731,22 @@ private async Task EnumerateItemsFromStandardFolderAsync(string path, Cance !isMtp && !isShellFolder && !isWslDistro; + bool isNetdisk = false; + + try + { + // Special handling for network drives + if (!isNetwork) + isNetdisk = (new DriveInfo(path).DriveType == System.IO.DriveType.Network); + } + catch { } + bool isFtp = FtpHelpers.IsFtpPath(path); bool enumFromStorageFolder = isBoxFolder || isFtp; BaseStorageFolder? rootFolder = null; - if (isNetwork) + if (isNetwork || isNetdisk) { var auth = await NetworkService.AuthenticateNetworkShare(path); if (!auth) @@ -1585,23 +1779,23 @@ private async Task EnumerateItemsFromStandardFolderAsync(string path, Cance else if (res == FileSystemStatusCode.Unauthorized) { await DialogDisplayHelper.ShowDialogAsync( - "AccessDenied".GetLocalizedResource(), - "AccessDeniedToFolder".GetLocalizedResource()); + Strings.AccessDenied.GetLocalizedResource(), + Strings.AccessDeniedToFolder.GetLocalizedResource()); return -1; } else if (res == FileSystemStatusCode.NotFound) { await DialogDisplayHelper.ShowDialogAsync( - "FolderNotFoundDialog/Title".GetLocalizedResource(), - "FolderNotFoundDialog/Text".GetLocalizedResource()); + Strings.FolderNotFoundDialog_Title.GetLocalizedResource(), + Strings.FolderNotFoundDialog_Text.GetLocalizedResource()); return -1; } else { await DialogDisplayHelper.ShowDialogAsync( - "DriveUnpluggedDialog/Title".GetLocalizedResource(), + Strings.DriveUnpluggedDialog_Title.GetLocalizedResource(), res.ErrorCode.ToString()); return -1; @@ -1702,7 +1896,7 @@ await DialogDisplayHelper.ShowDialogAsync( if (hFile == IntPtr.Zero) { - await DialogDisplayHelper.ShowDialogAsync("DriveUnpluggedDialog/Title".GetLocalizedResource(), ""); + await DialogDisplayHelper.ShowDialogAsync(Strings.DriveUnpluggedDialog_Title.GetLocalizedResource(), ""); return -1; } @@ -1714,8 +1908,8 @@ await DialogDisplayHelper.ShowDialogAsync( if (filesAndFolders.Count == 0 && errorCode == 0x5) { await DialogDisplayHelper.ShowDialogAsync( - "AccessDenied".GetLocalizedResource(), - "AccessDeniedToFolder".GetLocalizedResource()); + Strings.AccessDenied.GetLocalizedResource(), + Strings.AccessDeniedToFolder.GetLocalizedResource()); return -1; } @@ -1733,7 +1927,6 @@ await Task.Run(async () => }); filesAndFolders.AddRange(fileList); - FilesAndFoldersFilter = null; await OrderFilesAndFoldersAsync(); await ApplyFilesAndFoldersChangesAsync(); @@ -1742,6 +1935,7 @@ await dispatcherQueue.EnqueueOrInvokeAsync(() => { GetDesktopIniFileData(); CheckForBackgroundImage(); + FilesAndFoldersFilter = null; }, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low); }); @@ -1800,7 +1994,7 @@ await Task.Run(async () => private void CheckForSolutionFile() { SolutionFilePath = filesAndFolders.ToList().AsParallel() - .Where(item => FileExtensionHelpers.HasExtension(item.FileExtension, ".sln")) + .Where(item => FileExtensionHelpers.HasExtension(item.FileExtension, ".sln", ".slnx")) .FirstOrDefault()?.ItemPath; } @@ -1812,6 +2006,12 @@ private void GetDesktopIniFileData() public void CheckForBackgroundImage() { + if (WorkingDirectory == "Home" || WorkingDirectory == "ReleaseNotes" || WorkingDirectory == "Settings") + { + FolderBackgroundImageSource = null; + return; + } + var filesAppSection = DesktopIni?.FirstOrDefault(x => x.SectionName == "FilesApp"); if (filesAppSection is null || folderSettings.LayoutMode is FolderLayoutModes.ColumnView) { @@ -1828,11 +2028,19 @@ public void CheckForBackgroundImage() } else { - FolderBackgroundImageSource = new BitmapImage + try { - UriSource = new Uri(backgroundImage, UriKind.RelativeOrAbsolute), - CreateOptions = BitmapCreateOptions.IgnoreImageCache - }; + FolderBackgroundImageSource = new BitmapImage + { + UriSource = new Uri(backgroundImage, UriKind.RelativeOrAbsolute), + CreateOptions = BitmapCreateOptions.IgnoreImageCache + }; + } + catch (Exception ex) + { + // Handle errors with setting the URI + App.Logger.LogWarning(ex, ex.Message); + } } // Opacity @@ -1891,6 +2099,17 @@ public async Task CheckCloudDriveSyncStatusAsync(IStorageI return (CloudDriveSyncStatus)syncStatus; } + private async Task>?> GetExtraProperties(IStorageItem matchingStorageItem) + { + if (matchingStorageItem is BaseStorageFile file && file.Properties != null) + return await FilesystemTasks.Wrap(() => file.Properties.RetrievePropertiesAsync(["System.Image.Dimensions", "System.Media.Duration", "System.FileVersion"]).AsTask()); + + else if (matchingStorageItem is BaseStorageFolder folder && folder.Properties != null) + return await FilesystemTasks.Wrap(() => folder.Properties.RetrievePropertiesAsync(["System.FreeSpace", "System.Capacity", "System.SFGAOFlags"]).AsTask()); + + return null; + } + private async Task WatchForStorageFolderChangesAsync(BaseStorageFolder? rootFolder) { if (rootFolder is null) @@ -1929,7 +2148,11 @@ await Task.Factory.StartNew(() => private void WatchForWin32FolderChanges(string? folderPath) { - if (Directory.Exists(folderPath)) + if (!Directory.Exists(folderPath)) + return; + + // NOTE: Suppressed NullReferenceException caused by EnableRaisingEvents + SafetyExtensions.IgnoreExceptions(() => { watcher = new FileSystemWatcher { @@ -1942,7 +2165,7 @@ private void WatchForWin32FolderChanges(string? folderPath) watcher.Deleted += DirectoryWatcher_Changed; watcher.Renamed += DirectoryWatcher_Changed; watcher.EnableRaisingEvents = true; - } + }, App.Logger); } private async void DirectoryWatcher_Changed(object sender, FileSystemEventArgs e) @@ -2414,6 +2637,7 @@ private async Task AddFileOrFolderAsync(ListedItem? item) else if (storageItem.IsOfType(StorageItemTypes.Folder)) { var properties = await storageItem.AsBaseStorageFolder().GetBasicPropertiesAsync(); + size = item.IsArchive ? (long)properties.Size : null; modified = properties.DateModified; created = properties.DateCreated; } @@ -2528,6 +2752,13 @@ public async Task SearchAsync(FolderSearch search) await ApplyFilesAndFoldersChangesAsync(); EmptyTextType = EmptyTextType.None; + SearchHeaderTitle = !string.IsNullOrEmpty(search.Query) + ? string.Format(Strings.SearchResultsFor.GetLocalizedResource(), search.Query) + : string.Empty; + + if (SearchIconBitmapImage is null) + SearchIconBitmapImage ??= await UIHelpers.GetSearchIconResource(); + ItemLoadStatusChanged?.Invoke(this, new ItemLoadStatusChangedEventArgs() { Status = ItemLoadStatusChangedEventArgs.ItemLoadStatus.InProgress }); var results = new List(); @@ -2556,6 +2787,8 @@ public void CancelSearch() public void UpdateDateDisplay(bool isFormatChange) { + App.Logger.LogDebug($"UpdateDateDisplay: isFormatChange={isFormatChange}, itemCount={filesAndFolders?.Count}"); + filesAndFolders.ToList().AsParallel().ForAll(async item => { // Reassign values to update date display @@ -2567,7 +2800,7 @@ public void UpdateDateDisplay(bool isFormatChange) await dispatcherQueue.EnqueueOrInvokeAsync(() => item.ItemDateModifiedReal = item.ItemDateModifiedReal); if (item is RecycleBinItem recycleBinItem && (isFormatChange || IsDateDiff(recycleBinItem.ItemDateDeletedReal))) await dispatcherQueue.EnqueueOrInvokeAsync(() => recycleBinItem.ItemDateDeletedReal = recycleBinItem.ItemDateDeletedReal); - if (item is GitItem gitItem && gitItem.GitLastCommitDate is DateTimeOffset offset && (isFormatChange || IsDateDiff(offset))) + if (item is IGitItem gitItem && gitItem.GitLastCommitDate is DateTimeOffset offset && (isFormatChange || IsDateDiff(offset))) await dispatcherQueue.EnqueueOrInvokeAsync(() => gitItem.GitLastCommitDate = gitItem.GitLastCommitDate); }); } @@ -2577,9 +2810,13 @@ public void UpdateDateDisplay(bool isFormatChange) public void Dispose() { CancelLoadAndClearFiles(); - RecycleBinManager.Default.RecycleBinItemCreated -= RecycleBinItemCreatedAsync; - RecycleBinManager.Default.RecycleBinItemDeleted -= RecycleBinItemDeletedAsync; - RecycleBinManager.Default.RecycleBinRefreshRequested -= RecycleBinRefreshRequestedAsync; + filterDebounceCS?.Cancel(); + filterDebounceCS?.Dispose(); + App.Logger.LogInformation($"ShellViewModel.Dispose: CurrentFolder={LogPathHelper.GetPathIdentifier(CurrentFolder?.ItemPath)}"); + + StorageTrashBinService.Watcher.ItemAdded -= RecycleBinItemCreatedAsync; + StorageTrashBinService.Watcher.ItemDeleted -= RecycleBinItemDeletedAsync; + StorageTrashBinService.Watcher.RefreshRequested -= RecycleBinRefreshRequestedAsync; UserSettingsService.OnSettingChangedEvent -= UserSettingsService_OnSettingChangedEvent; fileTagsSettingsService.OnSettingImportedEvent -= FileTagsSettingsService_OnSettingUpdated; fileTagsSettingsService.OnTagsUpdated -= FileTagsSettingsService_OnSettingUpdated; diff --git a/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs b/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs deleted file mode 100644 index 016f2b3f6cf5..000000000000 --- a/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs +++ /dev/null @@ -1,1082 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using CommunityToolkit.WinUI.UI; -using Files.Shared.Helpers; -using Microsoft.UI.Dispatching; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; -using Microsoft.UI.Xaml.Input; -using System.IO; -using System.Windows.Input; -using Windows.ApplicationModel.DataTransfer; -using Windows.UI.Text; -using FocusManager = Microsoft.UI.Xaml.Input.FocusManager; - -namespace Files.App.ViewModels.UserControls -{ - public sealed class AddressToolbarViewModel : ObservableObject, IAddressToolbarViewModel, IDisposable - { - private const int MAX_SUGGESTIONS = 10; - - private IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetRequiredService(); - - private readonly IDialogService _dialogService = Ioc.Default.GetRequiredService(); - - private readonly DrivesViewModel drivesViewModel = Ioc.Default.GetRequiredService(); - - public IUpdateService UpdateService { get; } = Ioc.Default.GetService()!; - - public ICommandManager Commands { get; } = Ioc.Default.GetRequiredService(); - - public delegate void ToolbarPathItemInvokedEventHandler(object sender, PathNavigationEventArgs e); - - public delegate void ToolbarFlyoutOpenedEventHandler(object sender, ToolbarFlyoutOpenedEventArgs e); - - public delegate void ToolbarPathItemLoadedEventHandler(object sender, ToolbarPathItemLoadedEventArgs e); - - public delegate void AddressBarTextEnteredEventHandler(object sender, AddressBarTextEnteredEventArgs e); - - public delegate void PathBoxItemDroppedEventHandler(object sender, PathBoxItemDroppedEventArgs e); - - public event ToolbarPathItemInvokedEventHandler? ToolbarPathItemInvoked; - - public event ToolbarFlyoutOpenedEventHandler? ToolbarFlyoutOpened; - - public event ToolbarPathItemLoadedEventHandler? ToolbarPathItemLoaded; - - public event IAddressToolbarViewModel.ItemDraggedOverPathItemEventHandler? ItemDraggedOverPathItem; - - public event EventHandler? EditModeEnabled; - - public event IAddressToolbarViewModel.ToolbarQuerySubmittedEventHandler? PathBoxQuerySubmitted; - - public event AddressBarTextEnteredEventHandler? AddressBarTextEntered; - - public event PathBoxItemDroppedEventHandler? PathBoxItemDropped; - - public event EventHandler? RefreshRequested; - - public event EventHandler? RefreshWidgetsRequested; - - public ObservableCollection PathComponents { get; } = []; - - private bool _isCommandPaletteOpen; - public bool IsCommandPaletteOpen - { - get => _isCommandPaletteOpen; - set => SetProperty(ref _isCommandPaletteOpen, value); - } - - private bool isUpdating; - public bool IsUpdating - { - get => isUpdating; - set => SetProperty(ref isUpdating, value); - } - - private bool isUpdateAvailable; - public bool IsUpdateAvailable - { - get => isUpdateAvailable; - set => SetProperty(ref isUpdateAvailable, value); - } - - private string? releaseNotes; - public string? ReleaseNotes - { - get => releaseNotes; - set => SetProperty(ref releaseNotes, value); - } - - private bool isReleaseNotesVisible; - public bool IsReleaseNotesVisible - { - get => isReleaseNotesVisible; - set => SetProperty(ref isReleaseNotesVisible, value); - } - - private bool canCopyPathInPage; - public bool CanCopyPathInPage - { - get => canCopyPathInPage; - set => SetProperty(ref canCopyPathInPage, value); - } - - private bool canGoBack; - public bool CanGoBack - { - get => canGoBack; - set => SetProperty(ref canGoBack, value); - } - - private bool canGoForward; - public bool CanGoForward - { - get => canGoForward; - set => SetProperty(ref canGoForward, value); - } - - private bool canNavigateToParent; - public bool CanNavigateToParent - { - get => canNavigateToParent; - set => SetProperty(ref canNavigateToParent, value); - } - - private bool previewPaneEnabled; - public bool PreviewPaneEnabled - { - get => previewPaneEnabled; - set => SetProperty(ref previewPaneEnabled, value); - } - - private bool canRefresh; - public bool CanRefresh - { - get => canRefresh; - set => SetProperty(ref canRefresh, value); - } - - private string searchButtonGlyph = "\uE721"; - public string SearchButtonGlyph - { - get => searchButtonGlyph; - set => SetProperty(ref searchButtonGlyph, value); - } - - private bool isSearchBoxVisible; - public bool IsSearchBoxVisible - { - get => isSearchBoxVisible; - set - { - if (SetProperty(ref isSearchBoxVisible, value)) - SearchButtonGlyph = value ? "\uE711" : "\uE721"; - } - } - - private string? pathText; - public string? PathText - { - get => pathText; - set - { - pathText = value; - - OnPropertyChanged(nameof(PathText)); - } - } - - public ObservableCollection NavigationBarSuggestions = []; - - private CurrentInstanceViewModel instanceViewModel; - public CurrentInstanceViewModel InstanceViewModel - { - get => instanceViewModel; - set - { - if (instanceViewModel?.FolderSettings is not null) - instanceViewModel.FolderSettings.PropertyChanged -= FolderSettings_PropertyChanged; - - if (SetProperty(ref instanceViewModel, value) && instanceViewModel?.FolderSettings is not null) - { - FolderSettings_PropertyChanged(this, new PropertyChangedEventArgs(nameof(LayoutPreferencesManager.LayoutMode))); - instanceViewModel.FolderSettings.PropertyChanged += FolderSettings_PropertyChanged; - } - } - } - - private Style _LayoutOpacityIcon; - public Style LayoutOpacityIcon - { - get => _LayoutOpacityIcon; - set - { - if (SetProperty(ref _LayoutOpacityIcon, value)) - { - } - } - } - - private PointerRoutedEventArgs? pointerRoutedEventArgs; - - public AddressToolbarViewModel() - { - RefreshClickCommand = new RelayCommand(e => RefreshRequested?.Invoke(this, EventArgs.Empty)); - ViewReleaseNotesAsyncCommand = new AsyncRelayCommand(ViewReleaseNotesAsync); - - dispatcherQueue = DispatcherQueue.GetForCurrentThread(); - dragOverTimer = dispatcherQueue.CreateTimer(); - - SearchBox.Escaped += SearchRegion_Escaped; - UserSettingsService.OnSettingChangedEvent += UserSettingsService_OnSettingChangedEvent; - UpdateService.PropertyChanged += UpdateService_OnPropertyChanged; - } - - private async void UpdateService_OnPropertyChanged(object? sender, PropertyChangedEventArgs e) - { - IsUpdateAvailable = UpdateService.IsUpdateAvailable; - IsUpdating = UpdateService.IsUpdating; - - // TODO: Bad code, result is called twice when checking for release notes - if (UpdateService.IsReleaseNotesAvailable) - await CheckForReleaseNotesAsync(); - } - - private async Task ViewReleaseNotesAsync() - { - if (ReleaseNotes is null) - return; - - var viewModel = new ReleaseNotesDialogViewModel(ReleaseNotes); - var dialog = _dialogService.GetDialog(viewModel); - - await dialog.TryShowAsync(); - } - - public async Task CheckForReleaseNotesAsync() - { - var result = await UpdateService.GetLatestReleaseNotesAsync(); - if (result is null) - return; - - ReleaseNotes = result; - IsReleaseNotesVisible = true; - } - - public void RefreshWidgets() - { - RefreshWidgetsRequested?.Invoke(this, EventArgs.Empty); - } - - private void UserSettingsService_OnSettingChangedEvent(object? sender, SettingChangedEventArgs e) - { - switch (e.SettingName) - { - // TODO: Move this to the widget page, it doesn't belong here. - case nameof(UserSettingsService.GeneralSettingsService.ShowQuickAccessWidget): - case nameof(UserSettingsService.GeneralSettingsService.ShowDrivesWidget): - case nameof(UserSettingsService.GeneralSettingsService.ShowNetworkLocationsWidget): - case nameof(UserSettingsService.GeneralSettingsService.ShowFileTagsWidget): - case nameof(UserSettingsService.GeneralSettingsService.ShowRecentFilesWidget): - RefreshWidgetsRequested?.Invoke(this, EventArgs.Empty); - OnPropertyChanged(e.SettingName); - break; - case nameof(UserSettingsService.LayoutSettingsService.DetailsViewSize): - case nameof(UserSettingsService.LayoutSettingsService.ListViewSize): - case nameof(UserSettingsService.LayoutSettingsService.ColumnsViewSize): - case nameof(UserSettingsService.LayoutSettingsService.GridViewSize): - OnPropertyChanged(nameof(IsLayoutSizeCompact)); - OnPropertyChanged(nameof(IsLayoutSizeSmall)); - OnPropertyChanged(nameof(IsLayoutSizeMedium)); - OnPropertyChanged(nameof(IsLayoutSizeLarge)); - OnPropertyChanged(nameof(IsLayoutSizeExtraLarge)); - break; - } - } - - private DispatcherQueue dispatcherQueue; - private DispatcherQueueTimer dragOverTimer; - - private ISearchBoxViewModel searchBox = new SearchBoxViewModel(); - public ISearchBoxViewModel SearchBox - { - get => searchBox; - set => SetProperty(ref searchBox, value); - } - - public SearchBoxViewModel SearchBoxViewModel - => (SearchBoxViewModel)SearchBox; - - public bool IsSingleItemOverride { get; set; } = false; - - private string? dragOverPath = null; - - public void PathBoxItem_DragLeave(object sender, DragEventArgs e) - { - if (((StackPanel)sender).DataContext is not PathBoxItem pathBoxItem || - pathBoxItem.Path == "Home") - { - return; - } - - // Reset dragged over pathbox item - if (pathBoxItem.Path == dragOverPath) - dragOverPath = null; - } - - private bool lockFlag = false; - - public async Task PathBoxItem_Drop(object sender, DragEventArgs e) - { - if (lockFlag) - return; - - lockFlag = true; - - // Reset dragged over pathbox item - dragOverPath = null; - - if (((StackPanel)sender).DataContext is not PathBoxItem pathBoxItem || - pathBoxItem.Path == "Home") - { - return; - } - - var deferral = e.GetDeferral(); - - var signal = new AsyncManualResetEvent(); - - PathBoxItemDropped?.Invoke(this, new PathBoxItemDroppedEventArgs() - { - AcceptedOperation = e.AcceptedOperation, - Package = e.DataView, - Path = pathBoxItem.Path, - SignalEvent = signal - }); - - await signal.WaitAsync(); - - deferral.Complete(); - await Task.Yield(); - - lockFlag = false; - } - - public async Task PathBoxItem_DragOver(object sender, DragEventArgs e) - { - if (IsSingleItemOverride || - ((StackPanel)sender).DataContext is not PathBoxItem pathBoxItem || - pathBoxItem.Path == "Home") - { - return; - } - - if (dragOverPath != pathBoxItem.Path) - { - dragOverPath = pathBoxItem.Path; - dragOverTimer.Stop(); - - if (dragOverPath != (this as IAddressToolbarViewModel).PathComponents.LastOrDefault()?.Path) - { - dragOverTimer.Debounce(() => - { - if (dragOverPath is not null) - { - dragOverTimer.Stop(); - ItemDraggedOverPathItem?.Invoke(this, new PathNavigationEventArgs() - { - ItemPath = dragOverPath - }); - dragOverPath = null; - } - }, - TimeSpan.FromMilliseconds(1000), false); - } - } - - // In search page - if (!FilesystemHelpers.HasDraggedStorageItems(e.DataView) || string.IsNullOrEmpty(pathBoxItem.Path)) - { - e.AcceptedOperation = DataPackageOperation.None; - - return; - } - - e.Handled = true; - var deferral = e.GetDeferral(); - - var storageItems = await FilesystemHelpers.GetDraggedStorageItems(e.DataView); - - if (!storageItems.Any(storageItem => - !string.IsNullOrEmpty(storageItem?.Path) && - storageItem.Path.Replace(pathBoxItem.Path, string.Empty, StringComparison.Ordinal) - .Trim(Path.DirectorySeparatorChar) - .Contains(Path.DirectorySeparatorChar))) - { - e.AcceptedOperation = DataPackageOperation.None; - } - - // Copy be default when dragging from zip - else if (storageItems.Any(x => - x.Item is ZipStorageFile || - x.Item is ZipStorageFolder) || - ZipStorageFolder.IsZipPath(pathBoxItem.Path)) - { - e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), pathBoxItem.Title); - e.AcceptedOperation = DataPackageOperation.Copy; - } - else - { - e.DragUIOverride.IsCaptionVisible = true; - e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalizedResource(), pathBoxItem.Title); - // Some applications such as Edge can't raise the drop event by the Move flag (#14008), so we set the Copy flag as well. - e.AcceptedOperation = DataPackageOperation.Move | DataPackageOperation.Copy; - } - - deferral.Complete(); - } - - public bool IsEditModeEnabled - { - get => ManualEntryBoxLoaded; - set - { - if (value) - { - EditModeEnabled?.Invoke(this, EventArgs.Empty); - - var visiblePath = AddressToolbar?.FindDescendant(x => x.Name == "VisiblePath"); - visiblePath?.Focus(FocusState.Programmatic); - visiblePath?.FindDescendant()?.SelectAll(); - - AddressBarTextEntered?.Invoke(this, new AddressBarTextEnteredEventArgs() { AddressBarTextField = visiblePath }); - } - else - { - IsCommandPaletteOpen = false; - ManualEntryBoxLoaded = false; - ClickablePathLoaded = true; - } - } - } - - private bool manualEntryBoxLoaded; - public bool ManualEntryBoxLoaded - { - get => manualEntryBoxLoaded; - set => SetProperty(ref manualEntryBoxLoaded, value); - } - - private bool clickablePathLoaded = true; - public bool ClickablePathLoaded - { - get => clickablePathLoaded; - set => SetProperty(ref clickablePathLoaded, value); - } - - private string pathControlDisplayText; - public string PathControlDisplayText - { - get => pathControlDisplayText; - set => SetProperty(ref pathControlDisplayText, value); - } - - public ICommand RefreshClickCommand { get; } - public ICommand ViewReleaseNotesAsyncCommand { get; } - - public void PathItemSeparator_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args) - { - var pathSeparatorIcon = sender as FontIcon; - if (pathSeparatorIcon is null || pathSeparatorIcon.DataContext is null) - return; - - ToolbarPathItemLoaded?.Invoke(pathSeparatorIcon, new ToolbarPathItemLoadedEventArgs() - { - Item = (PathBoxItem)pathSeparatorIcon.DataContext, - OpenedFlyout = (MenuFlyout)pathSeparatorIcon.ContextFlyout - }); - } - - public void PathboxItemFlyout_Opened(object sender, object e) - { - ToolbarFlyoutOpened?.Invoke(this, new ToolbarFlyoutOpenedEventArgs() { OpenedFlyout = (MenuFlyout)sender }); - } - - public void CurrentPathSetTextBox_TextChanged(object sender, TextChangedEventArgs args) - { - if (sender is TextBox textBox) - PathBoxQuerySubmitted?.Invoke(this, new ToolbarQuerySubmittedEventArgs() { QueryText = textBox.Text }); - } - - public void VisiblePath_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args) - { - if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput) - AddressBarTextEntered?.Invoke(this, new AddressBarTextEnteredEventArgs() { AddressBarTextField = sender }); - } - - public void VisiblePath_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args) - { - PathBoxQuerySubmitted?.Invoke(this, new ToolbarQuerySubmittedEventArgs() { QueryText = args.QueryText }); - - (this as IAddressToolbarViewModel).IsEditModeEnabled = false; - } - - public void PathBoxItem_PointerPressed(object sender, PointerRoutedEventArgs e) - { - if (e.Pointer.PointerDeviceType != Microsoft.UI.Input.PointerDeviceType.Mouse) - return; - - var ptrPt = e.GetCurrentPoint(AddressToolbar); - pointerRoutedEventArgs = ptrPt.Properties.IsMiddleButtonPressed ? e : null; - } - - public async Task PathBoxItem_Tapped(object sender, TappedRoutedEventArgs e) - { - var itemTappedPath = ((sender as TextBlock)?.DataContext as PathBoxItem)?.Path; - if (itemTappedPath is null) - return; - - if (pointerRoutedEventArgs is not null) - { - await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(async () => - { - await NavigationHelpers.AddNewTabByPathAsync(typeof(ShellPanesPage), itemTappedPath, true); - }, DispatcherQueuePriority.Low); - e.Handled = true; - pointerRoutedEventArgs = null; - - return; - } - - ToolbarPathItemInvoked?.Invoke(this, new PathNavigationEventArgs() - { - ItemPath = itemTappedPath - }); - } - - public void OpenCommandPalette() - { - PathText = ">"; - IsCommandPaletteOpen = true; - ManualEntryBoxLoaded = true; - ClickablePathLoaded = false; - - var visiblePath = AddressToolbar?.FindDescendant(x => x.Name == "VisiblePath"); - AddressBarTextEntered?.Invoke(this, new AddressBarTextEnteredEventArgs() { AddressBarTextField = visiblePath }); - } - - public void SwitchSearchBoxVisibility() - { - if (IsSearchBoxVisible) - { - CloseSearchBox(true); - } - else - { - IsSearchBoxVisible = true; - - // Given that binding and layouting might take a few cycles, when calling UpdateLayout - // we can guarantee that the focus call will be able to find an open ASB - var searchbox = AddressToolbar?.FindDescendant("SearchRegion") as SearchBox; - searchbox?.UpdateLayout(); - searchbox?.Focus(FocusState.Programmatic); - } - } - - public void UpdateAdditionalActions() - { - OnPropertyChanged(nameof(HasAdditionalAction)); - } - - private AddressToolbar? AddressToolbar => (MainWindow.Instance.Content as Frame)?.FindDescendant(); - - private void CloseSearchBox(bool doFocus = false) - { - if (searchBox.WasQuerySubmitted) - { - searchBox.WasQuerySubmitted = false; - } - else - { - IsSearchBoxVisible = false; - - if (doFocus) - { - SearchBox.Query = string.Empty; - - var page = Ioc.Default.GetRequiredService().ShellPage?.SlimContentPage; - - if (page is BaseGroupableLayoutPage svb && svb.IsLoaded) - page.ItemManipulationModel.FocusFileList(); - else - AddressToolbar?.Focus(FocusState.Programmatic); - } - } - } - - public bool SearchHasFocus { get; private set; } - - public void SearchRegion_GotFocus(object sender, RoutedEventArgs e) - { - SearchHasFocus = true; - } - - public void SearchRegion_LostFocus(object sender, RoutedEventArgs e) - { - var element = FocusManager.GetFocusedElement(); - if (element is FlyoutBase or AppBarButton) - return; - - SearchHasFocus = false; - CloseSearchBox(); - } - - private void SearchRegion_Escaped(object? sender, ISearchBoxViewModel searchBox) - => CloseSearchBox(true); - - public IAsyncRelayCommand? OpenNewWindowCommand { get; set; } - - public ICommand? CreateNewFileCommand { get; set; } - - public ICommand? Share { get; set; } - - public ICommand? UpdateCommand { get; set; } - - public async Task SetPathBoxDropDownFlyoutAsync(MenuFlyout flyout, PathBoxItem pathItem, IShellPage shellPage) - { - var nextPathItemTitle = PathComponents[PathComponents.IndexOf(pathItem) + 1].Title; - IList? childFolders = null; - - StorageFolderWithPath folder = await shellPage.ShellViewModel.GetFolderWithPathFromPathAsync(pathItem.Path); - if (folder is not null) - childFolders = (await FilesystemTasks.Wrap(() => folder.GetFoldersWithPathAsync(string.Empty))).Result; - - flyout.Items?.Clear(); - - if (childFolders is null || childFolders.Count == 0) - { - var flyoutItem = new MenuFlyoutItem - { - Icon = new FontIcon { Glyph = "\uE7BA" }, - Text = "SubDirectoryAccessDenied".GetLocalizedResource(), - //Foreground = (SolidColorBrush)Application.Current.Resources["SystemControlErrorTextForegroundBrush"], - FontSize = 12 - }; - - flyout.Items?.Add(flyoutItem); - - return; - } - - var boldFontWeight = new FontWeight { Weight = 800 }; - var normalFontWeight = new FontWeight { Weight = 400 }; - - var workingPath = - PathComponents[PathComponents.Count - 1].Path?.TrimEnd(Path.DirectorySeparatorChar); - - foreach (var childFolder in childFolders) - { - var isPathItemFocused = childFolder.Item.Name == nextPathItemTitle; - - var flyoutItem = new MenuFlyoutItem - { - Icon = new FontIcon - { - Glyph = "\uED25", - FontWeight = isPathItemFocused ? boldFontWeight : normalFontWeight - }, - Text = childFolder.Item.Name, - FontSize = 12, - FontWeight = isPathItemFocused ? boldFontWeight : normalFontWeight - }; - - if (workingPath != childFolder.Path) - { - flyoutItem.Click += (sender, args) => - { - // Navigate to the directory - shellPage.NavigateToPath(childFolder.Path); - }; - } - - flyout.Items?.Add(flyoutItem); - } - } - - private static string NormalizePathInput(string currentInput, bool isFtp) - { - if (currentInput.Contains('/') && !isFtp) - currentInput = currentInput.Replace("/", "\\", StringComparison.Ordinal); - - currentInput = currentInput.Replace("\\\\", "\\", StringComparison.Ordinal); - - if (currentInput.StartsWith('\\') && !currentInput.StartsWith("\\\\", StringComparison.Ordinal)) - currentInput = currentInput.Insert(0, "\\"); - - return currentInput; - } - - public async Task CheckPathInputAsync(string currentInput, string currentSelectedPath, IShellPage shellPage) - { - if (currentInput.StartsWith('>')) - { - var code = currentInput.Substring(1).Trim(); - var command = Commands[code]; - - if (command == Commands.None) - await DialogDisplayHelper.ShowDialogAsync("InvalidCommand".GetLocalizedResource(), - string.Format("InvalidCommandContent".GetLocalizedResource(), code)); - else if (!command.IsExecutable) - await DialogDisplayHelper.ShowDialogAsync("CommandNotExecutable".GetLocalizedResource(), - string.Format("CommandNotExecutableContent".GetLocalizedResource(), command.Code)); - else - await command.ExecuteAsync(); - - return; - } - - var isFtp = FtpHelpers.IsFtpPath(currentInput); - - var normalizedInput = NormalizePathInput(currentInput, isFtp); - - if (currentSelectedPath == normalizedInput || string.IsNullOrWhiteSpace(normalizedInput)) - return; - - if (normalizedInput != shellPage.ShellViewModel.WorkingDirectory || shellPage.CurrentPageType == typeof(HomePage)) - { - if (normalizedInput.Equals("Home", StringComparison.OrdinalIgnoreCase) || normalizedInput.Equals("Home".GetLocalizedResource(), StringComparison.OrdinalIgnoreCase)) - { - SavePathToHistory("Home"); - shellPage.NavigateHome(); - } - else - { - normalizedInput = StorageFileExtensions.GetResolvedPath(normalizedInput, isFtp); - if (currentSelectedPath == normalizedInput) - return; - - var item = await FilesystemTasks.Wrap(() => DriveHelpers.GetRootFromPathAsync(normalizedInput)); - - var resFolder = await FilesystemTasks.Wrap(() => StorageFileExtensions.DangerousGetFolderWithPathFromPathAsync(normalizedInput, item)); - if (resFolder || FolderHelpers.CheckFolderAccessWithWin32(normalizedInput)) - { - var matchingDrive = drivesViewModel.Drives.Cast().FirstOrDefault(x => PathNormalization.NormalizePath(normalizedInput).StartsWith(PathNormalization.NormalizePath(x.Path), StringComparison.Ordinal)); - if (matchingDrive is not null && matchingDrive.Type == Data.Items.DriveType.CDRom && matchingDrive.MaxSpace == ByteSizeLib.ByteSize.FromBytes(0)) - { - bool ejectButton = await DialogDisplayHelper.ShowDialogAsync("InsertDiscDialog/Title".GetLocalizedResource(), string.Format("InsertDiscDialog/Text".GetLocalizedResource(), matchingDrive.Path), "InsertDiscDialog/OpenDriveButton".GetLocalizedResource(), "Close".GetLocalizedResource()); - if (ejectButton) - { - var result = await DriveHelpers.EjectDeviceAsync(matchingDrive.Path); - await UIHelpers.ShowDeviceEjectResultAsync(matchingDrive.Type, result); - } - return; - } - var pathToNavigate = resFolder.Result?.Path ?? normalizedInput; - SavePathToHistory(pathToNavigate); - shellPage.NavigateToPath(pathToNavigate); - } - else if (isFtp) - { - SavePathToHistory(normalizedInput); - shellPage.NavigateToPath(normalizedInput); - } - else // Not a folder or inaccessible - { - var resFile = await FilesystemTasks.Wrap(() => StorageFileExtensions.DangerousGetFileWithPathFromPathAsync(normalizedInput, item)); - if (resFile) - { - var pathToInvoke = resFile.Result.Path; - await Win32Helper.InvokeWin32ComponentAsync(pathToInvoke, shellPage); - } - else // Not a file or not accessible - { - var workingDir = - string.IsNullOrEmpty(shellPage.ShellViewModel.WorkingDirectory) || - shellPage.CurrentPageType == typeof(HomePage) ? - Constants.UserEnvironmentPaths.HomePath : - shellPage.ShellViewModel.WorkingDirectory; - - if (await LaunchApplicationFromPath(currentInput, workingDir)) - return; - - try - { - if (!await Windows.System.Launcher.LaunchUriAsync(new Uri(currentInput))) - await DialogDisplayHelper.ShowDialogAsync("InvalidItemDialogTitle".GetLocalizedResource(), - string.Format("InvalidItemDialogContent".GetLocalizedResource(), Environment.NewLine, resFolder.ErrorCode.ToString())); - } - catch (Exception ex) when (ex is UriFormatException || ex is ArgumentException) - { - await DialogDisplayHelper.ShowDialogAsync("InvalidItemDialogTitle".GetLocalizedResource(), - string.Format("InvalidItemDialogContent".GetLocalizedResource(), Environment.NewLine, resFolder.ErrorCode.ToString())); - } - } - } - } - - PathControlDisplayText = shellPage.ShellViewModel.WorkingDirectory; - } - } - - private void SavePathToHistory(string path) - { - var pathHistoryList = UserSettingsService.GeneralSettingsService.PathHistoryList?.ToList() ?? []; - pathHistoryList.Remove(path); - pathHistoryList.Insert(0, path); - - if (pathHistoryList.Count > MAX_SUGGESTIONS) - UserSettingsService.GeneralSettingsService.PathHistoryList = pathHistoryList.RemoveFrom(MAX_SUGGESTIONS + 1); - else - UserSettingsService.GeneralSettingsService.PathHistoryList = pathHistoryList; - } - - private static async Task LaunchApplicationFromPath(string currentInput, string workingDir) - { - var args = CommandLineParser.SplitArguments(currentInput); - return await LaunchHelper.LaunchAppAsync( - args.FirstOrDefault("").Trim('"'), string.Join(' ', args.Skip(1)), workingDir - ); - } - - public async Task SetAddressBarSuggestionsAsync(AutoSuggestBox sender, IShellPage shellpage) - { - if (sender.Text is not null && shellpage.ShellViewModel is not null) - { - if (!await SafetyExtensions.IgnoreExceptions(async () => - { - IList? suggestions = null; - - if (sender.Text.StartsWith(">")) - { - IsCommandPaletteOpen = true; - var searchText = sender.Text.Substring(1).Trim(); - suggestions = Commands.Where(command => - command.IsExecutable && - command.IsAccessibleGlobally && - (command.Description.Contains(searchText, StringComparison.OrdinalIgnoreCase) || - command.Code.ToString().Contains(searchText, StringComparison.OrdinalIgnoreCase))) - .Select(command => new NavigationBarSuggestionItem() - { - Text = ">" + command.Code, - PrimaryDisplay = command.Description, - HotKeys = command.HotKeys, - SearchText = searchText, - }).ToList(); - } - else - { - IsCommandPaletteOpen = false; - var currentInput = sender.Text; - - if (string.IsNullOrWhiteSpace(currentInput) || currentInput == "Home") - { - // Load previously entered path - var pathHistoryList = UserSettingsService.GeneralSettingsService.PathHistoryList; - if (pathHistoryList is not null) - { - suggestions = pathHistoryList.Select(x => new NavigationBarSuggestionItem() - { - Text = x, - PrimaryDisplay = x - }).ToList(); - } - } - else - { - var isFtp = FtpHelpers.IsFtpPath(currentInput); - currentInput = NormalizePathInput(currentInput, isFtp); - var expandedPath = StorageFileExtensions.GetResolvedPath(currentInput, isFtp); - var folderPath = PathNormalization.GetParentDir(expandedPath) ?? expandedPath; - StorageFolderWithPath folder = await shellpage.ShellViewModel.GetFolderWithPathFromPathAsync(folderPath); - - if (folder is null) - return false; - - var currPath = await folder.GetFoldersWithPathAsync(Path.GetFileName(expandedPath), (uint)MAX_SUGGESTIONS); - if (currPath.Count >= MAX_SUGGESTIONS) - { - suggestions = currPath.Select(x => new NavigationBarSuggestionItem() - { - Text = x.Path, - PrimaryDisplay = x.Item.DisplayName - }).ToList(); - } - else if (currPath.Any()) - { - var subPath = await currPath.First().GetFoldersWithPathAsync((uint)(MAX_SUGGESTIONS - currPath.Count)); - suggestions = currPath.Select(x => new NavigationBarSuggestionItem() - { - Text = x.Path, - PrimaryDisplay = x.Item.DisplayName - }).Concat( - subPath.Select(x => new NavigationBarSuggestionItem() - { - Text = x.Path, - PrimaryDisplay = PathNormalization.Combine(currPath.First().Item.DisplayName, x.Item.DisplayName) - })).ToList(); - } - } - } - - if (suggestions is null || suggestions.Count == 0) - { - suggestions = new List() { new NavigationBarSuggestionItem() { - Text = shellpage.ShellViewModel.WorkingDirectory, - PrimaryDisplay = "NavigationToolbarVisiblePathNoResults".GetLocalizedResource() } }; - } - - // NavigationBarSuggestions becoming empty causes flickering of the suggestion box - // Here we check whether at least an element is in common between old and new list - if (!NavigationBarSuggestions.IntersectBy(suggestions, x => x.PrimaryDisplay).Any()) - { - // No elements in common, update the list in-place - for (int index = 0; index < suggestions.Count; index++) - { - if (index < NavigationBarSuggestions.Count) - { - NavigationBarSuggestions[index].Text = suggestions[index].Text; - NavigationBarSuggestions[index].PrimaryDisplay = suggestions[index].PrimaryDisplay; - NavigationBarSuggestions[index].SearchText = suggestions[index].SearchText; - NavigationBarSuggestions[index].HotKeys = suggestions[index].HotKeys; - } - else - { - NavigationBarSuggestions.Add(suggestions[index]); - } - } - - while (NavigationBarSuggestions.Count > suggestions.Count) - NavigationBarSuggestions.RemoveAt(NavigationBarSuggestions.Count - 1); - } - else - { - // At least an element in common, show animation - foreach (var s in NavigationBarSuggestions.ExceptBy(suggestions, x => x.PrimaryDisplay).ToList()) - NavigationBarSuggestions.Remove(s); - - for (int index = 0; index < suggestions.Count; index++) - { - if (NavigationBarSuggestions.Count > index && NavigationBarSuggestions[index].PrimaryDisplay == suggestions[index].PrimaryDisplay) - { - NavigationBarSuggestions[index].SearchText = suggestions[index].SearchText; - NavigationBarSuggestions[index].HotKeys = suggestions[index].HotKeys; - } - else - NavigationBarSuggestions.Insert(index, suggestions[index]); - } - } - - return true; - })) - { - SafetyExtensions.IgnoreExceptions(() => { - NavigationBarSuggestions.Clear(); - NavigationBarSuggestions.Add(new NavigationBarSuggestionItem() - { - Text = shellpage.ShellViewModel.WorkingDirectory, - PrimaryDisplay = "NavigationToolbarVisiblePathNoResults".GetLocalizedResource() - }); - }); - } - } - } - - private void FolderSettings_PropertyChanged(object? sender, PropertyChangedEventArgs e) - { - switch (e.PropertyName) - { - case nameof(LayoutPreferencesManager.LayoutMode): - LayoutOpacityIcon = instanceViewModel.FolderSettings.LayoutMode switch - { - FolderLayoutModes.ListView => Commands.LayoutList.OpacityStyle!, - FolderLayoutModes.TilesView => Commands.LayoutTiles.OpacityStyle!, - FolderLayoutModes.ColumnView => Commands.LayoutColumns.OpacityStyle!, - FolderLayoutModes.GridView => Commands.LayoutGrid.OpacityStyle!, - _ => Commands.LayoutDetails.OpacityStyle! - }; - OnPropertyChanged(nameof(IsTilesLayout)); - OnPropertyChanged(nameof(IsListLayout)); - OnPropertyChanged(nameof(IsColumnLayout)); - OnPropertyChanged(nameof(IsGridLayout)); - OnPropertyChanged(nameof(IsDetailsLayout)); - OnPropertyChanged(nameof(IsLayoutSizeCompact)); - OnPropertyChanged(nameof(IsLayoutSizeSmall)); - OnPropertyChanged(nameof(IsLayoutSizeMedium)); - OnPropertyChanged(nameof(IsLayoutSizeLarge)); - OnPropertyChanged(nameof(IsLayoutSizeExtraLarge)); - break; - } - } - - private bool hasItem = false; - public bool HasItem - { - get => hasItem; - set => SetProperty(ref hasItem, value); - } - - private List? selectedItems; - - public List SelectedItems - { - get => selectedItems; - set - { - if (SetProperty(ref selectedItems, value)) - { - OnPropertyChanged(nameof(CanCopy)); - OnPropertyChanged(nameof(CanExtract)); - OnPropertyChanged(nameof(ExtractToText)); - OnPropertyChanged(nameof(IsArchiveOpened)); - OnPropertyChanged(nameof(IsSelectionArchivesOnly)); - OnPropertyChanged(nameof(IsMultipleArchivesSelected)); - OnPropertyChanged(nameof(IsInfFile)); - OnPropertyChanged(nameof(IsPowerShellScript)); - OnPropertyChanged(nameof(IsImage)); - OnPropertyChanged(nameof(IsMultipleImageSelected)); - OnPropertyChanged(nameof(IsFont)); - OnPropertyChanged(nameof(HasAdditionalAction)); - } - } - } - - public bool HasAdditionalAction => InstanceViewModel.IsPageTypeRecycleBin || IsPowerShellScript || CanExtract || IsImage || IsFont || IsInfFile; - public bool CanCopy => SelectedItems is not null && SelectedItems.Any(); - public bool CanExtract => IsArchiveOpened ? (SelectedItems is null || !SelectedItems.Any()) : IsSelectionArchivesOnly; - public bool IsArchiveOpened => FileExtensionHelpers.IsZipFile(Path.GetExtension(pathControlDisplayText)); - public bool IsSelectionArchivesOnly => SelectedItems is not null && SelectedItems.Any() && SelectedItems.All(x => FileExtensionHelpers.IsZipFile(x.FileExtension)) && !InstanceViewModel.IsPageTypeRecycleBin; - public bool IsMultipleArchivesSelected => IsSelectionArchivesOnly && SelectedItems.Count > 1; - public bool IsPowerShellScript => SelectedItems is not null && SelectedItems.Count == 1 && FileExtensionHelpers.IsPowerShellFile(SelectedItems.First().FileExtension) && !InstanceViewModel.IsPageTypeRecycleBin; - public bool IsImage => SelectedItems is not null && SelectedItems.Any() && SelectedItems.All(x => FileExtensionHelpers.IsImageFile(x.FileExtension)) && !InstanceViewModel.IsPageTypeRecycleBin; - public bool IsMultipleImageSelected => SelectedItems is not null && SelectedItems.Count > 1 && SelectedItems.All(x => FileExtensionHelpers.IsImageFile(x.FileExtension)) && !InstanceViewModel.IsPageTypeRecycleBin; - public bool IsInfFile => SelectedItems is not null && SelectedItems.Count == 1 && FileExtensionHelpers.IsInfFile(SelectedItems.First().FileExtension) && !InstanceViewModel.IsPageTypeRecycleBin; - public bool IsFont => SelectedItems is not null && SelectedItems.Any() && SelectedItems.All(x => FileExtensionHelpers.IsFontFile(x.FileExtension)) && !InstanceViewModel.IsPageTypeRecycleBin; - - public bool IsTilesLayout => instanceViewModel.FolderSettings.LayoutMode is FolderLayoutModes.TilesView; - public bool IsColumnLayout => instanceViewModel.FolderSettings.LayoutMode is FolderLayoutModes.ColumnView; - public bool IsGridLayout => instanceViewModel.FolderSettings.LayoutMode is FolderLayoutModes.GridView; - public bool IsDetailsLayout => instanceViewModel.FolderSettings.LayoutMode is FolderLayoutModes.DetailsView; - public bool IsListLayout => instanceViewModel.FolderSettings.LayoutMode is FolderLayoutModes.ListView; - - public bool IsLayoutSizeCompact => - (IsDetailsLayout && UserSettingsService.LayoutSettingsService.DetailsViewSize == DetailsViewSizeKind.Compact) || - (IsListLayout && UserSettingsService.LayoutSettingsService.ListViewSize == ListViewSizeKind.Compact) || - (IsColumnLayout && UserSettingsService.LayoutSettingsService.ColumnsViewSize == ColumnsViewSizeKind.Compact); - - public bool IsLayoutSizeSmall => - (IsDetailsLayout && UserSettingsService.LayoutSettingsService.DetailsViewSize == DetailsViewSizeKind.Small) || - (IsListLayout && UserSettingsService.LayoutSettingsService.ListViewSize == ListViewSizeKind.Small) || - (IsColumnLayout && UserSettingsService.LayoutSettingsService.ColumnsViewSize == ColumnsViewSizeKind.Small) || - (IsGridLayout && UserSettingsService.LayoutSettingsService.GridViewSize == GridViewSizeKind.Small); - - public bool IsLayoutSizeMedium => - (IsDetailsLayout && UserSettingsService.LayoutSettingsService.DetailsViewSize == DetailsViewSizeKind.Medium) || - (IsListLayout && UserSettingsService.LayoutSettingsService.ListViewSize == ListViewSizeKind.Medium) || - (IsColumnLayout && UserSettingsService.LayoutSettingsService.ColumnsViewSize == ColumnsViewSizeKind.Medium) || - (IsGridLayout && UserSettingsService.LayoutSettingsService.GridViewSize == GridViewSizeKind.Medium); - - public bool IsLayoutSizeLarge => - (IsDetailsLayout && UserSettingsService.LayoutSettingsService.DetailsViewSize == DetailsViewSizeKind.Large) || - (IsListLayout && UserSettingsService.LayoutSettingsService.ListViewSize == ListViewSizeKind.Large) || - (IsColumnLayout && UserSettingsService.LayoutSettingsService.ColumnsViewSize == ColumnsViewSizeKind.Large) || - (IsGridLayout && UserSettingsService.LayoutSettingsService.GridViewSize == GridViewSizeKind.Large); - - public bool IsLayoutSizeExtraLarge => - (IsDetailsLayout && UserSettingsService.LayoutSettingsService.DetailsViewSize == DetailsViewSizeKind.ExtraLarge) || - (IsListLayout && UserSettingsService.LayoutSettingsService.ListViewSize == ListViewSizeKind.ExtraLarge) || - (IsColumnLayout && UserSettingsService.LayoutSettingsService.ColumnsViewSize == ColumnsViewSizeKind.ExtraLarge) || - (IsGridLayout && UserSettingsService.LayoutSettingsService.GridViewSize == GridViewSizeKind.ExtraLarge); - - public string ExtractToText - => IsSelectionArchivesOnly ? SelectedItems.Count > 1 ? string.Format("ExtractToChildFolder".GetLocalizedResource(), $"*{Path.DirectorySeparatorChar}") : string.Format("ExtractToChildFolder".GetLocalizedResource() + "\\", Path.GetFileNameWithoutExtension(selectedItems.First().Name)) : "ExtractToChildFolder".GetLocalizedResource(); - - public void Dispose() - { - SearchBox.Escaped -= SearchRegion_Escaped; - UserSettingsService.OnSettingChangedEvent -= UserSettingsService_OnSettingChangedEvent; - } - } -} diff --git a/src/Files.App/ViewModels/UserControls/InfoPaneViewModel.cs b/src/Files.App/ViewModels/UserControls/InfoPaneViewModel.cs index 1c5f569b7c5c..7208175f8dfa 100644 --- a/src/Files.App/ViewModels/UserControls/InfoPaneViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/InfoPaneViewModel.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.UserControls.FilePreviews; using Files.App.ViewModels.Previews; @@ -7,16 +7,16 @@ using Microsoft.Extensions.Logging; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; -using Sentry; +using System.Collections.Specialized; using Windows.Storage; namespace Files.App.ViewModels.UserControls { - public sealed class InfoPaneViewModel : ObservableObject, IDisposable + public sealed partial class InfoPaneViewModel : ObservableObject, IDisposable { private IInfoPaneSettingsService infoPaneSettingsService { get; } = Ioc.Default.GetRequiredService(); - private IGeneralSettingsService generalSettingsService { get; } = Ioc.Default.GetRequiredService(); private IContentPageContext contentPageContext { get; } = Ioc.Default.GetRequiredService(); + private DrivesViewModel drivesViewModel { get; } = Ioc.Default.GetRequiredService(); private CancellationTokenSource loadCancellationTokenSource; @@ -29,7 +29,7 @@ public bool IsEnabled get => isEnabled; set { - infoPaneSettingsService.IsEnabled = value; + infoPaneSettingsService.IsInfoPaneEnabled = value; SetProperty(ref isEnabled, value); } @@ -51,6 +51,7 @@ public ListedItem? SelectedItem if (SetProperty(ref selectedItem, value)) { UpdateTagsItems(); + SetDriveItem(); OnPropertyChanged(nameof(LoadTagsList)); if (value is not null) @@ -59,6 +60,19 @@ public ListedItem? SelectedItem } } + /// + /// Current selected drive if any. + /// + private DriveItem? selectedDriveItem; + public DriveItem? SelectedDriveItem + { + get => selectedDriveItem; + set + { + SetProperty(ref selectedDriveItem, value); + } + } + /// /// Enum indicating whether to show the details or preview tab /// @@ -88,6 +102,13 @@ public PreviewPaneStates PreviewPaneState } } + private string? _DirectoryItemCount; + public string? DirectoryItemCount + { + get => _DirectoryItemCount; + set => SetProperty(ref _DirectoryItemCount, value); + } + /// /// Value indicating if the download cloud files option should be displayed /// @@ -102,7 +123,13 @@ public bool ShowCloudItemButton public UIElement PreviewPaneContent { get => previewPaneContent; - set => SetProperty(ref previewPaneContent, value); + set + { + var oldType = previewPaneContent?.GetType()?.Name; + var newType = value?.GetType()?.Name; + App.Logger.LogDebug($"PreviewPaneContent changing: {oldType} -> {newType}"); + SetProperty(ref previewPaneContent, value); + } } public bool LoadTagsList @@ -116,8 +143,25 @@ public InfoPaneViewModel() { infoPaneSettingsService.PropertyChanged += PreviewSettingsService_OnPropertyChangedEvent; contentPageContext.PropertyChanged += ContentPageContext_PropertyChanged; + CloudDrivesManager.DataChanged += CloudDrivesManager_DataChanged; - IsEnabled = infoPaneSettingsService.IsEnabled; + IsEnabled = infoPaneSettingsService.IsInfoPaneEnabled; + } + + private void CloudDrivesManager_DataChanged(object? sender, NotifyCollectionChangedEventArgs e) + { + _ = MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => + { + if (SelectedItem is null) + return; + + SetDriveItem(); + + if (PreviewPaneState is PreviewPaneStates.PreviewAndDetailsAvailable && SelectedDriveItem is not null) + PreviewPaneState = PreviewPaneStates.DriveStorageDetailsAvailable; + else if (PreviewPaneState is PreviewPaneStates.DriveStorageDetailsAvailable && SelectedDriveItem is null) + PreviewPaneState = PreviewPaneStates.PreviewAndDetailsAvailable; + }); } private async void ContentPageContext_PropertyChanged(object? sender, PropertyChangedEventArgs e) @@ -162,7 +206,7 @@ private async void ContentPageContext_PropertyChanged(object? sender, PropertyCh private async Task LoadPreviewControlAsync(CancellationToken token, bool downloadItem) { - if (SelectedItem.IsHiddenItem && !SelectedItem.ItemPath.EndsWith("\\")) + if (SelectedItem.IsHiddenItem && !SelectedItem.ItemPath.EndsWith('\\')) { PreviewPaneState = PreviewPaneStates.NoPreviewOrDetailsAvailable; @@ -178,7 +222,7 @@ private async Task LoadPreviewControlAsync(CancellationToken token, bool downloa if (control is not null) { PreviewPaneContent = control; - PreviewPaneState = PreviewPaneStates.PreviewAndDetailsAvailable; + PreviewPaneState = SelectedDriveItem is not null ? PreviewPaneStates.DriveStorageDetailsAvailable : PreviewPaneStates.PreviewAndDetailsAvailable; return; } @@ -191,7 +235,7 @@ private async Task LoadPreviewControlAsync(CancellationToken token, bool downloa return; PreviewPaneContent = control; - PreviewPaneState = PreviewPaneStates.PreviewAndDetailsAvailable; + PreviewPaneState = SelectedDriveItem is not null ? PreviewPaneStates.DriveStorageDetailsAvailable : PreviewPaneStates.PreviewAndDetailsAvailable; } private async Task GetBuiltInPreviewControlAsync(ListedItem item, bool downloadItem) @@ -265,7 +309,7 @@ private async Task GetBuiltInPreviewControlAsync(ListedItem item, b return new MediaPreview(model); } - if (MarkdownPreviewViewModel.ContainsExtension(ext)) + if (FileExtensionHelpers.IsMarkdownFile(ext)) { var model = new MarkdownPreviewViewModel(item); await model.LoadAsync(); @@ -281,7 +325,7 @@ private async Task GetBuiltInPreviewControlAsync(ListedItem item, b return new ImagePreview(model); } - if (TextPreviewViewModel.ContainsExtension(ext)) + if (FileExtensionHelpers.IsTextFile(ext)) { var model = new TextPreviewViewModel(item); await model.LoadAsync(); @@ -305,7 +349,7 @@ private async Task GetBuiltInPreviewControlAsync(ListedItem item, b return new HtmlPreview(model); }*/ - if (RichTextPreviewViewModel.ContainsExtension(ext)) + if (FileExtensionHelpers.IsRichTextFile(ext)) { var model = new RichTextPreviewViewModel(item); await model.LoadAsync(); @@ -321,12 +365,9 @@ private async Task GetBuiltInPreviewControlAsync(ListedItem item, b return new CodePreview(model); } - if - ( - ShellPreviewViewModel.FindPreviewHandlerFor(item.FileExtension, 0) is not null && + if (ShellPreviewViewModel.FindPreviewHandlerFor(item.FileExtension, 0) is not null && !FileExtensionHelpers.IsFontFile(item.FileExtension) && - !FileExtensionHelpers.IsExecutableFile(item.FileExtension) - ) + !FileExtensionHelpers.IsExecutableFile(item.FileExtension)) { var model = new ShellPreviewViewModel(item); await model.LoadAsync(); @@ -342,7 +383,13 @@ private async Task GetBuiltInPreviewControlAsync(ListedItem item, b public async Task UpdateSelectedItemPreviewAsync(bool downloadItem = false) { loadCancellationTokenSource?.Cancel(); - if (SelectedItem is not null && contentPageContext.SelectedItems.Count == 1) + if (contentPageContext.PageType is ContentPageTypes.ReleaseNotes || contentPageContext.PageType is ContentPageTypes.Settings) + { + PreviewPaneState = PreviewPaneStates.NoPreviewOrDetailsAvailable; + PreviewPaneContent = null; + return; + } + else if (SelectedItem is not null && contentPageContext.SelectedItems.Count == 1) { SelectedItem?.FileDetails?.Clear(); @@ -433,9 +480,9 @@ private async void PreviewSettingsService_OnPropertyChangedEvent(object? sender, // The preview will need refreshing as the file details won't be accurate await UpdateSelectedItemPreviewAsync(); } - else if (e.PropertyName is nameof(infoPaneSettingsService.IsEnabled)) + else if (e.PropertyName is nameof(infoPaneSettingsService.IsInfoPaneEnabled)) { - var newEnablingStatus = infoPaneSettingsService.IsEnabled; + var newEnablingStatus = infoPaneSettingsService.IsInfoPaneEnabled; if (isEnabled != newEnablingStatus) { isEnabled = newEnablingStatus; @@ -453,7 +500,7 @@ private async Task LoadBasicPreviewAsync() await basicModel.LoadAsync(); PreviewPaneContent = new BasicPreview(basicModel); - PreviewPaneState = PreviewPaneStates.PreviewAndDetailsAvailable; + PreviewPaneState = SelectedDriveItem is not null ? PreviewPaneStates.DriveStorageDetailsAvailable : PreviewPaneStates.PreviewAndDetailsAvailable; } catch (Exception ex) { @@ -475,12 +522,48 @@ private void UpdateTagsItems() SelectedItem?.FileTagsUI?.ForEach(tag => Items.Add(new TagItem(tag))); - Items.Add(new FlyoutItem(new Files.App.UserControls.Menus.FileTagsContextMenu(new List() { SelectedItem }))); + var contextMenu = new Files.App.UserControls.Menus.FileTagsContextMenu(new List() { SelectedItem }); + contextMenu.Closed += HandleClosed; + contextMenu.TagsChanged += RequireTagGroupsUpdate; + + Items.Add(new FlyoutItem(contextMenu)); + + async void RequireTagGroupsUpdate(object? sender, EventArgs e) + { + if (contentPageContext.ShellPage is not null) + await contentPageContext.ShellPage.ShellViewModel.RefreshTagGroups(); + } + + void HandleClosed(object? sender, object e) + { + contextMenu.TagsChanged -= RequireTagGroupsUpdate; + contextMenu.Closed -= HandleClosed; + } + } + + private void SetDriveItem() + { + if (selectedItem is null) + { + selectedDriveItem = null; + return; + } + + var normalizedPath = PathNormalization.NormalizePath(selectedItem.ItemPath); + + SelectedDriveItem = drivesViewModel.Drives + .OfType() + .Concat(CloudDrivesManager.Drives) + .FirstOrDefault(d => !string.IsNullOrWhiteSpace(d.Path) && + normalizedPath.Equals(PathNormalization.NormalizePath(d.Path), StringComparison.OrdinalIgnoreCase)); } public void Dispose() { - + loadCancellationTokenSource?.Cancel(); + infoPaneSettingsService.PropertyChanged -= PreviewSettingsService_OnPropertyChangedEvent; + contentPageContext.PropertyChanged -= ContentPageContext_PropertyChanged; + CloudDrivesManager.DataChanged -= CloudDrivesManager_DataChanged; } } } diff --git a/src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs b/src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs new file mode 100644 index 000000000000..86637f140947 --- /dev/null +++ b/src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs @@ -0,0 +1,1223 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using CommunityToolkit.WinUI; +using Files.App.Controls; +using Files.Shared.Helpers; +using Microsoft.Extensions.Logging; +using Microsoft.UI.Dispatching; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media.Imaging; +using System.IO; +using System.Windows.Input; +using Windows.AI.Actions; +using Windows.ApplicationModel.DataTransfer; +using Windows.UI.Text; + +namespace Files.App.ViewModels.UserControls +{ + public sealed partial class NavigationToolbarViewModel : ObservableObject, IAddressToolbarViewModel, IDisposable + { + // Constants + + private const int MaxSuggestionsCount = 10; + + public const string OmnibarPathModeName = "OmnibarPathMode"; + public const string OmnibarPaletteModeName = "OmnibarCommandPaletteMode"; + public const string OmnibarSearchModeName = "OmnibarSearchMode"; + + // Dependency injections + + private readonly IUserSettingsService UserSettingsService = Ioc.Default.GetRequiredService(); + private readonly IAppearanceSettingsService AppearanceSettingsService = Ioc.Default.GetRequiredService(); + private readonly DrivesViewModel drivesViewModel = Ioc.Default.GetRequiredService(); + private readonly IUpdateService UpdateService = Ioc.Default.GetRequiredService(); + private readonly ICommandManager Commands = Ioc.Default.GetRequiredService(); + private readonly IContentPageContext ContentPageContext = Ioc.Default.GetRequiredService(); + private readonly StatusCenterViewModel OngoingTasksViewModel = Ioc.Default.GetRequiredService(); + + // Fields + + private readonly DispatcherQueue _dispatcherQueue; + private readonly DispatcherQueueTimer _dragOverTimer; + + private string? _dragOverPath; + private bool _lockFlag; + private PointerRoutedEventArgs? _pointerRoutedEventArgs; + + // Events + + public delegate void ToolbarPathItemInvokedEventHandler(object sender, PathNavigationEventArgs e); + public delegate void PathBoxItemDroppedEventHandler(object sender, PathBoxItemDroppedEventArgs e); + public event ToolbarPathItemInvokedEventHandler? ToolbarPathItemInvoked; + public event IAddressToolbarViewModel.ItemDraggedOverPathItemEventHandler? ItemDraggedOverPathItem; + public event IAddressToolbarViewModel.ToolbarQuerySubmittedEventHandler? PathBoxQuerySubmitted; + + public event PathBoxItemDroppedEventHandler? PathBoxItemDropped; + public event EventHandler? RefreshWidgetsRequested; + + // Properties + + internal static ActionRuntime? ActionRuntime { get; private set; } + + public ObservableCollection PathComponents { get; } = []; + + public ObservableCollection NavigationBarSuggestions { get; } = []; + + internal ObservableCollection PathModeSuggestionItems { get; } = []; + + internal ObservableCollection OmnibarCommandPaletteModeSuggestionItems { get; } = []; + + internal ObservableCollection OmnibarSearchModeSuggestionItems { get; } = []; + + public bool IsSingleItemOverride { get; set; } + + public bool ShowStatusCenterButton => + AppearanceSettingsService.StatusCenterVisibility == StatusCenterVisibility.Always || + (AppearanceSettingsService.StatusCenterVisibility == StatusCenterVisibility.DuringOngoingFileOperations && OngoingTasksViewModel.HasAnyItem); + + public bool ShowShelfPaneToggleButton => AppearanceSettingsService.ShowShelfPaneToggleButton && AppLifecycleHelper.AppEnvironment is AppEnvironment.Dev; + + private NavigationToolbar? AddressToolbar => (MainWindow.Instance.Content as Frame)?.FindDescendant(); + + public bool HasAdditionalAction => + InstanceViewModel.IsPageTypeRecycleBin || + Commands.RunWithPowershell.IsExecutable || + CanExtract || + Commands.DecompressArchive.IsExecutable || + Commands.DecompressArchiveHere.IsExecutable || + Commands.DecompressArchiveHereSmart.IsExecutable || + Commands.DecompressArchiveToChildFolder.IsExecutable || + Commands.EditInNotepad.IsExecutable || + Commands.RotateLeft.IsExecutable || + Commands.RotateRight.IsExecutable || + Commands.SetAsAppBackground.IsExecutable || + Commands.SetAsWallpaperBackground.IsExecutable || + Commands.SetAsLockscreenBackground.IsExecutable || + Commands.SetAsSlideshowBackground.IsExecutable || + Commands.InstallFont.IsExecutable || + Commands.InstallInfDriver.IsExecutable || + Commands.InstallCertificate.IsExecutable; + + public bool CanExtract => Commands.DecompressArchive.CanExecute(null) || Commands.DecompressArchiveHere.CanExecute(null) || Commands.DecompressArchiveHereSmart.CanExecute(null) || Commands.DecompressArchiveToChildFolder.CanExecute(null); + + public bool IsCardsLayout => _InstanceViewModel.FolderSettings.LayoutMode is FolderLayoutModes.CardsView; + public bool IsColumnLayout => _InstanceViewModel.FolderSettings.LayoutMode is FolderLayoutModes.ColumnView; + public bool IsGridLayout => _InstanceViewModel.FolderSettings.LayoutMode is FolderLayoutModes.GridView; + public bool IsDetailsLayout => _InstanceViewModel.FolderSettings.LayoutMode is FolderLayoutModes.DetailsView; + public bool IsListLayout => _InstanceViewModel.FolderSettings.LayoutMode is FolderLayoutModes.ListView; + + public bool IsLayoutSizeCompact => + (IsDetailsLayout && UserSettingsService.LayoutSettingsService.DetailsViewSize == DetailsViewSizeKind.Compact) || + (IsListLayout && UserSettingsService.LayoutSettingsService.ListViewSize == ListViewSizeKind.Compact) || + (IsColumnLayout && UserSettingsService.LayoutSettingsService.ColumnsViewSize == ColumnsViewSizeKind.Compact); + + public bool IsLayoutSizeSmall => + (IsDetailsLayout && UserSettingsService.LayoutSettingsService.DetailsViewSize == DetailsViewSizeKind.Small) || + (IsListLayout && UserSettingsService.LayoutSettingsService.ListViewSize == ListViewSizeKind.Small) || + (IsColumnLayout && UserSettingsService.LayoutSettingsService.ColumnsViewSize == ColumnsViewSizeKind.Small) || + (IsCardsLayout && UserSettingsService.LayoutSettingsService.CardsViewSize == CardsViewSizeKind.Small) || + (IsGridLayout && UserSettingsService.LayoutSettingsService.GridViewSize == GridViewSizeKind.Small); + + public bool IsLayoutSizeMedium => + (IsDetailsLayout && UserSettingsService.LayoutSettingsService.DetailsViewSize == DetailsViewSizeKind.Medium) || + (IsListLayout && UserSettingsService.LayoutSettingsService.ListViewSize == ListViewSizeKind.Medium) || + (IsColumnLayout && UserSettingsService.LayoutSettingsService.ColumnsViewSize == ColumnsViewSizeKind.Medium) || + (IsCardsLayout && UserSettingsService.LayoutSettingsService.CardsViewSize == CardsViewSizeKind.Medium) || + (IsGridLayout && UserSettingsService.LayoutSettingsService.GridViewSize == GridViewSizeKind.Medium); + + public bool IsLayoutSizeLarge => + (IsDetailsLayout && UserSettingsService.LayoutSettingsService.DetailsViewSize == DetailsViewSizeKind.Large) || + (IsListLayout && UserSettingsService.LayoutSettingsService.ListViewSize == ListViewSizeKind.Large) || + (IsColumnLayout && UserSettingsService.LayoutSettingsService.ColumnsViewSize == ColumnsViewSizeKind.Large) || + (IsCardsLayout && UserSettingsService.LayoutSettingsService.CardsViewSize == CardsViewSizeKind.Large) || + (IsGridLayout && UserSettingsService.LayoutSettingsService.GridViewSize == GridViewSizeKind.Large); + + public bool IsLayoutSizeExtraLarge => + (IsDetailsLayout && UserSettingsService.LayoutSettingsService.DetailsViewSize == DetailsViewSizeKind.ExtraLarge) || + (IsListLayout && UserSettingsService.LayoutSettingsService.ListViewSize == ListViewSizeKind.ExtraLarge) || + (IsColumnLayout && UserSettingsService.LayoutSettingsService.ColumnsViewSize == ColumnsViewSizeKind.ExtraLarge) || + (IsCardsLayout && UserSettingsService.LayoutSettingsService.CardsViewSize == CardsViewSizeKind.ExtraLarge) || + (IsGridLayout && UserSettingsService.LayoutSettingsService.GridViewSize == GridViewSizeKind.ExtraLarge); + + private bool _IsDynamicOverflowEnabled; + public bool IsDynamicOverflowEnabled { get => _IsDynamicOverflowEnabled; set => SetProperty(ref _IsDynamicOverflowEnabled, value); } + + private bool _IsUpdating; + public bool IsUpdating { get => _IsUpdating; set => SetProperty(ref _IsUpdating, value); } + + private bool _IsUpdateAvailable; + public bool IsUpdateAvailable { get => _IsUpdateAvailable; set => SetProperty(ref _IsUpdateAvailable, value); } + + private bool _CanCopyPathInPage; + public bool CanCopyPathInPage { get => _CanCopyPathInPage; set => SetProperty(ref _CanCopyPathInPage, value); } + + private bool _CanGoBack; + public bool CanGoBack { get => _CanGoBack; set => SetProperty(ref _CanGoBack, value); } + + private bool _CanGoForward; + public bool CanGoForward { get => _CanGoForward; set => SetProperty(ref _CanGoForward, value); } + + private bool _CanNavigateToParent; + public bool CanNavigateToParent { get => _CanNavigateToParent; set => SetProperty(ref _CanNavigateToParent, value); } + + private bool _PreviewPaneEnabled; + public bool PreviewPaneEnabled { get => _PreviewPaneEnabled; set => SetProperty(ref _PreviewPaneEnabled, value); } + + private bool _CanRefresh; + public bool CanRefresh { get => _CanRefresh; set => SetProperty(ref _CanRefresh, value); } + + private string _PathControlDisplayText; + [Obsolete("Superseded by Omnibar.")] + public string PathControlDisplayText { get => _PathControlDisplayText; set => SetProperty(ref _PathControlDisplayText, value); } + + private bool _HasItem = false; + public bool HasItem { get => _HasItem; set => SetProperty(ref _HasItem, value); } + + private Style _LayoutThemedIcon; + public Style LayoutThemedIcon { get => _LayoutThemedIcon; set => SetProperty(ref _LayoutThemedIcon, value); } + + // SetProperty doesn't seem to properly notify the binding in path bar + private string? _PathText; + public string? PathText + { + get => _PathText; + set + { + _PathText = value; + OnPropertyChanged(nameof(PathText)); + } + } + + // Workaround to ensure Omnibar is only loaded after the ViewModel is initialized + public bool LoadOmnibar => + true; + + private string? _OmnibarCommandPaletteModeText; + public string? OmnibarCommandPaletteModeText { get => _OmnibarCommandPaletteModeText; set => SetProperty(ref _OmnibarCommandPaletteModeText, value); } + + private string? _OmnibarSearchModeText; + public string? OmnibarSearchModeText { get => _OmnibarSearchModeText; set => SetProperty(ref _OmnibarSearchModeText, value); } + + private string _OmnibarCurrentSelectedModeName = OmnibarPathModeName; + public string OmnibarCurrentSelectedModeName { get => _OmnibarCurrentSelectedModeName; set => SetProperty(ref _OmnibarCurrentSelectedModeName, value); } + + private CurrentInstanceViewModel _InstanceViewModel; + public CurrentInstanceViewModel InstanceViewModel + { + get => _InstanceViewModel; + set + { + if (_InstanceViewModel?.FolderSettings is not null) + _InstanceViewModel.FolderSettings.PropertyChanged -= FolderSettings_PropertyChanged; + + if (SetProperty(ref _InstanceViewModel, value) && _InstanceViewModel?.FolderSettings is not null) + { + FolderSettings_PropertyChanged(this, new PropertyChangedEventArgs(nameof(LayoutPreferencesManager.LayoutMode))); + _InstanceViewModel.FolderSettings.PropertyChanged += FolderSettings_PropertyChanged; + } + } + } + + private List? _SelectedItems; + public List? SelectedItems + { + get => _SelectedItems; + set + { + if (SetProperty(ref _SelectedItems, value)) + { + OnPropertyChanged(nameof(CanExtract)); + OnPropertyChanged(nameof(HasAdditionalAction)); + + // Workaround to ensure the overflow button is only displayed when there are overflow items + IsDynamicOverflowEnabled = false; + IsDynamicOverflowEnabled = true; + } + } + } + + // Commands + + public IAsyncRelayCommand? OpenNewWindowCommand { get; set; } + public ICommand? CreateNewFileCommand { get; set; } + public ICommand? Share { get; set; } + public ICommand? UpdateCommand { get; set; } + + // Constructor + + public NavigationToolbarViewModel() + { + _dispatcherQueue = DispatcherQueue.GetForCurrentThread(); + _dragOverTimer = _dispatcherQueue.CreateTimer(); + + UserSettingsService.OnSettingChangedEvent += UserSettingsService_OnSettingChangedEvent; + UpdateService.PropertyChanged += UpdateService_OnPropertyChanged; + + Commands.DecompressArchive.PropertyChanged += (s, e) => + { + if (e.PropertyName is nameof(Commands.DecompressArchive.IsExecutable)) + OnPropertyChanged(nameof(CanExtract)); + }; + + Commands.DecompressArchiveHere.PropertyChanged += (s, e) => + { + if (e.PropertyName is nameof(Commands.DecompressArchiveHere.IsExecutable)) + OnPropertyChanged(nameof(CanExtract)); + }; + + Commands.DecompressArchiveHereSmart.PropertyChanged += (s, e) => + { + if (e.PropertyName is nameof(Commands.DecompressArchiveHereSmart.IsExecutable)) + OnPropertyChanged(nameof(CanExtract)); + }; + + Commands.DecompressArchiveHereSmart.PropertyChanged += (s, e) => + { + if (e.PropertyName is nameof(Commands.DecompressArchiveToChildFolder.IsExecutable)) + OnPropertyChanged(nameof(CanExtract)); + }; + + AppearanceSettingsService.PropertyChanged += (s, e) => + { + switch (e.PropertyName) + { + case nameof(AppearanceSettingsService.StatusCenterVisibility): + OnPropertyChanged(nameof(ShowStatusCenterButton)); + break; + case nameof(AppearanceSettingsService.ShowShelfPaneToggleButton): + OnPropertyChanged(nameof(ShowShelfPaneToggleButton)); + break; + } + }; + OngoingTasksViewModel.PropertyChanged += (s, e) => + { + switch (e.PropertyName) + { + case nameof(OngoingTasksViewModel.HasAnyItem): + OnPropertyChanged(nameof(ShowStatusCenterButton)); + break; + } + }; + } + + // Methods + + private void UpdateService_OnPropertyChanged(object? sender, PropertyChangedEventArgs e) + { + IsUpdateAvailable = UpdateService.IsUpdateAvailable; + IsUpdating = UpdateService.IsUpdating; + } + + private void UserSettingsService_OnSettingChangedEvent(object? sender, SettingChangedEventArgs e) + { + switch (e.SettingName) + { + // TODO: Move this to the widget page, it doesn't belong here. + case nameof(UserSettingsService.GeneralSettingsService.ShowQuickAccessWidget): + case nameof(UserSettingsService.GeneralSettingsService.ShowDrivesWidget): + case nameof(UserSettingsService.GeneralSettingsService.ShowNetworkLocationsWidget): + case nameof(UserSettingsService.GeneralSettingsService.ShowFileTagsWidget): + case nameof(UserSettingsService.GeneralSettingsService.ShowRecentFilesWidget): + RefreshWidgetsRequested?.Invoke(this, EventArgs.Empty); + OnPropertyChanged(e.SettingName); + break; + case nameof(UserSettingsService.LayoutSettingsService.DetailsViewSize): + case nameof(UserSettingsService.LayoutSettingsService.ListViewSize): + case nameof(UserSettingsService.LayoutSettingsService.ColumnsViewSize): + case nameof(UserSettingsService.LayoutSettingsService.CardsViewSize): + case nameof(UserSettingsService.LayoutSettingsService.GridViewSize): + OnPropertyChanged(nameof(IsLayoutSizeCompact)); + OnPropertyChanged(nameof(IsLayoutSizeSmall)); + OnPropertyChanged(nameof(IsLayoutSizeMedium)); + OnPropertyChanged(nameof(IsLayoutSizeLarge)); + OnPropertyChanged(nameof(IsLayoutSizeExtraLarge)); + break; + } + } + + [Obsolete("Superseded by Omnibar.")] + public void PathBoxItem_DragLeave(object sender, DragEventArgs e) + { + if (((FrameworkElement)sender).DataContext is not PathBoxItem pathBoxItem || + pathBoxItem.Path == "Home" || + pathBoxItem.Path == "ReleaseNotes" || + pathBoxItem.Path == "Settings") + { + return; + } + + // Reset dragged over pathbox item + if (pathBoxItem.Path == _dragOverPath) + _dragOverPath = null; + } + + [Obsolete("Superseded by Omnibar.")] + public async Task PathBoxItem_Drop(object sender, DragEventArgs e) + { + if (_lockFlag) + return; + + _lockFlag = true; + + // Reset dragged over pathbox item + _dragOverPath = null; + + if (((FrameworkElement)sender).DataContext is not PathBoxItem pathBoxItem || + pathBoxItem.Path == "Home" || + pathBoxItem.Path == "ReleaseNotes" || + pathBoxItem.Path == "Settings") + { + return; + } + + var deferral = e.GetDeferral(); + + var signal = new AsyncManualResetEvent(); + + PathBoxItemDropped?.Invoke(this, new PathBoxItemDroppedEventArgs() + { + AcceptedOperation = e.AcceptedOperation, + Package = e.DataView, + Path = pathBoxItem.Path, + SignalEvent = signal + }); + + await signal.WaitAsync(); + + deferral.Complete(); + await Task.Yield(); + + _lockFlag = false; + } + + [Obsolete("Superseded by Omnibar.")] + public async Task PathBoxItem_DragOver(object sender, DragEventArgs e) + { + if (IsSingleItemOverride || + ((FrameworkElement)sender).DataContext is not PathBoxItem pathBoxItem || + pathBoxItem.Path == "Home" || + pathBoxItem.Path == "ReleaseNotes" || + pathBoxItem.Path == "Settings") + { + return; + } + + if (_dragOverPath != pathBoxItem.Path) + { + _dragOverPath = pathBoxItem.Path; + _dragOverTimer.Stop(); + + if (_dragOverPath != (this as IAddressToolbarViewModel).PathComponents.LastOrDefault()?.Path) + { + _dragOverTimer.Debounce(() => + { + if (_dragOverPath is not null) + { + _dragOverTimer.Stop(); + ItemDraggedOverPathItem?.Invoke(this, new PathNavigationEventArgs() + { + ItemPath = _dragOverPath + }); + _dragOverPath = null; + } + }, + TimeSpan.FromMilliseconds(Constants.DragAndDrop.HoverToOpenTimespan), false); + } + } + + // In search page + if (!FilesystemHelpers.HasDraggedStorageItems(e.DataView) || string.IsNullOrEmpty(pathBoxItem.Path)) + { + e.AcceptedOperation = DataPackageOperation.None; + + return; + } + + e.Handled = true; + var deferral = e.GetDeferral(); + + var storageItems = await FilesystemHelpers.GetDraggedStorageItems(e.DataView); + + if (!storageItems.Any(storageItem => + !string.IsNullOrEmpty(storageItem?.Path) && + storageItem.Path.Replace(pathBoxItem.Path, string.Empty, StringComparison.Ordinal) + .Trim(Path.DirectorySeparatorChar) + .Contains(Path.DirectorySeparatorChar))) + { + e.AcceptedOperation = DataPackageOperation.None; + } + + // Copy be default when dragging from zip + else if (storageItems.Any(x => + x.Item is ZipStorageFile || + x.Item is ZipStorageFolder) || + ZipStorageFolder.IsZipPath(pathBoxItem.Path)) + { + e.DragUIOverride.Caption = string.Format(Strings.CopyToFolderCaptionText.GetLocalizedResource(), pathBoxItem.Title); + e.AcceptedOperation = DataPackageOperation.Copy; + } + else + { + e.DragUIOverride.IsCaptionVisible = true; + e.DragUIOverride.Caption = string.Format(Strings.MoveToFolderCaptionText.GetLocalizedResource(), pathBoxItem.Title); + // Some applications such as Edge can't raise the drop event by the Move flag (#14008), so we set the Copy flag as well. + e.AcceptedOperation = DataPackageOperation.Move | DataPackageOperation.Copy; + } + + deferral.Complete(); + } + + [Obsolete("Superseded by Omnibar.")] + public void CurrentPathSetTextBox_TextChanged(object sender, TextChangedEventArgs args) + { + if (sender is TextBox textBox) + PathBoxQuerySubmitted?.Invoke(this, new ToolbarQuerySubmittedEventArgs() { QueryText = textBox.Text }); + } + + public async Task HandleFolderNavigationAsync(string path, bool openNewTab = false) + { + openNewTab |= _pointerRoutedEventArgs is not null; + if (openNewTab) + { + await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync( + async () => + { + await NavigationHelpers.AddNewTabByPathAsync(typeof(ShellPanesPage), path, true); + }, + DispatcherQueuePriority.Low); + + _pointerRoutedEventArgs = null; + + return; + } + + ToolbarPathItemInvoked?.Invoke(this, new() { ItemPath = path }); + } + + public async Task HandleItemNavigationAsync(string path) + { + if (ContentPageContext.ShellPage is null) + return; + + var currentPath = PathComponents.LastOrDefault()?.Path; + var isFtp = FtpHelpers.IsFtpPath(path); + var normalizedInput = NormalizePathInput(path, isFtp); + if (currentPath is not null && currentPath.Equals(normalizedInput, StringComparison.OrdinalIgnoreCase) || + string.IsNullOrWhiteSpace(normalizedInput)) + return; + + if (normalizedInput.Equals(ContentPageContext.ShellPage.ShellViewModel.WorkingDirectory) && + ContentPageContext.ShellPage.CurrentPageType != typeof(HomePage) && + !ContentPageContext.ShellPage.ShellViewModel.IsSearchResults) + return; + + if (normalizedInput.Equals("Home", StringComparison.OrdinalIgnoreCase) || + normalizedInput.Equals(Strings.Home.GetLocalizedResource(), StringComparison.OrdinalIgnoreCase)) + { + SavePathToHistory("Home"); + ContentPageContext.ShellPage.NavigateHome(); + } + else if (normalizedInput.Equals("ReleaseNotes", StringComparison.OrdinalIgnoreCase) || + normalizedInput.Equals(Strings.ReleaseNotes.GetLocalizedResource(), StringComparison.OrdinalIgnoreCase)) + { + SavePathToHistory("ReleaseNotes"); + ContentPageContext.ShellPage.NavigateToReleaseNotes(); + } + else if (normalizedInput.Equals("Settings", StringComparison.OrdinalIgnoreCase) || + normalizedInput.Equals(Strings.Settings.GetLocalizedResource(), StringComparison.OrdinalIgnoreCase)) + { + //SavePathToHistory("Settings"); + //ContentPageContext.ShellPage.NavigateToSettings(); + } + else + { + normalizedInput = StorageFileExtensions.GetResolvedPath(normalizedInput, isFtp); + if (currentPath is not null && currentPath.Equals(normalizedInput, StringComparison.OrdinalIgnoreCase)) + return; + + var item = await FilesystemTasks.Wrap(() => DriveHelpers.GetRootFromPathAsync(normalizedInput)); + + var resFolder = await FilesystemTasks.Wrap(() => StorageFileExtensions.DangerousGetFolderWithPathFromPathAsync(normalizedInput, item)); + if (resFolder || FolderHelpers.CheckFolderAccessWithWin32(normalizedInput)) + { + var matchingDrive = drivesViewModel.Drives.Cast().FirstOrDefault(x => PathNormalization.NormalizePath(normalizedInput).StartsWith(PathNormalization.NormalizePath(x.Path), StringComparison.Ordinal)); + if (matchingDrive is not null && matchingDrive.Type == Data.Items.DriveType.CDRom && matchingDrive.MaxSpace == ByteSizeLib.ByteSize.FromBytes(0)) + { + bool ejectButton = await DialogDisplayHelper.ShowDialogAsync(Strings.InsertDiscDialog_Title.GetLocalizedResource(), string.Format(Strings.InsertDiscDialog_Text.GetLocalizedResource(), matchingDrive.Path), Strings.InsertDiscDialog_OpenDriveButton.GetLocalizedResource(), Strings.Close.GetLocalizedResource()); + if (ejectButton) + DriveHelpers.EjectDeviceAsync(matchingDrive.Path); + return; + } + + var pathToNavigate = resFolder.Result?.Path ?? normalizedInput; + SavePathToHistory(pathToNavigate); + ContentPageContext.ShellPage.NavigateToPath(pathToNavigate); + } + else if (isFtp) + { + SavePathToHistory(normalizedInput); + ContentPageContext.ShellPage.NavigateToPath(normalizedInput); + } + else // Not a folder or inaccessible + { + var resFile = await FilesystemTasks.Wrap(() => StorageFileExtensions.DangerousGetFileWithPathFromPathAsync(normalizedInput, item)); + if (resFile) + { + var pathToInvoke = resFile.Result.Path; + await Win32Helper.InvokeWin32ComponentAsync(pathToInvoke, ContentPageContext.ShellPage); + } + else // Not a file or not accessible + { + var workingDir = + string.IsNullOrEmpty(ContentPageContext.ShellPage.ShellViewModel.WorkingDirectory) || + ContentPageContext.ShellPage.CurrentPageType == typeof(HomePage) + ? Constants.UserEnvironmentPaths.HomePath + : ContentPageContext.ShellPage.ShellViewModel.WorkingDirectory; + + if (await LaunchApplicationFromPath(PathText, workingDir)) + return; + + try + { + if (!await Windows.System.Launcher.LaunchUriAsync(new Uri(PathText))) + await DialogDisplayHelper.ShowDialogAsync(Strings.InvalidItemDialogTitle.GetLocalizedResource(), + string.Format(Strings.InvalidItemDialogContent.GetLocalizedResource(), Environment.NewLine, resFolder.ErrorCode.ToString())); + } + catch (Exception ex) when (ex is UriFormatException || ex is ArgumentException) + { + await DialogDisplayHelper.ShowDialogAsync(Strings.InvalidItemDialogTitle.GetLocalizedResource(), + string.Format(Strings.InvalidItemDialogContent.GetLocalizedResource(), Environment.NewLine, resFolder.ErrorCode.ToString())); + } + } + } + } + + PathControlDisplayText = ContentPageContext.ShellPage.ShellViewModel.WorkingDirectory; + } + + public void SwitchToCommandPaletteMode() + { + OmnibarCurrentSelectedModeName = OmnibarPaletteModeName; + } + + public async Task SwitchToSearchMode() + { + // If the Omnibar is already focused such as when the user initiates a search via the Command Palette, + // add a short delay to allow the Command Palette to fully close before switching modes. + var omnibar = AddressToolbar?.FindDescendant("Omnibar") as Omnibar; + if (omnibar is not null && omnibar.IsFocused) + await Task.Delay(100); + + OmnibarCurrentSelectedModeName = OmnibarSearchModeName; + } + + public async Task SwitchToPathMode() + { + // If the Omnibar is already focused such as when the user initiates the Edit Path action via the + // Command Palette, add a short delay to allow the Command Palette to fully close before switching modes. + var omnibar = AddressToolbar?.FindDescendant("Omnibar") as Omnibar; + if (omnibar is not null && omnibar.IsFocused) + await Task.Delay(100); + + OmnibarCurrentSelectedModeName = OmnibarPathModeName; + omnibar?.Focus(FocusState.Programmatic); + omnibar.IsFocused = true; + } + + public void UpdateAdditionalActions() + { + OnPropertyChanged(nameof(HasAdditionalAction)); + } + + public async Task SetPathBoxDropDownFlyoutAsync(MenuFlyout flyout, PathBoxItem pathItem) + { + var nextPathItemTitle = PathComponents[PathComponents.IndexOf(pathItem) + 1].Title; + var childFolders = GetSubfolders(pathItem.Path); + + // Fall back to StorageFolder API for non-filesystem paths (e.g. FTP) + if (childFolders is null) + { + StorageFolderWithPath folder = await ContentPageContext.ShellPage.ShellViewModel.GetFolderWithPathFromPathAsync(pathItem.Path); + if (folder is not null) + { + var result = (await FilesystemTasks.Wrap(() => folder.GetFoldersWithPathAsync(string.Empty))).Result; + childFolders = result?.Select(f => (f.Item.Name, f.Path, false)).ToList(); + } + } + + flyout.Items?.Clear(); + + if (childFolders is null || childFolders.Count == 0) + { + var flyoutItem = new MenuFlyoutItem + { + Icon = new FontIcon { Glyph = "\uE7BA" }, + Text = Strings.SubDirectoryAccessDenied.GetLocalizedResource(), + //Foreground = (SolidColorBrush)Application.Current.Resources["SystemControlErrorTextForegroundBrush"], + }; + + flyout.Items?.Add(flyoutItem); + + return; + } + + var boldFontWeight = new FontWeight { Weight = 800 }; + var normalFontWeight = new FontWeight { Weight = 400 }; + + var workingPath = + PathComponents[PathComponents.Count - 1].Path?.TrimEnd(Path.DirectorySeparatorChar); + + foreach (var (name, path, isHidden) in childFolders) + { + var flyoutItem = new MenuFlyoutItem + { + Icon = new FontIcon { Glyph = "\uE8B7" }, // Use font icon as placeholder + Text = name, + Opacity = isHidden ? Constants.UI.DimItemOpacity : 1.0, + }; + + if (workingPath != path) + { + flyoutItem.Click += (sender, args) => + { + // Navigate to the directory + ContentPageContext.ShellPage.NavigateToPath(path); + }; + } + + flyout.Items?.Add(flyoutItem); + + // Start loading the thumbnail in the background + _ = LoadFlyoutItemIconAsync(flyoutItem, path); + } + } + + /// + /// Enumerates subfolders using Win32 API, including hidden folders based on user settings. + /// Returns null if the path cannot be enumerated with Win32. + /// + private List<(string Name, string Path, bool IsHidden)>? GetSubfolders(string parentPath) + { + IntPtr hFile = Win32PInvoke.FindFirstFileExFromApp( + $"{parentPath}{Path.DirectorySeparatorChar}*.*", + Win32PInvoke.FINDEX_INFO_LEVELS.FindExInfoBasic, + out Win32PInvoke.WIN32_FIND_DATA findData, + Win32PInvoke.FINDEX_SEARCH_OPS.FindExSearchNameMatch, + IntPtr.Zero, + Win32PInvoke.FIND_FIRST_EX_LARGE_FETCH); + + if (hFile.ToInt64() == -1) + return null; + + var showHidden = UserSettingsService.FoldersSettingsService.ShowHiddenItems; + var showSystem = UserSettingsService.FoldersSettingsService.ShowProtectedSystemFiles; + var showDot = UserSettingsService.FoldersSettingsService.ShowDotFiles; + var folders = new List<(string Name, string Path, bool IsHidden)>(); + + do + { + if (findData.cFileName is "." or "..") + continue; + + if (((FileAttributes)findData.dwFileAttributes & FileAttributes.Directory) == 0) + continue; + + bool isHidden = ((FileAttributes)findData.dwFileAttributes & FileAttributes.Hidden) != 0; + bool isSystem = ((FileAttributes)findData.dwFileAttributes & FileAttributes.System) != 0; + + if (isHidden && (!showHidden || (isSystem && !showSystem))) + continue; + + if (findData.cFileName.StartsWith('.') && !showDot) + continue; + + folders.Add((findData.cFileName, Path.Combine(parentPath, findData.cFileName), isHidden)); + } + while (Win32PInvoke.FindNextFile(hFile, out findData)); + + Win32PInvoke.FindClose(hFile); + folders.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase)); + + return folders; + } + + private async Task LoadFlyoutItemIconAsync(MenuFlyoutItem flyoutItem, string path) + { + var imageSource = await NavigationHelpers.GetIconForPathAsync(path); + + if (imageSource is not null) + flyoutItem.Icon = new ImageIcon { Source = imageSource }; + } + + private static string NormalizePathInput(string currentInput, bool isFtp) + { + if (currentInput.Contains('/') && !isFtp) + currentInput = currentInput.Replace('/', '\\'); + + currentInput = currentInput.Replace("\\\\", "\\", StringComparison.Ordinal); + + if (currentInput.StartsWith('\\') && !currentInput.StartsWith("\\\\", StringComparison.Ordinal)) + currentInput = currentInput.Insert(0, "\\"); + + return currentInput; + } + + [Obsolete("Superseded by Omnibar.")] + public async Task CheckPathInputAsync(string currentInput, string currentSelectedPath, IShellPage shellPage) + { + if (currentInput.StartsWith('>')) + { + var code = currentInput.Substring(1).Trim(); + var command = Commands[code]; + + if (command == Commands.None) + await DialogDisplayHelper.ShowDialogAsync(Strings.InvalidCommand.GetLocalizedResource(), + string.Format(Strings.InvalidCommandContent.GetLocalizedResource(), code)); + else if (!command.IsExecutable) + await DialogDisplayHelper.ShowDialogAsync(Strings.CommandNotExecutable.GetLocalizedResource(), + string.Format(Strings.CommandNotExecutableContent.GetLocalizedResource(), command.Code)); + else + await command.ExecuteAsync(); + + return; + } + + var isFtp = FtpHelpers.IsFtpPath(currentInput); + + var normalizedInput = NormalizePathInput(currentInput, isFtp); + + if (currentSelectedPath == normalizedInput || string.IsNullOrWhiteSpace(normalizedInput)) + return; + + if (normalizedInput != shellPage.ShellViewModel.WorkingDirectory || shellPage.CurrentPageType == typeof(HomePage)) + { + if (normalizedInput.Equals("Home", StringComparison.OrdinalIgnoreCase) || normalizedInput.Equals(Strings.Home.GetLocalizedResource(), StringComparison.OrdinalIgnoreCase)) + { + SavePathToHistory("Home"); + shellPage.NavigateHome(); + } + else if (normalizedInput.Equals("ReleaseNotes", StringComparison.OrdinalIgnoreCase) || normalizedInput.Equals(Strings.ReleaseNotes.GetLocalizedResource(), StringComparison.OrdinalIgnoreCase)) + { + SavePathToHistory("ReleaseNotes"); + shellPage.NavigateToReleaseNotes(); + } + // TODO add settings page + //else if (normalizedInput.Equals("Settings", StringComparison.OrdinalIgnoreCase) || normalizedInput.Equals(Strings.Settings.GetLocalizedResource(), StringComparison.OrdinalIgnoreCase)) + //{ + // SavePathToHistory("Settings"); + // shellPage.NavigateToReleaseNotes(); + //} + else + { + normalizedInput = StorageFileExtensions.GetResolvedPath(normalizedInput, isFtp); + if (currentSelectedPath == normalizedInput) + return; + + var item = await FilesystemTasks.Wrap(() => DriveHelpers.GetRootFromPathAsync(normalizedInput)); + + var resFolder = await FilesystemTasks.Wrap(() => StorageFileExtensions.DangerousGetFolderWithPathFromPathAsync(normalizedInput, item)); + if (resFolder || FolderHelpers.CheckFolderAccessWithWin32(normalizedInput)) + { + var matchingDrive = drivesViewModel.Drives.Cast().FirstOrDefault(x => PathNormalization.NormalizePath(normalizedInput).StartsWith(PathNormalization.NormalizePath(x.Path), StringComparison.Ordinal)); + if (matchingDrive is not null && matchingDrive.Type == Data.Items.DriveType.CDRom && matchingDrive.MaxSpace == ByteSizeLib.ByteSize.FromBytes(0)) + { + bool ejectButton = await DialogDisplayHelper.ShowDialogAsync(Strings.InsertDiscDialog_Title.GetLocalizedResource(), string.Format(Strings.InsertDiscDialog_Text.GetLocalizedResource(), matchingDrive.Path), Strings.InsertDiscDialog_OpenDriveButton.GetLocalizedResource(), Strings.Close.GetLocalizedResource()); + if (ejectButton) + DriveHelpers.EjectDeviceAsync(matchingDrive.Path); + return; + } + var pathToNavigate = resFolder.Result?.Path ?? normalizedInput; + SavePathToHistory(pathToNavigate); + shellPage.NavigateToPath(pathToNavigate); + } + else if (isFtp) + { + SavePathToHistory(normalizedInput); + shellPage.NavigateToPath(normalizedInput); + } + else // Not a folder or inaccessible + { + var resFile = await FilesystemTasks.Wrap(() => StorageFileExtensions.DangerousGetFileWithPathFromPathAsync(normalizedInput, item)); + if (resFile) + { + var pathToInvoke = resFile.Result.Path; + await Win32Helper.InvokeWin32ComponentAsync(pathToInvoke, shellPage); + } + else // Not a file or not accessible + { + var workingDir = + string.IsNullOrEmpty(shellPage.ShellViewModel.WorkingDirectory) || + shellPage.CurrentPageType == typeof(HomePage) ? + Constants.UserEnvironmentPaths.HomePath : + shellPage.ShellViewModel.WorkingDirectory; + + if (await LaunchApplicationFromPath(currentInput, workingDir)) + return; + + try + { + if (!await Windows.System.Launcher.LaunchUriAsync(new Uri(currentInput))) + await DialogDisplayHelper.ShowDialogAsync(Strings.InvalidItemDialogTitle.GetLocalizedResource(), + string.Format(Strings.InvalidItemDialogContent.GetLocalizedResource(), Environment.NewLine, resFolder.ErrorCode.ToString())); + } + catch (Exception ex) when (ex is UriFormatException || ex is ArgumentException) + { + await DialogDisplayHelper.ShowDialogAsync(Strings.InvalidItemDialogTitle.GetLocalizedResource(), + string.Format(Strings.InvalidItemDialogContent.GetLocalizedResource(), Environment.NewLine, resFolder.ErrorCode.ToString())); + } + } + } + } + + PathControlDisplayText = shellPage.ShellViewModel.WorkingDirectory; + } + } + + private void SavePathToHistory(string path) + { + var pathHistoryList = UserSettingsService.GeneralSettingsService.PathHistoryList?.ToList() ?? []; + pathHistoryList.Remove(path); + pathHistoryList.Insert(0, path); + + if (pathHistoryList.Count > MaxSuggestionsCount) + UserSettingsService.GeneralSettingsService.PathHistoryList = pathHistoryList.RemoveFrom(MaxSuggestionsCount + 1); + else + UserSettingsService.GeneralSettingsService.PathHistoryList = pathHistoryList; + } + + public void SaveSearchQueryToList(string searchQuery) + { + var previousSearchQueriesList = UserSettingsService.GeneralSettingsService.PreviousSearchQueriesList?.ToList() ?? []; + previousSearchQueriesList.Remove(searchQuery); + previousSearchQueriesList.Insert(0, searchQuery); + + if (previousSearchQueriesList.Count > MaxSuggestionsCount) + UserSettingsService.GeneralSettingsService.PreviousSearchQueriesList = previousSearchQueriesList.RemoveFrom(MaxSuggestionsCount + 1); + else + UserSettingsService.GeneralSettingsService.PreviousSearchQueriesList = previousSearchQueriesList; + } + + private static async Task LaunchApplicationFromPath(string currentInput, string workingDir) + { + var args = CommandLineParser.SplitArguments(currentInput); + return await LaunchHelper.LaunchAppAsync( + args.FirstOrDefault("").Trim('"'), string.Join(' ', args.Skip(1)), workingDir + ); + } + + public async Task PopulateOmnibarSuggestionsForPathMode() + { + var result = await SafetyExtensions.IgnoreExceptions((Func>)(async () => + { + List? newSuggestions = []; + var pathText = this.PathText; + + // If the current input is special, populate navigation history instead. + if (string.IsNullOrWhiteSpace((string)pathText) || + pathText is "Home" or "ReleaseNotes" or "Settings") + { + // Load previously entered path + if (UserSettingsService.GeneralSettingsService.PathHistoryList is { } pathHistoryList) + { + newSuggestions.AddRange(pathHistoryList.Select(x => new OmnibarPathModeSuggestionModel(x, x))); + } + } + else + { + var isFtp = FtpHelpers.IsFtpPath((string)pathText); + pathText = NormalizePathInput((string)pathText, isFtp); + var expandedPath = StorageFileExtensions.GetResolvedPath((string)pathText, isFtp); + var folderPath = PathNormalization.GetParentDir(expandedPath) ?? expandedPath; + StorageFolderWithPath folder = await ContentPageContext.ShellPage.ShellViewModel.GetFolderWithPathFromPathAsync(folderPath); + if (folder is null) + return false; + + var currPath = await folder.GetFoldersWithPathAsync(Path.GetFileName(expandedPath), MaxSuggestionsCount); + if (currPath.Count >= MaxSuggestionsCount) + { + newSuggestions.AddRange(currPath.Select(x => new OmnibarPathModeSuggestionModel(x.Path, x.Item.DisplayName))); + } + else if (currPath.Any()) + { + var subPath = await currPath.First().GetFoldersWithPathAsync((uint)(MaxSuggestionsCount - currPath.Count)); + newSuggestions.AddRange(currPath.Select(x => new OmnibarPathModeSuggestionModel(x.Path, x.Item.DisplayName))); + newSuggestions.AddRange(subPath.Select(x => new OmnibarPathModeSuggestionModel(x.Path, PathNormalization.Combine(currPath.First().Item.DisplayName, x.Item.DisplayName)))); + } + } + + // If there are no suggestions, show "No suggestions" + if (newSuggestions.Count is 0) + return false; + + // Check whether at least one item is in common between the old and the new suggestions + // since the suggestions popup becoming empty causes flickering + if (!PathModeSuggestionItems.IntersectBy(newSuggestions, x => x.DisplayName).Any()) + { + // No items in common, update the list in-place + for (int index = 0; index < newSuggestions.Count; index++) + { + if (index < PathModeSuggestionItems.Count) + { + PathModeSuggestionItems[index] = newSuggestions[index]; + } + else + { + PathModeSuggestionItems.Add(newSuggestions[index]); + } + } + + while (PathModeSuggestionItems.Count > newSuggestions.Count) + PathModeSuggestionItems.RemoveAt(PathModeSuggestionItems.Count - 1); + } + else + { + // At least an element in common, show animation + foreach (var s in PathModeSuggestionItems.ExceptBy(newSuggestions, x => x.DisplayName).ToList()) + PathModeSuggestionItems.Remove(s); + + for (int index = 0; index < newSuggestions.Count; index++) + { + if (PathModeSuggestionItems.Count > index && PathModeSuggestionItems[index].DisplayName == newSuggestions[index].DisplayName) + { + PathModeSuggestionItems[index] = newSuggestions[index]; + } + else + PathModeSuggestionItems.Insert(index, newSuggestions[index]); + } + } + + return true; + })); + + if (!result) + { + AddNoResultsItem(); + } + + void AddNoResultsItem() + { + PathModeSuggestionItems.Clear(); + + // Use null-safe access to avoid NullReferenceException during app lifecycle transitions + var workingDirectory = string.IsNullOrEmpty(ContentPageContext.ShellPage?.ShellViewModel?.WorkingDirectory) + ? Constants.UserEnvironmentPaths.HomePath + : ContentPageContext.ShellPage.ShellViewModel.WorkingDirectory; + + PathModeSuggestionItems.Add(new( + workingDirectory, + Strings.NavigationToolbarVisiblePathNoResults.GetLocalizedResource())); + } + } + + public async Task PopulateOmnibarSuggestionsForCommandPaletteMode() + { + var (suggestionsToProcess, commandsToProcess) = await Task.Run(() => + { + var suggestions = new List(); + + if (ContentPageContext.SelectedItems.Count == 1 && ContentPageContext.SelectedItem is not null && !ContentPageContext.SelectedItem.IsFolder) + { + try + { + var selectedItemPath = ContentPageContext.SelectedItem.ItemPath; + var fileActionEntity = ActionManager.Instance.EntityFactory.CreateFileEntity(selectedItemPath); + var actions = ActionManager.Instance.ActionRuntime.ActionCatalog.GetActionsForInputs(new[] { fileActionEntity }); + + foreach (var action in actions.Where(a => a.Definition.Description.Contains(OmnibarCommandPaletteModeText, StringComparison.OrdinalIgnoreCase))) + { + var newItem = new NavigationBarSuggestionItem + { + PrimaryDisplay = action.Definition.Description, + SearchText = OmnibarCommandPaletteModeText, + ActionInstance = action + }; + + if (Uri.TryCreate(action.Definition.IconFullPath, UriKind.RelativeOrAbsolute, out Uri? validUri)) + { + try + { + newItem.ActionIconSource = new BitmapImage(validUri); + } + catch (Exception) + { + } + } + + suggestions.Add(newItem); + } + } + catch (Exception ex) + { + App.Logger.LogWarning(ex, ex.Message); + } + } + + var commandsData = Commands + .Where(command => command.IsAccessibleGlobally + && (command.Description.Contains(OmnibarCommandPaletteModeText, StringComparison.OrdinalIgnoreCase) + || command.Code.ToString().Contains(OmnibarCommandPaletteModeText, StringComparison.OrdinalIgnoreCase))) + .Where(command => command.Description != Commands.OpenCommandPalette.Description.ToString()) + .ToList(); + + return (suggestions, commandsData); + }); + + var newSuggestions = new List(suggestionsToProcess); + int processedCount = 0; + + foreach (var command in commandsToProcess) + { + if (!command.IsExecutable) + { + processedCount++; + // To allow UI updates + if (processedCount % 3 == 0) + await Task.Yield(); + continue; + } + + var newItem = new NavigationBarSuggestionItem + { + ThemedIconStyle = command.Glyph.ToThemedIconStyle(), + Glyph = command.Glyph.BaseGlyph, + Text = command.Description, + PrimaryDisplay = command.Description, + HotKeys = command.HotKeys, + SearchText = OmnibarCommandPaletteModeText, + }; + + newSuggestions.Add(newItem); + processedCount++; + + // To allow UI updates + if (processedCount % 3 == 0) + await Task.Yield(); + } + + UpdateCommandPaletteSuggestions(newSuggestions); + } + + private void UpdateCommandPaletteSuggestions(List newSuggestions) + { + if (newSuggestions.Count == 0) + { + newSuggestions.Add(new NavigationBarSuggestionItem() + { + PrimaryDisplay = string.Format(Strings.NoCommandsFound.GetLocalizedResource(), OmnibarCommandPaletteModeText), + SearchText = OmnibarCommandPaletteModeText, + }); + } + + if (!OmnibarCommandPaletteModeSuggestionItems.IntersectBy(newSuggestions, x => x.PrimaryDisplay).Any()) + { + for (int index = 0; index < newSuggestions.Count; index++) + { + if (index < OmnibarCommandPaletteModeSuggestionItems.Count) + OmnibarCommandPaletteModeSuggestionItems[index] = newSuggestions[index]; + else + OmnibarCommandPaletteModeSuggestionItems.Add(newSuggestions[index]); + } + + while (OmnibarCommandPaletteModeSuggestionItems.Count > newSuggestions.Count) + OmnibarCommandPaletteModeSuggestionItems.RemoveAt(OmnibarCommandPaletteModeSuggestionItems.Count - 1); + } + else + { + foreach (var s in OmnibarCommandPaletteModeSuggestionItems.ExceptBy(newSuggestions, x => x.PrimaryDisplay).ToList()) + OmnibarCommandPaletteModeSuggestionItems.Remove(s); + + for (int index = 0; index < newSuggestions.Count; index++) + { + if (OmnibarCommandPaletteModeSuggestionItems.Count > index + && OmnibarCommandPaletteModeSuggestionItems[index].PrimaryDisplay == newSuggestions[index].PrimaryDisplay) + { + OmnibarCommandPaletteModeSuggestionItems[index] = newSuggestions[index]; + } + else + { + OmnibarCommandPaletteModeSuggestionItems.Insert(index, newSuggestions[index]); + } + } + } + } + + public async Task PopulateOmnibarSuggestionsForSearchMode() + { + if (ContentPageContext.ShellPage is null) + return; + + List newSuggestions = []; + + if (string.IsNullOrWhiteSpace(OmnibarSearchModeText)) + { + var previousSearchQueries = UserSettingsService.GeneralSettingsService.PreviousSearchQueriesList; + if (previousSearchQueries is not null) + newSuggestions.AddRange(previousSearchQueries.Select(query => new SuggestionModel(query, true))); + } + else + { + var search = new FolderSearch + { + Query = OmnibarSearchModeText, + Folder = ContentPageContext.ShellPage.ShellViewModel.WorkingDirectory, + MaxItemCount = 10, + }; + + var results = await search.SearchAsync(); + newSuggestions.AddRange(results.Select(result => new SuggestionModel(result))); + } + + // Remove outdated suggestions + var toRemove = OmnibarSearchModeSuggestionItems + .Where(existing => !newSuggestions.Any(newItem => newItem.ItemPath == existing.ItemPath)) + .ToList(); + + foreach (var item in toRemove) + OmnibarSearchModeSuggestionItems.Remove(item); + + // Add new suggestions + var toAdd = newSuggestions + .Where(newItem => !OmnibarSearchModeSuggestionItems.Any(existing => existing.Name == newItem.Name)); + + foreach (var item in toAdd) + OmnibarSearchModeSuggestionItems.Add(item); + } + + private void FolderSettings_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + switch (e.PropertyName) + { + case nameof(LayoutPreferencesManager.LayoutMode): + LayoutThemedIcon = _InstanceViewModel.FolderSettings.LayoutMode switch + { + FolderLayoutModes.ListView => Commands.LayoutList.ThemedIconStyle!, + FolderLayoutModes.CardsView => Commands.LayoutCards.ThemedIconStyle!, + FolderLayoutModes.ColumnView => Commands.LayoutColumns.ThemedIconStyle!, + FolderLayoutModes.GridView => Commands.LayoutGrid.ThemedIconStyle!, + _ => Commands.LayoutDetails.ThemedIconStyle! + }; + OnPropertyChanged(nameof(IsCardsLayout)); + OnPropertyChanged(nameof(IsListLayout)); + OnPropertyChanged(nameof(IsColumnLayout)); + OnPropertyChanged(nameof(IsGridLayout)); + OnPropertyChanged(nameof(IsDetailsLayout)); + OnPropertyChanged(nameof(IsLayoutSizeCompact)); + OnPropertyChanged(nameof(IsLayoutSizeSmall)); + OnPropertyChanged(nameof(IsLayoutSizeMedium)); + OnPropertyChanged(nameof(IsLayoutSizeLarge)); + OnPropertyChanged(nameof(IsLayoutSizeExtraLarge)); + break; + } + } + + // Disposer + + public void Dispose() + { + UserSettingsService.OnSettingChangedEvent -= UserSettingsService_OnSettingChangedEvent; + } + } +} diff --git a/src/Files.App/ViewModels/UserControls/Previews/ArchivePreviewViewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/ArchivePreviewViewModel.cs index 2855d345b3c3..bd4268930d40 100644 --- a/src/Files.App/ViewModels/UserControls/Previews/ArchivePreviewViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Previews/ArchivePreviewViewModel.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.ViewModels.Properties; using SevenZip; @@ -7,7 +7,7 @@ namespace Files.App.ViewModels.Previews { - public sealed class ArchivePreviewViewModel : BasePreviewModel + public sealed partial class ArchivePreviewViewModel : BasePreviewModel { public ArchivePreviewViewModel(ListedItem item) : base(item) @@ -51,7 +51,7 @@ public override async Task> LoadPreviewAndDetailsAsync() folderCount = (int)zipFile.FilesCount - fileCount; - string propertyItemCount = "DetailsArchiveItems".GetLocalizedFormatResource(zipFile.FilesCount, fileCount, folderCount); + string propertyItemCount = Strings.DetailsArchiveItems.GetLocalizedFormatResource(zipFile.FilesCount, fileCount, folderCount); details.Add(GetFileProperty("PropertyItemCount", propertyItemCount)); details.Add(GetFileProperty("PropertyUncompressedSize", totalSize.ToLongSizeString())); diff --git a/src/Files.App/ViewModels/UserControls/Previews/BasePreviewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/BasePreviewModel.cs index fd4f5009d672..63839ade64d8 100644 --- a/src/Files.App/ViewModels/UserControls/Previews/BasePreviewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Previews/BasePreviewModel.cs @@ -1,14 +1,13 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.ViewModels.Properties; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Media.Imaging; -using Windows.Storage.FileProperties; namespace Files.App.ViewModels.Previews { - public abstract class BasePreviewModel : ObservableObject + public abstract partial class BasePreviewModel : ObservableObject { private readonly IUserSettingsService userSettingsService = Ioc.Default.GetRequiredService(); @@ -89,7 +88,7 @@ public async virtual Task> LoadPreviewAndDetailsAsync() Constants.ShellIconSizes.Jumbo, false, IconOptions.None); - + if (result is not null) await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(async () => FileImage = await result.ToBitmapAsync()); else @@ -129,7 +128,7 @@ private async Task> GetSystemFilePropertiesAsync() return list.Where(i => i.ValueText is not null).ToList(); } - private sealed class DetailsOnlyPreviewModel : BasePreviewModel + private sealed partial class DetailsOnlyPreviewModel : BasePreviewModel { public DetailsOnlyPreviewModel(ListedItem item) : base(item) { } diff --git a/src/Files.App/ViewModels/UserControls/Previews/BasicPreviewViewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/BasicPreviewViewModel.cs index 43586005f1d3..b7dcfb12f854 100644 --- a/src/Files.App/ViewModels/UserControls/Previews/BasicPreviewViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Previews/BasicPreviewViewModel.cs @@ -1,9 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.ViewModels.Previews { - public sealed class BasicPreviewViewModel : BasePreviewModel + public sealed partial class BasicPreviewViewModel : BasePreviewModel { public BasicPreviewViewModel(ListedItem item) : base(item) { } } diff --git a/src/Files.App/ViewModels/UserControls/Previews/CodePreviewViewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/CodePreviewViewModel.cs index f0fe0cde10b6..4ad8d1f71f26 100644 --- a/src/Files.App/ViewModels/UserControls/Previews/CodePreviewViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Previews/CodePreviewViewModel.cs @@ -1,13 +1,13 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using System.Collections.Frozen; using ColorCode; using Files.App.ViewModels.Properties; +using System.Collections.Frozen; namespace Files.App.ViewModels.Previews { - public sealed class CodePreviewViewModel : BasePreviewModel + public sealed partial class CodePreviewViewModel : BasePreviewModel { private static readonly FrozenDictionary extensions = GetDictionary(); diff --git a/src/Files.App/ViewModels/UserControls/Previews/FolderPreviewViewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/FolderPreviewViewModel.cs index f826df056e35..fd2f759d306a 100644 --- a/src/Files.App/ViewModels/UserControls/Previews/FolderPreviewViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Previews/FolderPreviewViewModel.cs @@ -1,17 +1,15 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.ViewModels.Properties; using Microsoft.UI.Xaml.Media.Imaging; using System.IO; -using Windows.Storage.FileProperties; namespace Files.App.ViewModels.Previews { public sealed class FolderPreviewViewModel { - private static readonly IDateTimeFormatter dateTimeFormatter = Ioc.Default.GetRequiredService(); - + private readonly InfoPaneViewModel infoPaneViewModel = Ioc.Default.GetRequiredService(); public ListedItem Item { get; } public BitmapImage Thumbnail { get; set; } = new(); @@ -35,15 +33,21 @@ private async Task LoadPreviewAndDetailsAsync() Constants.ShellIconSizes.Jumbo, true, IconOptions.None); - + if (result is not null) Thumbnail = await result.ToBitmapAsync(); + // If the selected item is the root of a drive (e.g. "C:\") or a cloud drive, + // we do not need to load the properties below, since they will not be shown. + // Drive properties will be obtained through the DrivesViewModel service. + if (Item.IsDriveRoot || infoPaneViewModel?.SelectedDriveItem is not null) + return; + var info = await Folder.GetBasicPropertiesAsync(); Item.FileDetails = [ - GetFileProperty("PropertyItemCount", items.Count), + GetFileProperty("PropertyItemCount", infoPaneViewModel?.DirectoryItemCount), GetFileProperty("PropertyDateModified", info.DateModified), GetFileProperty("PropertyDateCreated", info.DateCreated), GetFileProperty("PropertyParsingPath", Folder.Path), @@ -56,7 +60,7 @@ private async Task LoadPreviewAndDetailsAsync() var headName = (await GitHelpers.GetRepositoryHead(gitDirectory))?.Name ?? string.Empty; var repositoryName = GitHelpers.GetOriginRepositoryName(gitDirectory); - if(!string.IsNullOrEmpty(gitDirectory)) + if (!string.IsNullOrEmpty(gitDirectory)) Item.FileDetails.Add(GetFileProperty("GitOriginRepositoryName", repositoryName)); if (!string.IsNullOrWhiteSpace(headName)) diff --git a/src/Files.App/ViewModels/UserControls/Previews/HtmlPreviewViewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/HtmlPreviewViewModel.cs index 4edbb7d8343e..934e4789785b 100644 --- a/src/Files.App/ViewModels/UserControls/Previews/HtmlPreviewViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Previews/HtmlPreviewViewModel.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.ViewModels.Properties; namespace Files.App.ViewModels.Previews { - public sealed class HtmlPreviewViewModel : BasePreviewModel + public sealed partial class HtmlPreviewViewModel : BasePreviewModel { public HtmlPreviewViewModel(ListedItem item) : base(item) diff --git a/src/Files.App/ViewModels/UserControls/Previews/ImagePreviewViewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/ImagePreviewViewModel.cs index a7a1bb8f6eae..6a323b90e1d6 100644 --- a/src/Files.App/ViewModels/UserControls/Previews/ImagePreviewViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Previews/ImagePreviewViewModel.cs @@ -1,7 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using CommunityToolkit.WinUI; using Files.App.ViewModels.Properties; using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Media.Imaging; @@ -10,7 +9,7 @@ namespace Files.App.ViewModels.Previews { - public sealed class ImagePreviewViewModel : BasePreviewModel + public sealed partial class ImagePreviewViewModel : BasePreviewModel { private ImageSource imageSource; public ImageSource ImageSource @@ -26,7 +25,7 @@ public ImagePreviewViewModel(ListedItem item) // TODO: Use existing helper mothods public static bool ContainsExtension(string extension) - => extension is ".png" or ".jpg" or ".jpeg" or ".bmp" or ".gif" or ".tiff" or ".ico" or ".webp"; + => extension is ".png" or ".jpg" or ".jpeg" or ".bmp" or ".gif" or ".tiff" or ".ico" or ".webp" or ".jxr"; public override async Task> LoadPreviewAndDetailsAsync() { diff --git a/src/Files.App/ViewModels/UserControls/Previews/MarkdownPreviewViewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/MarkdownPreviewViewModel.cs index 75f70c4a25d4..699d06a211e5 100644 --- a/src/Files.App/ViewModels/UserControls/Previews/MarkdownPreviewViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Previews/MarkdownPreviewViewModel.cs @@ -1,11 +1,11 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.ViewModels.Properties; namespace Files.App.ViewModels.Previews { - public sealed class MarkdownPreviewViewModel : BasePreviewModel + public sealed partial class MarkdownPreviewViewModel : BasePreviewModel { private string textValue; public string TextValue @@ -19,9 +19,6 @@ public MarkdownPreviewViewModel(ListedItem item) { } - public static bool ContainsExtension(string extension) - => extension is ".md" or ".markdown"; - public override async Task> LoadPreviewAndDetailsAsync() { var text = await ReadFileAsTextAsync(Item.ItemFile); diff --git a/src/Files.App/ViewModels/UserControls/Previews/MediaPreviewViewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/MediaPreviewViewModel.cs index 970ce5a1ff69..7221dbc7d5a6 100644 --- a/src/Files.App/ViewModels/UserControls/Previews/MediaPreviewViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Previews/MediaPreviewViewModel.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.ViewModels.Properties; using Microsoft.UI.Xaml; @@ -7,7 +7,7 @@ namespace Files.App.ViewModels.Previews { - public sealed class MediaPreviewViewModel : BasePreviewModel + public sealed partial class MediaPreviewViewModel : BasePreviewModel { public event EventHandler TogglePlaybackRequested; diff --git a/src/Files.App/ViewModels/UserControls/Previews/PDFPreviewViewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/PDFPreviewViewModel.cs index c2b0f8c86c48..fa1d08dc7698 100644 --- a/src/Files.App/ViewModels/UserControls/Previews/PDFPreviewViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Previews/PDFPreviewViewModel.cs @@ -1,7 +1,6 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using CommunityToolkit.WinUI; using Files.App.ViewModels.Properties; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Media.Imaging; @@ -11,7 +10,7 @@ namespace Files.App.ViewModels.Previews { - public sealed class PDFPreviewViewModel : BasePreviewModel + public sealed partial class PDFPreviewViewModel : BasePreviewModel { private Visibility loadingBarVisibility; public Visibility LoadingBarVisibility diff --git a/src/Files.App/ViewModels/UserControls/Previews/RichTextPreviewViewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/RichTextPreviewViewModel.cs index fda43bedea55..18d55829a526 100644 --- a/src/Files.App/ViewModels/UserControls/Previews/RichTextPreviewViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Previews/RichTextPreviewViewModel.cs @@ -1,20 +1,17 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.ViewModels.Properties; using Windows.Storage.Streams; namespace Files.App.ViewModels.Previews { - public sealed class RichTextPreviewViewModel : BasePreviewModel + public sealed partial class RichTextPreviewViewModel : BasePreviewModel { public IRandomAccessStream Stream { get; set; } public RichTextPreviewViewModel(ListedItem item) : base(item) { } - public static bool ContainsExtension(string extension) - => extension is ".rtf"; - public async override Task> LoadPreviewAndDetailsAsync() { Stream = await Item.ItemFile.OpenReadAsync(); diff --git a/src/Files.App/ViewModels/UserControls/Previews/ShellPreviewViewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/ShellPreviewViewModel.cs index 4a932c1de4df..d7dbab8ae08f 100644 --- a/src/Files.App/ViewModels/UserControls/Previews/ShellPreviewViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Previews/ShellPreviewViewModel.cs @@ -1,124 +1,183 @@ -using Files.App.ViewModels.Properties; +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Files.App.Helpers; +using Files.App.ViewModels.Properties; +using Microsoft.Extensions.Logging; using Microsoft.UI.Content; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Hosting; using System.Runtime.InteropServices; -using System.Text; -using Vanara.PInvoke; using Windows.Win32; +using Windows.Win32.Foundation; using Windows.Win32.Graphics.Direct3D; using Windows.Win32.Graphics.Direct3D11; -using Windows.Win32.Graphics.Dxgi; using Windows.Win32.Graphics.DirectComposition; -using WinRT; -using Windows.Win32; using Windows.Win32.Graphics.Dwm; -using static Vanara.PInvoke.ShlwApi; -using static Vanara.PInvoke.User32; +using Windows.Win32.Graphics.Dxgi; +using Windows.Win32.System.Com; +using Windows.Win32.UI.Shell; +using Windows.Win32.UI.WindowsAndMessaging; +using WinRT; + +#pragma warning disable CS8305 // Type is for evaluation purposes only and is subject to change or removal in future updates. namespace Files.App.ViewModels.Previews { - public sealed class ShellPreviewViewModel : BasePreviewModel + public sealed partial class ShellPreviewViewModel : BasePreviewModel { - public ShellPreviewViewModel(ListedItem item) - : base(item) + // Fields + + ContentExternalOutputLink? _contentExternalOutputLink; + PreviewHandler? _previewHandler; + WNDCLASSEXW _windowClass; + WNDPROC _windProc = null!; + HWND _hWnd = HWND.Null; + bool _isOfficePreview = false; + unsafe char* _pszClassName; + + // Constructor + + public ShellPreviewViewModel(ListedItem item) : base(item) { } - public async override Task> LoadPreviewAndDetailsAsync() - => []; - - private const string IPreviewHandlerIid = "{8895b1c6-b41f-4c1c-a562-0d564250836f}"; - private static readonly Guid QueryAssociationsClsid = new Guid(0xa07034fd, 0x6caa, 0x4954, 0xac, 0x3f, 0x97, 0xa2, 0x72, 0x16, 0xf9, 0x8a); - private static readonly Guid IQueryAssociationsIid = Guid.ParseExact("c46ca590-3c3f-11d2-bee6-0000f805ca57", "d"); + // Methods - PreviewHandler? currentHandler; - ContentExternalOutputLink? outputLink; - WindowClass? wCls; - HWND hwnd = HWND.NULL; - bool isOfficePreview = false; + public override Task> LoadPreviewAndDetailsAsync() + => Task.FromResult>([]); - public static Guid? FindPreviewHandlerFor(string extension, IntPtr hwnd) + public static unsafe Guid? FindPreviewHandlerFor(string extension, nint hwnd) { if (string.IsNullOrEmpty(extension)) return null; - var hr = AssocCreate(QueryAssociationsClsid, IQueryAssociationsIid, out var queryAssoc); - if (!hr.Succeeded) - return null; + try { - if (queryAssoc == null) - return null; - queryAssoc.Init(ASSOCF.ASSOCF_INIT_DEFAULTTOSTAR, extension, IntPtr.Zero, hwnd); - var sb = new StringBuilder(128); - uint cch = 64; - queryAssoc.GetString(ASSOCF.ASSOCF_NOTRUNCATE, ASSOCSTR.ASSOCSTR_SHELLEXTENSION, IPreviewHandlerIid, sb, ref cch); - Debug.WriteLine($"Preview handler for {extension}: {sb}"); - return Guid.Parse(sb.ToString()); + fixed (char* pszAssoc = extension, + pszExtra = "{8895b1c6-b41f-4c1c-a562-0d564250836f}", + pszOutput = new char[1024]) + { + PWSTR pwszAssoc = new(pszAssoc); + PWSTR pwszExtra = new(pszExtra); + PWSTR pwszOutput = new(pszOutput); + uint cchOutput = 2024; + + // Try to find registered preview handler associated with specified extension name + var res = PInvoke.AssocQueryString( + ASSOCF.ASSOCF_NOTRUNCATE, + ASSOCSTR.ASSOCSTR_SHELLEXTENSION, + pwszAssoc, + pwszExtra, + pwszOutput, + &cchOutput); + + return Guid.Parse(pwszOutput.ToString()); + } } catch { return null; } - finally - { - Marshal.ReleaseComObject(queryAssoc); - } } public void SizeChanged(RECT size) { - if (hwnd != HWND.NULL) - SetWindowPos(hwnd, HWND.HWND_TOP, size.Left, size.Top, size.Width, size.Height, SetWindowPosFlags.SWP_NOACTIVATE); - if (currentHandler != null) - currentHandler.ResetBounds(new(0, 0, size.Width, size.Height)); - if (outputLink is not null) - outputLink.PlacementVisual.Size = new(size.Width, size.Height); + if (_hWnd != HWND.Null) + PInvoke.SetWindowPos(_hWnd, new(0), size.left, size.top, size.Width, size.Height, SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE); + + _previewHandler?.ResetBounds(new(0, 0, size.Width, size.Height)); + + if (_contentExternalOutputLink is not null) + _contentExternalOutputLink.PlacementVisual.Size = new(size.Width, size.Height); } - private IntPtr WndProc(HWND hwnd, uint msg, IntPtr wParam, IntPtr lParam) + private unsafe LRESULT WndProc(HWND hwnd, uint msg, WPARAM wParam, LPARAM lParam) { - if (msg == (uint)WindowMessage.WM_CREATE) + if (msg is PInvoke.WM_CREATE) { - var clsid = FindPreviewHandlerFor(Item.FileExtension, hwnd.DangerousGetHandle()); - isOfficePreview = new Guid?[] { - Guid.Parse("84F66100-FF7C-4fb4-B0C0-02CD7FB668FE"), - Guid.Parse("65235197-874B-4A07-BDC5-E65EA825B718"), - Guid.Parse("00020827-0000-0000-C000-000000000046") }.Contains(clsid); + var clsid = FindPreviewHandlerFor(Item.FileExtension, hwnd); + + _isOfficePreview = new Guid?[] + { + Guid.Parse("84F66100-FF7C-4fb4-B0C0-02CD7FB668FE"), // preview handler for Word files + Guid.Parse("65235197-874B-4A07-BDC5-E65EA825B718"), // preview handler for PowerPoint files + Guid.Parse("00020827-0000-0000-C000-000000000046") // preview handler for Excel files + }.Contains(clsid); + try { - currentHandler = new PreviewHandler(clsid.Value, hwnd.DangerousGetHandle()); - currentHandler.InitWithFileWithEveryWay(Item.ItemPath); - currentHandler.DoPreview(); + _previewHandler = new PreviewHandler(clsid!.Value, hwnd); + _previewHandler.InitWithFileWithEveryWay(Item.ItemPath); + _previewHandler.DoPreview(); } - catch (Exception ex) + catch { UnloadPreview(); } } - else if (msg == (uint)WindowMessage.WM_DESTROY) + else if (msg is PInvoke.WM_DESTROY) { - if (currentHandler is not null) + if (_previewHandler is not null) { - currentHandler.UnloadPreview(); - currentHandler = null; + _previewHandler.Dispose(); + _previewHandler = null; } } - return DefWindowProc(hwnd, msg, wParam, lParam); + + return PInvoke.DefWindowProc(hwnd, msg, wParam, lParam); } - public void LoadPreview(UIElement presenter) + public unsafe void LoadPreview(UIElement presenter) { + App.Logger.LogInformation($"ShellPreview.LoadPreview: Item={LogPathHelper.GetPathIdentifier(Item?.ItemPath)}"); + var parent = MainWindow.Instance.WindowHandle; + var hInst = PInvoke.GetModuleHandle(default(PWSTR)); + var szClassName = $"{nameof(ShellPreviewViewModel)}-{Guid.NewGuid()}"; + var szWindowName = $"Preview"; + + _pszClassName = (char*)Marshal.StringToHGlobalUni(szClassName); + + _windProc = new(WndProc); + var pWindProc = Marshal.GetFunctionPointerForDelegate(_windProc); + var pfnWndProc = (delegate* unmanaged[Stdcall])pWindProc; - HINSTANCE hInst = Kernel32.GetModuleHandle(); - wCls = new WindowClass($"{GetType().Name}{Guid.NewGuid()}", hInst, WndProc); - hwnd = CreateWindowEx(WindowStylesEx.WS_EX_LAYERED | WindowStylesEx.WS_EX_COMPOSITED, wCls.ClassName, "Preview", WindowStyles.WS_CHILD | WindowStyles.WS_CLIPSIBLINGS | WindowStyles.WS_VISIBLE, 0, 0, 0, 0, hWndParent: parent, hInstance: hInst); + _windowClass = new WNDCLASSEXW() + { + cbSize = (uint)sizeof(WNDCLASSEXW), + lpfnWndProc = pfnWndProc, + hInstance = hInst, + lpszClassName = _pszClassName, + style = 0, + hIcon = default, + hIconSm = default, + hCursor = default, + hbrBackground = default, + lpszMenuName = null, + cbClsExtra = 0, + cbWndExtra = 0, + }; + + PInvoke.RegisterClassEx(_windowClass); + + fixed (char* pszWindowName = szWindowName) + { + _hWnd = PInvoke.CreateWindowEx( + WINDOW_EX_STYLE.WS_EX_LAYERED | WINDOW_EX_STYLE.WS_EX_COMPOSITED, + _pszClassName, + pszWindowName, + WINDOW_STYLE.WS_CHILD | WINDOW_STYLE.WS_CLIPSIBLINGS | WINDOW_STYLE.WS_VISIBLE, + 0, 0, 0, 0, + new(parent), + HMENU.Null, + hInst); + } _ = ChildWindowToXaml(parent, presenter); } - private unsafe bool ChildWindowToXaml(IntPtr parent, UIElement presenter) + private unsafe bool ChildWindowToXaml(nint parent, UIElement presenter) { D3D_DRIVER_TYPE[] driverTypes = [ @@ -126,118 +185,128 @@ private unsafe bool ChildWindowToXaml(IntPtr parent, UIElement presenter) D3D_DRIVER_TYPE.D3D_DRIVER_TYPE_WARP, ]; - ID3D11Device? d3d11Device = null; - ID3D11DeviceContext? d3d11DeviceContext = null; + HRESULT hr = default; + Guid IID_IDCompositionDevice = typeof(IDCompositionDevice).GUID; + using ComPtr pD3D11Device = default; + using ComPtr pD3D11DeviceContext = default; + using ComPtr pDXGIDevice = default; + using ComPtr pDCompositionDevice = default; + using ComPtr pControlSurface = default; + ComPtr pChildVisual = default; // Don't dispose this one, it's used by the compositor - foreach (var driveType in driverTypes) + // Create the D3D11 device + foreach (var driverType in driverTypes) { - var hr = PInvoke.D3D11CreateDevice( - null, - driveType, - new(IntPtr.Zero), + hr = PInvoke.D3D11CreateDevice( + null, driverType, new(nint.Zero), D3D11_CREATE_DEVICE_FLAG.D3D11_CREATE_DEVICE_BGRA_SUPPORT, - null, - 0, - 7, - out d3d11Device, - null, - out d3d11DeviceContext); + null, /* FeatureLevels */ 0, /* SDKVersion */ 7, + pD3D11Device.GetAddressOf(), null, + pD3D11DeviceContext.GetAddressOf()); if (hr.Succeeded) break; } - if (d3d11Device is null) + if (pD3D11Device.IsNull) return false; - IDXGIDevice dxgiDevice = (IDXGIDevice)d3d11Device; - if (PInvoke.DCompositionCreateDevice(dxgiDevice, typeof(IDCompositionDevice).GUID, out var compDevicePtr).Failed) + + // Create the DComp device + pDXGIDevice.Attach((IDXGIDevice*)pD3D11Device.Get()); + hr = PInvoke.DCompositionCreateDevice( + pDXGIDevice.Get(), + &IID_IDCompositionDevice, + (void**)pDCompositionDevice.GetAddressOf()); + if (hr.Failed) return false; - IDCompositionDevice compDevice = (IDCompositionDevice)compDevicePtr; - compDevice.CreateVisual(out var childVisual); - compDevice.CreateSurfaceFromHwnd(new(hwnd.DangerousGetHandle()), out var controlSurface); - childVisual.SetContent(controlSurface); - if (childVisual is null || controlSurface is null) + // Create the visual + hr = pDCompositionDevice.Get()->CreateVisual(pChildVisual.GetAddressOf()); + hr = pDCompositionDevice.Get()->CreateSurfaceFromHwnd(_hWnd, pControlSurface.GetAddressOf()); + hr = pChildVisual.Get()->SetContent(pControlSurface.Get()); + if (pChildVisual.IsNull || pControlSurface.IsNull) return false; + // Get the compositor and set the visual on it var compositor = ElementCompositionPreview.GetElementVisual(presenter).Compositor; - outputLink = ContentExternalOutputLink.Create(compositor); - IDCompositionTarget target = outputLink.As(); - target.SetRoot(childVisual); - - outputLink.PlacementVisual.Size = new(0, 0); - outputLink.PlacementVisual.Scale = new(1/(float)presenter.XamlRoot.RasterizationScale); - ElementCompositionPreview.SetElementChildVisual(presenter, outputLink.PlacementVisual); - - compDevice.Commit(); - - Marshal.ReleaseComObject(target); - Marshal.ReleaseComObject(childVisual); - Marshal.ReleaseComObject(controlSurface); - Marshal.ReleaseComObject(compDevice); - Marshal.ReleaseComObject(compDevicePtr); - Marshal.ReleaseComObject(dxgiDevice); - Marshal.ReleaseComObject(d3d11Device); - Marshal.ReleaseComObject(d3d11DeviceContext); - - unsafe - { - var dwAttrib = Convert.ToUInt32(true); + _contentExternalOutputLink = ContentExternalOutputLink.Create(compositor); - return - PInvoke.DwmSetWindowAttribute( - new((nint)hwnd), - DWMWINDOWATTRIBUTE.DWMWA_CLOAK, - &dwAttrib, - (uint)Marshal.SizeOf(dwAttrib)) - .Succeeded; - } + var target = _contentExternalOutputLink.As(); + target.SetRoot((nint)pChildVisual.Get()); + + _contentExternalOutputLink.PlacementVisual.Size = new(0, 0); + _contentExternalOutputLink.PlacementVisual.Scale = new(1 / (float)presenter.XamlRoot.RasterizationScale); + ElementCompositionPreview.SetElementChildVisual(presenter, _contentExternalOutputLink.PlacementVisual); + + // Commit the all pending DComp commands + pDCompositionDevice.Get()->Commit(); + + var dwAttrib = Convert.ToUInt32(true); + + return + PInvoke.DwmSetWindowAttribute( + new((nint)_hWnd), + DWMWINDOWATTRIBUTE.DWMWA_CLOAK, + &dwAttrib, + (uint)Marshal.SizeOf(dwAttrib)) + .Succeeded; } - public void UnloadPreview() + public unsafe void UnloadPreview() { - if (hwnd != HWND.NULL) - DestroyWindow(hwnd); - if (outputLink is not null) - outputLink.Dispose(); - if (wCls is not null) - UnregisterClass(wCls.ClassName, Kernel32.GetModuleHandle()); + if (_hWnd != HWND.Null) + { + PInvoke.DestroyWindow(_hWnd); + App.Logger.LogInformation($"ShellPreview.UnloadPreview: HWND={((nint)_hWnd)}, Item={LogPathHelper.GetPathIdentifier(Item?.ItemPath)}"); + } + else + App.Logger.LogInformation($"ShellPreview.UnloadPreview: HWND=, Item={LogPathHelper.GetPathIdentifier(Item?.ItemPath)}"); + + + _contentExternalOutputLink?.Dispose(); + _contentExternalOutputLink = null; + + PInvoke.UnregisterClass(_windowClass.lpszClassName, PInvoke.GetModuleHandle(default(PWSTR))); + + if (_pszClassName is not null) + { + Marshal.FreeHGlobal((nint)_pszClassName); + _pszClassName = null; + } } - public void PointerEntered(bool onPreview) + public unsafe void PointerEntered(bool onPreview) { if (onPreview) { - unsafe - { - var dwAttrib = Convert.ToUInt32(false); + var dwAttrib = Convert.ToUInt32(false); - PInvoke.DwmSetWindowAttribute( - new((nint)hwnd), - DWMWINDOWATTRIBUTE.DWMWA_CLOAK, - &dwAttrib, - (uint)Marshal.SizeOf(dwAttrib)); - } + PInvoke.DwmSetWindowAttribute( + new((nint)_hWnd), + DWMWINDOWATTRIBUTE.DWMWA_CLOAK, + &dwAttrib, + (uint)Marshal.SizeOf(dwAttrib)); - if (isOfficePreview) - Win32Helper.SetWindowLong(hwnd, WindowLongFlags.GWL_EXSTYLE, 0); + if (_isOfficePreview) + PInvoke.SetWindowLongPtr(new((nint)_hWnd), WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, 0); } else { - Win32Helper.SetWindowLong(hwnd, WindowLongFlags.GWL_EXSTYLE, - (nint)(WindowStylesEx.WS_EX_LAYERED | WindowStylesEx.WS_EX_COMPOSITED)); + PInvoke.SetWindowLongPtr( + new((nint)_hWnd), + WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, + (nint)(WINDOW_EX_STYLE.WS_EX_LAYERED | WINDOW_EX_STYLE.WS_EX_COMPOSITED)); - unsafe - { - var dwAttrib = Convert.ToUInt32(true); + var dwAttrib = Convert.ToUInt32(true); - PInvoke.DwmSetWindowAttribute( - new((nint)hwnd), - DWMWINDOWATTRIBUTE.DWMWA_CLOAK, - &dwAttrib, - (uint)Marshal.SizeOf(dwAttrib)); - } + PInvoke.DwmSetWindowAttribute( + new((nint)_hWnd), + DWMWINDOWATTRIBUTE.DWMWA_CLOAK, + &dwAttrib, + (uint)Marshal.SizeOf(dwAttrib)); } } } } + +#pragma warning restore CS8305 // Type is for evaluation purposes only and is subject to change or removal in future updates. diff --git a/src/Files.App/ViewModels/UserControls/Previews/ShortcutPreviewViewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/ShortcutPreviewViewModel.cs index 46f6f964fe9b..ee0d5126488d 100644 --- a/src/Files.App/ViewModels/UserControls/Previews/ShortcutPreviewViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Previews/ShortcutPreviewViewModel.cs @@ -1,17 +1,17 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.ViewModels.Properties; namespace Files.App.ViewModels.Previews { - internal sealed class ShortcutPreviewViewModel : BasePreviewModel + internal sealed partial class ShortcutPreviewViewModel : BasePreviewModel { public ShortcutPreviewViewModel(ListedItem item) : base(item) { } public async override Task> LoadPreviewAndDetailsAsync() { - var item = Item as ShortcutItem; + var item = Item as IShortcutItem; var details = new List { GetFileProperty("PropertyParsingPath", item.ItemPath), diff --git a/src/Files.App/ViewModels/UserControls/Previews/TextPreviewViewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/TextPreviewViewModel.cs index 1081792c7d8f..0fa858db2077 100644 --- a/src/Files.App/ViewModels/UserControls/Previews/TextPreviewViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Previews/TextPreviewViewModel.cs @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.UserControls.FilePreviews; using Files.App.ViewModels.Properties; namespace Files.App.ViewModels.Previews { - public sealed class TextPreviewViewModel : BasePreviewModel + public sealed partial class TextPreviewViewModel : BasePreviewModel { private string textValue; public string TextValue @@ -20,9 +20,6 @@ public TextPreviewViewModel(ListedItem item) { } - public static bool ContainsExtension(string extension) - => extension is ".txt"; - public async override Task> LoadPreviewAndDetailsAsync() { var details = new List(); diff --git a/src/Files.App/ViewModels/UserControls/SearchBoxViewModel.cs b/src/Files.App/ViewModels/UserControls/SearchBoxViewModel.cs deleted file mode 100644 index e61d35f200c7..000000000000 --- a/src/Files.App/ViewModels/UserControls/SearchBoxViewModel.cs +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Input; -using Windows.Foundation; -using Windows.System; - -namespace Files.App.ViewModels.UserControls -{ - public sealed class SearchBoxViewModel : ObservableObject, ISearchBoxViewModel - { - private string query; - public string Query - { - get => query; - set => SetProperty(ref query, value); - } - - public bool WasQuerySubmitted { get; set; } = false; - - public event TypedEventHandler? TextChanged; - - public event TypedEventHandler? QuerySubmitted; - - public event EventHandler? Escaped; - - private readonly SuggestionComparer suggestionComparer = new SuggestionComparer(); - - public ObservableCollection Suggestions { get; } = []; - - private readonly List oldQueries = []; - - public void ClearSuggestions() - { - Suggestions.Clear(); - } - - public void SetSuggestions(IEnumerable suggestions) - { - ClearSuggestions(); - - var items = suggestions.OrderBy(suggestion => suggestion, suggestionComparer).ToList(); - - var oldSuggestions = Suggestions.Except(items, suggestionComparer).ToList(); - foreach (var oldSuggestion in oldSuggestions) - Suggestions.Remove(oldSuggestion); - - var newSuggestions = items.Except(Suggestions, suggestionComparer).ToList(); - foreach (var newSuggestion in newSuggestions) - { - var indexSuggestion = Suggestions.FirstOrDefault(suggestion => suggestionComparer.Compare(suggestion, newSuggestion) < 1); - if (!(indexSuggestion is null)) - { - int index = Suggestions.IndexOf(indexSuggestion); - Suggestions.Insert(index, newSuggestion); - } - else - { - Suggestions.Add(newSuggestion); - } - } - } - - public void SearchRegion_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs e) - { - TextChanged?.Invoke(this, new SearchBoxTextChangedEventArgs(e.Reason)); - } - - public void SearchRegion_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs e) - { - if (e.ChosenSuggestion is null && string.IsNullOrWhiteSpace(e.QueryText)) - return; - - WasQuerySubmitted = true; - - if (e.ChosenSuggestion is SuggestionModel chosen && chosen.ItemPath is null) - { - Query = chosen.Name; - QuerySubmitted?.Invoke(this, new SearchBoxQuerySubmittedEventArgs(null)); - } - else - { - QuerySubmitted?.Invoke(this, new SearchBoxQuerySubmittedEventArgs((SuggestionModel)e.ChosenSuggestion)); - } - - if (!string.IsNullOrWhiteSpace(e.QueryText)) - { - // If the element is already contained, update its position - if (oldQueries.FirstOrDefault(suggestion => suggestion.Name == e.QueryText) is SuggestionModel old) - oldQueries.Remove(old); - - oldQueries.Insert(0, new SuggestionModel(e.QueryText, true)); - - // Limit to last 10 queries to improve performance - if (oldQueries.Count > 10) - oldQueries.RemoveAt(10); - } - } - - public void SearchRegion_Escaped(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs e) - { - Escaped?.Invoke(this, this); - } - - public void SearchRegion_GotFocus(object sender, RoutedEventArgs e) - { - if (string.IsNullOrWhiteSpace(query)) - AddRecentQueries(); - } - - public void SearchRegion_KeyDown(object sender, KeyRoutedEventArgs e) - { - e.Handled = - e.Key is VirtualKey.Left || - e.Key is VirtualKey.Right || - ((e.Key is VirtualKey.Up || e.Key is VirtualKey.Down) && Suggestions.Count == 0); - } - - public void AddRecentQueries() - { - ClearSuggestions(); - oldQueries.ForEach(Suggestions.Add); - } - - private sealed class SuggestionComparer : IEqualityComparer, IComparer - { - public int Compare(SuggestionModel x, SuggestionModel y) - => y.ItemPath.CompareTo(x.ItemPath); - - public bool Equals(SuggestionModel x, SuggestionModel y) - => y.ItemPath.Equals(x.ItemPath); - - public int GetHashCode(SuggestionModel o) - => o.ItemPath.GetHashCode(); - } - } -} diff --git a/src/Files.App/ViewModels/UserControls/ShelfViewModel.cs b/src/Files.App/ViewModels/UserControls/ShelfViewModel.cs new file mode 100644 index 000000000000..8b80c2b61e93 --- /dev/null +++ b/src/Files.App/ViewModels/UserControls/ShelfViewModel.cs @@ -0,0 +1,108 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Files.Shared.Utils; +using System.Collections.Specialized; + +namespace Files.App.ViewModels.UserControls +{ + [Bindable(true)] + public sealed partial class ShelfViewModel : ObservableObject, IAsyncInitialize + { + private readonly Dictionary _watchers; + + public ObservableCollection Items { get; } + + public ShelfViewModel() + { + _watchers = new(); + Items = new(); + Items.CollectionChanged += Items_CollectionChanged; + } + + /// + public Task InitAsync(CancellationToken cancellationToken = default) + { + // TODO: Load persisted shelf items + return Task.CompletedTask; + } + + [RelayCommand] + private void ClearItems() + { + Items.Clear(); + } + + private async void Items_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add when e.NewItems is not null: + { + if (e.NewItems[0] is not ShelfItem shelfItem) + return; + + var parentPath = SystemIO.Path.GetDirectoryName(shelfItem.Inner.Id) ?? string.Empty; + if (_watchers.TryGetValue(parentPath, out var reference)) + { + // Only increase the reference count if the watcher already exists + reference.Item2++; + return; + } + + if (await shelfItem.Inner.GetParentAsync() is not IMutableFolder mutableFolder) + return; + + // Register new watcher + var watcher = await mutableFolder.GetFolderWatcherAsync(); + watcher.CollectionChanged += Watcher_CollectionChanged; + + _watchers.Add(parentPath, (watcher, 1)); + break; + } + + case NotifyCollectionChangedAction.Remove when e.OldItems is not null: + { + if (e.OldItems[0] is not ShelfItem shelfItem) + return; + + var parentPath = SystemIO.Path.GetDirectoryName(shelfItem.Inner.Id) ?? string.Empty; + if (!_watchers.TryGetValue(parentPath, out var reference)) + return; + + // Decrease the reference count and remove the watcher if no references are present + reference.Item2--; + if (reference.Item2 < 1) + { + reference.Item1.CollectionChanged -= Watcher_CollectionChanged; + reference.Item1.Dispose(); + _watchers.Remove(parentPath); + } + + break; + } + } + } + + private async void Watcher_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) + { + if (sender is not IFolderWatcher watcher) + return; + + switch (e.Action) + { + case NotifyCollectionChangedAction.Remove when e.OldItems is not null: + { + // Remove the matching item notified from the watcher + var item = e.OldItems.Cast().ElementAt(0); + var itemToRemove = Items.FirstOrDefault(x => x.Inner.Id == item.Id); + if (itemToRemove is null) + return; + + await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => Items.Remove(itemToRemove)); + break; + } + } + } + } +} diff --git a/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs b/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs index 4b7f2e43d5a6..d3f9f314479a 100644 --- a/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs @@ -1,9 +1,8 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. +using Files.App.Controls; using Files.App.Helpers.ContextFlyouts; -using Files.App.UserControls.Sidebar; -using Files.App.ViewModels.Dialogs; using Microsoft.UI.Input; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -12,17 +11,15 @@ using System.Collections.Specialized; using System.IO; using System.Windows.Input; -using Windows.ApplicationModel.DataTransfer.DragDrop; using Windows.ApplicationModel.DataTransfer; +using Windows.ApplicationModel.DataTransfer.DragDrop; using Windows.Storage; using Windows.System; using Windows.UI.Core; -using Files.Core.Storage; -using Files.Core.Storage.Extensions; namespace Files.App.ViewModels.UserControls { - public sealed class SidebarViewModel : ObservableObject, IDisposable, ISidebarViewModel + public sealed partial class SidebarViewModel : ObservableObject, IDisposable { private INetworkService NetworkService { get; } = Ioc.Default.GetRequiredService(); private IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetRequiredService(); @@ -61,6 +58,7 @@ public SidebarDisplayMode SidebarDisplayMode if (SetProperty(ref sidebarDisplayMode, value)) { OnPropertyChanged(nameof(IsSidebarCompactSize)); + OnPropertyChanged(nameof(AreSectionsHidden)); IsSidebarOpen = sidebarDisplayMode == SidebarDisplayMode.Expanded; UpdateTabControlMargin(); } @@ -136,6 +134,16 @@ public bool IsSidebarOpen } } + public bool AreSectionsHidden => + !ShowPinnedFoldersSection && + !ShowLibrarySection && + !ShowDrivesSection && + !ShowCloudDrivesSection && + !ShowNetworkSection && + (!ShowWslSection || WSLDistroManager.Distros.Any() == false) && + !ShowFileTagsSection && + SidebarDisplayMode is not SidebarDisplayMode.Compact; + public bool ShowPinnedFoldersSection { get => UserSettingsService.GeneralSettingsService.ShowPinnedSection; @@ -247,9 +255,9 @@ public SidebarViewModel() App.QuickAccessManager.Model.DataChanged += Manager_DataChanged; App.LibraryManager.DataChanged += Manager_DataChanged; - drivesViewModel.Drives.CollectionChanged += (x, args) => Manager_DataChanged(SectionType.Drives, args); + drivesViewModel.Drives.CollectionChanged += Manager_DataChangedForDrives; CloudDrivesManager.DataChanged += Manager_DataChanged; - NetworkService.Computers.CollectionChanged += (x, args) => Manager_DataChanged(SectionType.Network, args); + NetworkService.Computers.CollectionChanged += Manager_DataChangedForNetworkComputers; WSLDistroManager.DataChanged += Manager_DataChanged; App.FileTagsManager.DataChanged += Manager_DataChanged; SidebarDisplayMode = UserSettingsService.AppearanceSettingsService.IsSidebarOpen ? SidebarDisplayMode.Expanded : SidebarDisplayMode.Compact; @@ -257,11 +265,7 @@ public SidebarViewModel() HideSectionCommand = new RelayCommand(HideSection); UnpinItemCommand = new RelayCommand(UnpinItem); PinItemCommand = new RelayCommand(PinItem); - OpenInNewTabCommand = new AsyncRelayCommand(OpenInNewTabAsync); - OpenInNewWindowCommand = new AsyncRelayCommand(OpenInNewWindowAsync); - OpenInNewPaneCommand = new AsyncRelayCommand(OpenInNewPaneAsync); - EjectDeviceCommand = new AsyncRelayCommand(EjectDeviceAsync); - FormatDriveCommand = new RelayCommand(FormatDrive); + EjectDeviceCommand = new RelayCommand(EjectDevice); OpenPropertiesCommand = new RelayCommand(OpenProperties); ReorderItemsCommand = new AsyncRelayCommand(ReorderItemsAsync); } @@ -273,6 +277,9 @@ private Task CreateItemHomeAsync() private async void Manager_DataChanged(object sender, NotifyCollectionChangedEventArgs e) { + if (dispatcherQueue is null) + return; + await dispatcherQueue.EnqueueOrInvokeAsync(async () => { var sectionType = (SectionType)sender; @@ -292,6 +299,10 @@ await dispatcherQueue.EnqueueOrInvokeAsync(async () => }); } + private void Manager_DataChangedForDrives(object? sender, NotifyCollectionChangedEventArgs e) => Manager_DataChanged(SectionType.Drives, e); + + private void Manager_DataChangedForNetworkComputers(object? sender, NotifyCollectionChangedEventArgs e) => Manager_DataChanged(SectionType.Network, e); + private async Task SyncSidebarItemsAsync(LocationItem section, Func> getElements, NotifyCollectionChangedEventArgs e) { if (section is null) @@ -397,7 +408,6 @@ await lib.CheckDefaultSaveFolderAccess() && } } - section.IsExpanded = Ioc.Default.GetRequiredService().Get(section.Text == "Pinned".GetLocalizedResource(), $"section:{section.Text.Replace('\\', '_')}"); section.PropertyChanged += Section_PropertyChanged; } @@ -405,7 +415,30 @@ private void Section_PropertyChanged(object? sender, PropertyChangedEventArgs e) { if (sender is LocationItem section && e.PropertyName == nameof(section.IsExpanded)) { - Ioc.Default.GetRequiredService().Set(section.IsExpanded, $"section:{section.Text.Replace('\\', '_')}"); + switch (section.Text) + { + case var text when text == Strings.Pinned.GetLocalizedResource(): + UserSettingsService.GeneralSettingsService.IsPinnedSectionExpanded = section.IsExpanded; + break; + case var text when text == Strings.SidebarLibraries.GetLocalizedResource(): + UserSettingsService.GeneralSettingsService.IsLibrarySectionExpanded = section.IsExpanded; + break; + case var text when text == Strings.Drives.GetLocalizedResource(): + UserSettingsService.GeneralSettingsService.IsDriveSectionExpanded = section.IsExpanded; + break; + case var text when text == Strings.SidebarCloudDrives.GetLocalizedResource(): + UserSettingsService.GeneralSettingsService.IsCloudDriveSectionExpanded = section.IsExpanded; + break; + case var text when text == Strings.Network.GetLocalizedResource(): + UserSettingsService.GeneralSettingsService.IsNetworkSectionExpanded = section.IsExpanded; + break; + case var text when text == Strings.WSL.GetLocalizedResource(): + UserSettingsService.GeneralSettingsService.IsWslSectionExpanded = section.IsExpanded; + break; + case var text when text == Strings.FileTags.GetLocalizedResource(): + UserSettingsService.GeneralSettingsService.IsFileTagsSectionExpanded = section.IsExpanded; + break; + } } } @@ -430,7 +463,7 @@ private async Task CreateSectionAsync(SectionType sectionType) { case SectionType.Home: { - section = BuildSection("Home".GetLocalizedResource(), sectionType, new ContextMenuOptions { IsLocationItem = true }, true); + section = BuildSection(Strings.Home.GetLocalizedResource(), sectionType, new ContextMenuOptions { IsLocationItem = true }, true); section.Path = "Home"; section.Icon = new BitmapImage(new Uri(Constants.FluentIconsPaths.HomeIcon)); section.IsHeader = true; @@ -445,9 +478,10 @@ private async Task CreateSectionAsync(SectionType sectionType) break; } - section = BuildSection("Pinned".GetLocalizedResource(), sectionType, new ContextMenuOptions { ShowHideSection = true }, false); + section = BuildSection(Strings.Pinned.GetLocalizedResource(), sectionType, new ContextMenuOptions { ShowHideSection = true }, false); icon = new BitmapImage(new Uri(Constants.FluentIconsPaths.StarIcon)); section.IsHeader = true; + section.IsExpanded = UserSettingsService.GeneralSettingsService.IsPinnedSectionExpanded; break; } @@ -458,9 +492,10 @@ private async Task CreateSectionAsync(SectionType sectionType) { break; } - section = BuildSection("SidebarLibraries".GetLocalizedResource(), sectionType, new ContextMenuOptions { IsLibrariesHeader = true, ShowHideSection = true }, false); + section = BuildSection(Strings.SidebarLibraries.GetLocalizedResource(), sectionType, new ContextMenuOptions { IsLibrariesHeader = true, ShowHideSection = true }, false); iconIdex = Constants.ImageRes.Libraries; section.IsHeader = true; + section.IsExpanded = UserSettingsService.GeneralSettingsService.IsLibrarySectionExpanded; break; } @@ -471,9 +506,10 @@ private async Task CreateSectionAsync(SectionType sectionType) { break; } - section = BuildSection("Drives".GetLocalizedResource(), sectionType, new ContextMenuOptions { ShowHideSection = true }, false); + section = BuildSection(Strings.Drives.GetLocalizedResource(), sectionType, new ContextMenuOptions { ShowHideSection = true }, false); iconIdex = Constants.ImageRes.ThisPC; section.IsHeader = true; + section.IsExpanded = UserSettingsService.GeneralSettingsService.IsDriveSectionExpanded; break; } @@ -484,9 +520,10 @@ private async Task CreateSectionAsync(SectionType sectionType) { break; } - section = BuildSection("SidebarCloudDrives".GetLocalizedResource(), sectionType, new ContextMenuOptions { ShowHideSection = true }, false); + section = BuildSection(Strings.SidebarCloudDrives.GetLocalizedResource(), sectionType, new ContextMenuOptions { ShowHideSection = true }, false); icon = new BitmapImage(new Uri(Constants.FluentIconsPaths.CloudDriveIcon)); section.IsHeader = true; + section.IsExpanded = UserSettingsService.GeneralSettingsService.IsCloudDriveSectionExpanded; break; } @@ -497,9 +534,10 @@ private async Task CreateSectionAsync(SectionType sectionType) { break; } - section = BuildSection("Network".GetLocalizedResource(), sectionType, new ContextMenuOptions { ShowHideSection = true }, false); + section = BuildSection(Strings.Network.GetLocalizedResource(), sectionType, new ContextMenuOptions { ShowHideSection = true }, false); iconIdex = Constants.ImageRes.Network; section.IsHeader = true; + section.IsExpanded = UserSettingsService.GeneralSettingsService.IsNetworkSectionExpanded; break; } @@ -510,9 +548,10 @@ private async Task CreateSectionAsync(SectionType sectionType) { break; } - section = BuildSection("WSL".GetLocalizedResource(), sectionType, new ContextMenuOptions { ShowHideSection = true }, false); + section = BuildSection(Strings.WSL.GetLocalizedResource(), sectionType, new ContextMenuOptions { ShowHideSection = true }, false); icon = new BitmapImage(new Uri(Constants.WslIconsPaths.GenericIcon)); section.IsHeader = true; + section.IsExpanded = UserSettingsService.GeneralSettingsService.IsWslSectionExpanded; break; } @@ -523,9 +562,10 @@ private async Task CreateSectionAsync(SectionType sectionType) { break; } - section = BuildSection("FileTags".GetLocalizedResource(), sectionType, new ContextMenuOptions { ShowHideSection = true }, false); + section = BuildSection(Strings.FileTags.GetLocalizedResource(), sectionType, new ContextMenuOptions { IsTagsHeader = true, ShowHideSection = true }, false); icon = new BitmapImage(new Uri(Constants.FluentIconsPaths.FileTagsIcon)); section.IsHeader = true; + section.IsExpanded = UserSettingsService.GeneralSettingsService.IsFileTagsSectionExpanded; break; } @@ -608,30 +648,37 @@ private async void UserSettingsService_OnSettingChangedEvent(object sender, Sett case nameof(UserSettingsService.GeneralSettingsService.ShowPinnedSection): await UpdateSectionVisibilityAsync(SectionType.Pinned, ShowPinnedFoldersSection); OnPropertyChanged(nameof(ShowPinnedFoldersSection)); + OnPropertyChanged(nameof(AreSectionsHidden)); break; case nameof(UserSettingsService.GeneralSettingsService.ShowLibrarySection): await UpdateSectionVisibilityAsync(SectionType.Library, ShowLibrarySection); OnPropertyChanged(nameof(ShowLibrarySection)); + OnPropertyChanged(nameof(AreSectionsHidden)); break; case nameof(UserSettingsService.GeneralSettingsService.ShowCloudDrivesSection): await UpdateSectionVisibilityAsync(SectionType.CloudDrives, ShowCloudDrivesSection); OnPropertyChanged(nameof(ShowCloudDrivesSection)); + OnPropertyChanged(nameof(AreSectionsHidden)); break; case nameof(UserSettingsService.GeneralSettingsService.ShowDrivesSection): await UpdateSectionVisibilityAsync(SectionType.Drives, ShowDrivesSection); OnPropertyChanged(nameof(ShowDrivesSection)); + OnPropertyChanged(nameof(AreSectionsHidden)); break; case nameof(UserSettingsService.GeneralSettingsService.ShowNetworkSection): await UpdateSectionVisibilityAsync(SectionType.Network, ShowNetworkSection); OnPropertyChanged(nameof(ShowNetworkSection)); + OnPropertyChanged(nameof(AreSectionsHidden)); break; case nameof(UserSettingsService.GeneralSettingsService.ShowWslSection): await UpdateSectionVisibilityAsync(SectionType.WSL, ShowWslSection); OnPropertyChanged(nameof(ShowWslSection)); + OnPropertyChanged(nameof(AreSectionsHidden)); break; case nameof(UserSettingsService.GeneralSettingsService.ShowFileTagsSection): await UpdateSectionVisibilityAsync(SectionType.FileTag, ShowFileTagsSection); OnPropertyChanged(nameof(ShowFileTagsSection)); + OnPropertyChanged(nameof(AreSectionsHidden)); break; } } @@ -642,11 +689,13 @@ public void Dispose() App.QuickAccessManager.Model.DataChanged -= Manager_DataChanged; App.LibraryManager.DataChanged -= Manager_DataChanged; - drivesViewModel.Drives.CollectionChanged -= (x, args) => Manager_DataChanged(SectionType.Drives, args); + drivesViewModel.Drives.CollectionChanged -= Manager_DataChangedForDrives; CloudDrivesManager.DataChanged -= Manager_DataChanged; - NetworkService.Computers.CollectionChanged -= (x, args) => Manager_DataChanged(SectionType.Network, args); + NetworkService.Computers.CollectionChanged -= Manager_DataChangedForNetworkComputers; WSLDistroManager.DataChanged -= Manager_DataChanged; App.FileTagsManager.DataChanged -= Manager_DataChanged; + + dispatcherQueue = null; } public void UpdateTabControlMargin() @@ -680,7 +729,7 @@ public async void HandleItemContextInvokedAsync(object sender, ItemContextInvoke await foreach (var taggedItem in fileTagsService.GetItemsForTagAsync(tagItem.FileTag.Uid, cts.Token)) { items.Add(( - taggedItem.Storable.TryGetPath() ?? string.Empty, + taggedItem.Storable.Id, taggedItem.Storable is IFolder)); } @@ -692,13 +741,25 @@ public async void HandleItemContextInvokedAsync(object sender, ItemContextInvoke var itemContextMenuFlyout = new CommandBarFlyout() { - Placement = FlyoutPlacementMode.Full + Placement = FlyoutPlacementMode.Right, + AlwaysExpanded = true }; itemContextMenuFlyout.Opening += (sender, e) => App.LastOpenedFlyout = sender as CommandBarFlyout; var menuItems = GetLocationItemMenuItems(item, itemContextMenuFlyout); - var (_, secondaryElements) = ContextFlyoutModelToElementHelper.GetAppBarItemsFromModel(menuItems); + var (primaryElements, secondaryElements) = ContextFlyoutModelToElementHelper.GetAppBarItemsFromModel(menuItems); + + // Workaround for WinUI (#5508) - AppBarButtons don't auto-close CommandBarFlyout + var closeHandler = new RoutedEventHandler((s, e) => itemContextMenuFlyout.Hide()); + primaryElements + .OfType() + .ForEach(button => button.Click += closeHandler); + primaryElements + .OfType() + .ForEach(button => button.Click += closeHandler); + + primaryElements.ForEach(itemContextMenuFlyout.PrimaryCommands.Add); secondaryElements .OfType() @@ -735,7 +796,7 @@ public async void HandleItemInvokedAsync(object item, PointerUpdateKind pointerU middleClickPressed) && navigationControlItem.Path is not null) { - await NavigationHelpers.OpenPathInNewTab(navigationControlItem.Path, false); + await NavigationHelpers.OpenPathInNewTab(navigationControlItem.Path); return; } @@ -801,43 +862,12 @@ public async void HandleItemInvokedAsync(object item, PointerUpdateKind pointerU private ICommand UnpinItemCommand { get; } - private ICommand OpenInNewTabCommand { get; } - - private ICommand OpenInNewWindowCommand { get; } - - private ICommand OpenInNewPaneCommand { get; } - private ICommand EjectDeviceCommand { get; } - private ICommand FormatDriveCommand { get; } - private ICommand OpenPropertiesCommand { get; } private ICommand ReorderItemsCommand { get; } - private async Task OpenInNewPaneAsync() - { - if (await DriveHelpers.CheckEmptyDrive(rightClickedItem.Path)) - return; - PaneHolder.OpenSecondaryPane(rightClickedItem.Path); - } - - private async Task OpenInNewTabAsync() - { - if (await DriveHelpers.CheckEmptyDrive(rightClickedItem.Path)) - return; - - await NavigationHelpers.OpenPathInNewTab(rightClickedItem.Path, false); - } - - private async Task OpenInNewWindowAsync() - { - if (await DriveHelpers.CheckEmptyDrive(rightClickedItem.Path)) - return; - - await NavigationHelpers.OpenPathInNewWindowAsync(rightClickedItem.Path); - } - private void PinItem() { if (rightClickedItem is DriveItem) @@ -901,7 +931,7 @@ private void OpenProperties(CommandBarFlyout menu) ItemPath = locationItem.Path, ItemNameRaw = locationItem.Text, PrimaryItemAttribute = StorageItemTypes.Folder, - ItemType = "Folder".GetLocalizedResource(), + ItemType = Strings.Folder.GetLocalizedResource(), }; if (!string.Equals(locationItem.Path, Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.OrdinalIgnoreCase)) @@ -920,15 +950,9 @@ private void OpenProperties(CommandBarFlyout menu) menu.Closed += flyoutClosed; } - private async Task EjectDeviceAsync() + private void EjectDevice() { - var result = await DriveHelpers.EjectDeviceAsync(rightClickedItem.Path); - await UIHelpers.ShowDeviceEjectResultAsync(rightClickedItem is DriveItem driveItem ? driveItem.Type : Data.Items.DriveType.Unknown, result); - } - - private void FormatDrive() - { - Win32Helper.OpenFormatDriveDialog(rightClickedItem.Path); + DriveHelpers.EjectDeviceAsync(rightClickedItem.Path); } private List GetLocationItemMenuItems(INavigationControlItem item, CommandBarFlyout menu) @@ -950,14 +974,14 @@ private List GetLocationItemMenuItems(INavigatio { new ContextMenuFlyoutItemViewModel() { - Text = "SideBarCreateNewLibrary/Text".GetLocalizedResource(), + Text = Strings.SideBarCreateNewLibrary_Text.GetLocalizedResource(), Glyph = "\uE710", Command = CreateLibraryCommand, ShowItem = options.IsLibrariesHeader }, new ContextMenuFlyoutItemViewModel() { - Text = "SideBarRestoreLibraries/Text".GetLocalizedResource(), + Text = Strings.SideBarRestoreLibraries_Text.GetLocalizedResource(), Glyph = "\uE10E", Command = RestoreLibrariesCommand, ShowItem = options.IsLibrariesHeader @@ -970,67 +994,88 @@ private List GetLocationItemMenuItems(INavigatio { IsVisible = options.ShowEmptyRecycleBin, }.Build(), - new ContextMenuFlyoutItemViewModelBuilder(Commands.OpenInNewTabFromSidebarAction).Build(), - new ContextMenuFlyoutItemViewModelBuilder(Commands.OpenInNewWindowFromSidebarAction).Build(), - new ContextMenuFlyoutItemViewModelBuilder(Commands.OpenInNewPaneFromSidebarAction).Build(), + new ContextMenuFlyoutItemViewModelBuilder(Commands.OpenInNewTabFromSidebar) + { + IsVisible = UserSettingsService.GeneralSettingsService.ShowOpenInNewTab && Commands.OpenInNewTabFromSidebar.IsExecutable + }.Build(), + new ContextMenuFlyoutItemViewModelBuilder(Commands.OpenInNewWindowFromSidebar) + { + IsVisible = UserSettingsService.GeneralSettingsService.ShowOpenInNewWindow && Commands.OpenInNewWindowFromSidebar.IsExecutable + }.Build(), + new ContextMenuFlyoutItemViewModelBuilder(Commands.OpenInNewPaneFromSidebar) + { + IsVisible = UserSettingsService.GeneralSettingsService.ShowOpenInNewPane && Commands.OpenInNewPaneFromSidebar.IsExecutable + }.Build(), + new ContextMenuFlyoutItemViewModelBuilder(Commands.CopyItemFromSidebar) + { + IsPrimary = true, + IsVisible = Commands.CopyItemFromSidebar.IsExecutable + }.Build(), + new ContextMenuFlyoutItemViewModel() + { + Text = Strings.Properties.GetLocalizedResource(), + ThemedIconModel = new ThemedIconModel() + { + ThemedIconStyle = "App.ThemedIcons.Properties", + }, + Command = OpenPropertiesCommand, + CommandParameter = menu, + IsPrimary = true, + ShowItem = options.ShowProperties + }, new ContextMenuFlyoutItemViewModel() { - Text = "PinFolderToSidebar".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() + Text = Strings.PinFolderToSidebar.GetLocalizedResource(), + ThemedIconModel = new ThemedIconModel() { - OpacityIconStyle = "Icons.Pin.16x16", + ThemedIconStyle = "App.ThemedIcons.FavoritePin", }, Command = PinItemCommand, ShowItem = isDriveItem && !isDriveItemPinned }, new ContextMenuFlyoutItemViewModel() { - Text = "UnpinFolderFromSidebar".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() + Text = Strings.UnpinFolderFromSidebar.GetLocalizedResource(), + ThemedIconModel = new ThemedIconModel() { - OpacityIconStyle = "Icons.Unpin.16x16", + ThemedIconStyle = "App.ThemedIcons.FavoritePinRemove", }, Command = UnpinItemCommand, ShowItem = options.ShowUnpinItem || isDriveItemPinned }, new ContextMenuFlyoutItemViewModel() { - Text = "ReorderSidebarItemsDialogText".GetLocalizedResource(), + Text = Strings.ReorderSidebarItemsDialogText.GetLocalizedResource(), Glyph = "\uE8D8", Command = ReorderItemsCommand, ShowItem = isPinnedItem || item.Section is SectionType.Pinned }, new ContextMenuFlyoutItemViewModel() { - Text = string.Format("SideBarHideSectionFromSideBar/Text".GetLocalizedResource(), rightClickedItem.Text), + Text = string.Format(Strings.SideBarHideSectionFromSideBar_Text.GetLocalizedResource(), rightClickedItem.Text), Glyph = "\uE77A", Command = HideSectionCommand, ShowItem = options.ShowHideSection }, new ContextMenuFlyoutItemViewModel() { - Text = "Eject".GetLocalizedResource(), + Text = Strings.Eject.GetLocalizedResource(), Command = EjectDeviceCommand, ShowItem = options.ShowEjectDevice }, new ContextMenuFlyoutItemViewModel() { - Text = "FormatDriveText".GetLocalizedResource(), - Command = FormatDriveCommand, - CommandParameter = item, - ShowItem = options.ShowFormatDrive + ItemType = ContextMenuFlyoutItemType.Separator, + ShowItem = (UserSettingsService.GeneralSettingsService.ShowOpenTerminal && Commands.OpenTerminalFromSidebar.IsExecutable) || + Commands.OpenStorageSenseFromSidebar.IsExecutable || + Commands.FormatDriveFromSidebar.IsExecutable }, - new ContextMenuFlyoutItemViewModel() + new ContextMenuFlyoutItemViewModelBuilder(Commands.OpenTerminalFromSidebar) { - Text = "Properties".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() - { - OpacityIconStyle = "ColorIconProperties", - }, - Command = OpenPropertiesCommand, - CommandParameter = menu, - ShowItem = options.ShowProperties - }, + IsVisible = UserSettingsService.GeneralSettingsService.ShowOpenTerminal && Commands.OpenTerminalFromSidebar.IsExecutable + }.Build(), + new ContextMenuFlyoutItemViewModelBuilder(Commands.OpenStorageSenseFromSidebar).Build(), + new ContextMenuFlyoutItemViewModelBuilder(Commands.FormatDriveFromSidebar).Build(), new ContextMenuFlyoutItemViewModel() { ItemType = ContextMenuFlyoutItemType.Separator, @@ -1039,13 +1084,21 @@ private List GetLocationItemMenuItems(INavigatio }, new ContextMenuFlyoutItemViewModel() { - Text = "Loading".GetLocalizedResource(), + Text = Strings.Loading.GetLocalizedResource(), Glyph = "\xE712", Items = [], ID = "ItemOverflow", Tag = "ItemOverflow", IsEnabled = false, IsHidden = !options.ShowShellItems, + }, + new ContextMenuFlyoutItemViewModel() + { + Text = Strings.ManageTags.GetLocalizedResource(), + Glyph = "\uE8EC", + Command = Commands.OpenSettings, + CommandParameter = new SettingsNavigationParams() { PageKind = SettingsPageKind.TagsPage }, + ShowItem = options.IsTagsHeader } }.Where(x => x.ShowItem).ToList(); } @@ -1082,13 +1135,15 @@ private async Task HandleLocationItemDragOverAsync(LocationItem locationItem, It } else { - var captionText = "PinFolderToSidebar".GetLocalizedResource(); + var captionText = Strings.PinFolderToSidebar.GetLocalizedResource(); CompleteDragEventArgs(rawEvent, captionText, DataPackageOperation.Move); } } else if (isPathNull || (hasStorageItems && storageItems.AreItemsAlreadyInFolder(locationItem.Path)) || - locationItem.Path.StartsWith("Home", StringComparison.OrdinalIgnoreCase)) + locationItem.Path.StartsWith("Home", StringComparison.OrdinalIgnoreCase) || + locationItem.Path.StartsWith("ReleaseNotes", StringComparison.OrdinalIgnoreCase) || + locationItem.Path.StartsWith("Settings", StringComparison.OrdinalIgnoreCase)) { rawEvent.AcceptedOperation = DataPackageOperation.None; } @@ -1102,41 +1157,41 @@ private async Task HandleLocationItemDragOverAsync(LocationItem locationItem, It DataPackageOperation operationType; if (locationItem.Path.StartsWith(Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.Ordinal)) { - captionText = string.Format("MoveToFolderCaptionText".GetLocalizedResource(), locationItem.Text); + captionText = string.Format(Strings.MoveToFolderCaptionText.GetLocalizedResource(), locationItem.Text); // Some applications such as Edge can't raise the drop event by the Move flag (#14008), so we set the Copy flag as well. operationType = DataPackageOperation.Move | DataPackageOperation.Copy; } else if (rawEvent.Modifiers.HasFlag(DragDropModifiers.Alt) || rawEvent.Modifiers.HasFlag(DragDropModifiers.Control | DragDropModifiers.Shift)) { - captionText = string.Format("LinkToFolderCaptionText".GetLocalizedResource(), locationItem.Text); + captionText = string.Format(Strings.LinkToFolderCaptionText.GetLocalizedResource(), locationItem.Text); operationType = DataPackageOperation.Link; } else if (rawEvent.Modifiers.HasFlag(DragDropModifiers.Control)) { - captionText = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), locationItem.Text); + captionText = string.Format(Strings.CopyToFolderCaptionText.GetLocalizedResource(), locationItem.Text); operationType = DataPackageOperation.Copy; } else if (rawEvent.Modifiers.HasFlag(DragDropModifiers.Shift)) { - captionText = string.Format("MoveToFolderCaptionText".GetLocalizedResource(), locationItem.Text); + captionText = string.Format(Strings.MoveToFolderCaptionText.GetLocalizedResource(), locationItem.Text); // Some applications such as Edge can't raise the drop event by the Move flag (#14008), so we set the Copy flag as well. operationType = DataPackageOperation.Move | DataPackageOperation.Copy; } else if (storageItems.Any(x => x.Item is ZipStorageFile || x.Item is ZipStorageFolder) || ZipStorageFolder.IsZipPath(locationItem.Path)) { - captionText = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), locationItem.Text); + captionText = string.Format(Strings.CopyToFolderCaptionText.GetLocalizedResource(), locationItem.Text); operationType = DataPackageOperation.Copy; } else if (locationItem.IsDefaultLocation || storageItems.AreItemsInSameDrive(locationItem.Path)) { - captionText = string.Format("MoveToFolderCaptionText".GetLocalizedResource(), locationItem.Text); + captionText = string.Format(Strings.MoveToFolderCaptionText.GetLocalizedResource(), locationItem.Text); // Some applications such as Edge can't raise the drop event by the Move flag (#14008), so we set the Copy flag as well. operationType = DataPackageOperation.Move | DataPackageOperation.Copy; } else { - captionText = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), locationItem.Text); + captionText = string.Format(Strings.CopyToFolderCaptionText.GetLocalizedResource(), locationItem.Text); operationType = DataPackageOperation.Copy; } CompleteDragEventArgs(rawEvent, captionText, operationType); @@ -1154,7 +1209,7 @@ private async Task HandleDriveItemDragOverAsync(DriveItem driveItem, ItemDragOve var storageItems = await Utils.Storage.FilesystemHelpers.GetDraggedStorageItems(args.DroppedItem); var hasStorageItems = storageItems.Any(); - if ("Unknown".GetLocalizedResource().Equals(driveItem.SpaceText, StringComparison.OrdinalIgnoreCase) || + if (Strings.Unknown.GetLocalizedResource().Equals(driveItem.SpaceText, StringComparison.OrdinalIgnoreCase) || (hasStorageItems && storageItems.AreItemsAlreadyInFolder(driveItem.Path))) { args.RawEvent.AcceptedOperation = DataPackageOperation.None; @@ -1169,29 +1224,29 @@ private async Task HandleDriveItemDragOverAsync(DriveItem driveItem, ItemDragOve DataPackageOperation operationType; if (args.RawEvent.Modifiers.HasFlag(DragDropModifiers.Alt) || args.RawEvent.Modifiers.HasFlag(DragDropModifiers.Control | DragDropModifiers.Shift)) { - captionText = string.Format("LinkToFolderCaptionText".GetLocalizedResource(), driveItem.Text); + captionText = string.Format(Strings.LinkToFolderCaptionText.GetLocalizedResource(), driveItem.Text); operationType = DataPackageOperation.Link; } else if (args.RawEvent.Modifiers.HasFlag(DragDropModifiers.Control)) { - captionText = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), driveItem.Text); + captionText = string.Format(Strings.CopyToFolderCaptionText.GetLocalizedResource(), driveItem.Text); operationType = DataPackageOperation.Copy; } else if (args.RawEvent.Modifiers.HasFlag(DragDropModifiers.Shift)) { - captionText = string.Format("MoveToFolderCaptionText".GetLocalizedResource(), driveItem.Text); + captionText = string.Format(Strings.MoveToFolderCaptionText.GetLocalizedResource(), driveItem.Text); // Some applications such as Edge can't raise the drop event by the Move flag (#14008), so we set the Copy flag as well. operationType = DataPackageOperation.Move | DataPackageOperation.Copy; } else if (storageItems.AreItemsInSameDrive(driveItem.Path)) { - captionText = string.Format("MoveToFolderCaptionText".GetLocalizedResource(), driveItem.Text); + captionText = string.Format(Strings.MoveToFolderCaptionText.GetLocalizedResource(), driveItem.Text); // Some applications such as Edge can't raise the drop event by the Move flag (#14008), so we set the Copy flag as well. operationType = DataPackageOperation.Move | DataPackageOperation.Copy; } else { - captionText = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), driveItem.Text); + captionText = string.Format(Strings.CopyToFolderCaptionText.GetLocalizedResource(), driveItem.Text); operationType = DataPackageOperation.Copy; } CompleteDragEventArgs(args.RawEvent, captionText, operationType); @@ -1207,19 +1262,18 @@ private async Task HandleTagItemDragOverAsync(FileTagItem tagItem, ItemDragOverE var storageItems = await Utils.Storage.FilesystemHelpers.GetDraggedStorageItems(args.DroppedItem); - if (!storageItems.Any()) + if (!storageItems.Any(x => !string.IsNullOrEmpty(x.Path))) { args.RawEvent.AcceptedOperation = DataPackageOperation.None; } else { args.RawEvent.DragUIOverride.IsCaptionVisible = true; - args.RawEvent.DragUIOverride.Caption = string.Format("LinkToFolderCaptionText".GetLocalizedResource(), tagItem.Text); + args.RawEvent.DragUIOverride.Caption = string.Format(Strings.LinkToFolderCaptionText.GetLocalizedResource(), tagItem.Text); args.RawEvent.AcceptedOperation = DataPackageOperation.Link; } } - public async Task HandleItemDroppedAsync(ItemDroppedEventArgs args) { if (args.DropTarget is LocationItem locationItem) @@ -1258,14 +1312,25 @@ private Task HandleDriveItemDroppedAsync(DriveItem driveItem, Item private async Task HandleTagItemDroppedAsync(FileTagItem fileTagItem, ItemDroppedEventArgs args) { var storageItems = await Utils.Storage.FilesystemHelpers.GetDraggedStorageItems(args.DroppedItem); + var dbInstance = FileTagsHelper.GetDbInstance(); + var pathToTags = new Dictionary(); foreach (var item in storageItems.Where(x => !string.IsNullOrEmpty(x.Path))) { - var listedItem = new ListedItem(null) + var filesTags = FileTagsHelper.ReadFileTag(item.Path); + if (!filesTags.Contains(fileTagItem.FileTag.Uid)) { - ItemPath = item.Path, - FileFRN = await FileTagsHelper.GetFileFRN(item.Item), - FileTags = [fileTagItem.FileTag.Uid] - }; + filesTags = [.. filesTags, fileTagItem.FileTag.Uid]; + var fileFRN = await FileTagsHelper.GetFileFRN(item.Item); + dbInstance.SetTags(item.Path, fileFRN, filesTags); + FileTagsHelper.WriteFileTag(item.Path, filesTags); + pathToTags[item.Path] = filesTags; + } + } + + if (paneHolder.ActivePane is not null) + { + await paneHolder.ActivePane.ShellViewModel.UpdateItemsTags(pathToTags); + await paneHolder.ActivePane.ShellViewModel.RefreshTagGroups(); } } diff --git a/src/Files.App/ViewModels/UserControls/StatusCenterViewModel.cs b/src/Files.App/ViewModels/UserControls/StatusCenterViewModel.cs index d1f376a5dcdc..fc8a2e8570a4 100644 --- a/src/Files.App/ViewModels/UserControls/StatusCenterViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/StatusCenterViewModel.cs @@ -1,10 +1,12 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. namespace Files.App.ViewModels.UserControls { - public sealed class StatusCenterViewModel : ObservableObject + public sealed partial class StatusCenterViewModel : ObservableObject { + private readonly Files.App.Storage.TaskbarManager _taskbar = Files.App.Storage.TaskbarManager.Default; + public ObservableCollection StatusCenterItems { get; } = []; private int _AverageOperationProgressValue = 0; @@ -31,11 +33,28 @@ public int InProgressItemCount } public bool HasAnyItemInProgress - => InProgressItemCount > 0; + { + get + { + if (InProgressItemCount > 0) + ShowProgressRing = true; + + return InProgressItemCount > 0; + } + } public bool HasAnyItem => StatusCenterItems.Any(); + + private bool _ShowProgressRing = false; + public bool ShowProgressRing + { + get => _ShowProgressRing; + private set => SetProperty(ref _ShowProgressRing, value); + + } + public int InfoBadgeState { get @@ -64,6 +83,11 @@ public StatusCenterViewModel() StatusCenterItems.CollectionChanged += (s, e) => OnPropertyChanged(nameof(HasAnyItem)); } + public void OnStatusCenterFlyoutOpened() + { + ShowProgressRing = HasAnyItemInProgress || InfoBadgeState == 3; + } + public StatusCenterItem AddItem( string headerResource, string subHeaderResource, @@ -128,12 +152,37 @@ public void NotifyChanges() OnPropertyChanged(nameof(InfoBadgeValue)); UpdateAverageProgressValue(); + UpdateTaskbarProgress(); } public void UpdateAverageProgressValue() { if (HasAnyItemInProgress) AverageOperationProgressValue = (int)StatusCenterItems.Where((item) => item.IsInProgress).Average(x => x.ProgressPercentage); + else + AverageOperationProgressValue = 0; + } + + private void UpdateTaskbarProgress() + { + try + { + var hwnd = new Windows.Win32.Foundation.HWND(MainWindow.Instance.WindowHandle); + + if (HasAnyItemInProgress) + { + _taskbar.SetProgressState(hwnd, Windows.Win32.UI.Shell.TBPFLAG.TBPF_NORMAL); + _taskbar.SetProgressValue(hwnd, (ulong)Math.Clamp(AverageOperationProgressValue, 0, 100), 100); + } + else + { + _taskbar.SetProgressState(hwnd, Windows.Win32.UI.Shell.TBPFLAG.TBPF_NOPROGRESS); + } + } + catch + { + // Ignore taskbar update failures to avoid interrupting status updates. + } } } } diff --git a/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs index cec3cb3398c5..0ed33641858c 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.Helpers.ContextFlyouts; using Microsoft.UI.Xaml; @@ -17,12 +17,14 @@ public abstract class BaseWidgetViewModel : ObservableObject { // Dependency injections + protected IWindowsRecentItemsService WindowsRecentItemsService { get; } = Ioc.Default.GetRequiredService(); protected IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetRequiredService(); protected IQuickAccessService QuickAccessService { get; } = Ioc.Default.GetRequiredService(); protected IStorageService StorageService { get; } = Ioc.Default.GetRequiredService(); protected IHomePageContext HomePageContext { get; } = Ioc.Default.GetRequiredService(); protected IContentPageContext ContentPageContext { get; } = Ioc.Default.GetRequiredService(); protected IFileTagsService FileTagsService { get; } = Ioc.Default.GetRequiredService(); + protected IFileTagsSettingsService FileTagsSettingsService { get; } = Ioc.Default.GetRequiredService(); protected DrivesViewModel DrivesViewModel { get; } = Ioc.Default.GetRequiredService(); protected INetworkService NetworkService { get; } = Ioc.Default.GetRequiredService(); protected ICommandManager CommandManager { get; } = Ioc.Default.GetRequiredService(); @@ -36,8 +38,6 @@ public abstract class BaseWidgetViewModel : ObservableObject protected ICommand RemoveRecentItemCommand { get; set; } = null!; protected ICommand ClearAllItemsCommand { get; set; } = null!; protected ICommand OpenFileLocationCommand { get; set; } = null!; - protected ICommand OpenInNewTabCommand { get; set; } = null!; - protected ICommand OpenInNewWindowCommand { get; set; } = null!; protected ICommand OpenPropertiesCommand { get; set; } = null!; protected ICommand PinToSidebarCommand { get; set; } = null!; protected ICommand UnpinFromSidebarCommand { get; set; } = null!; @@ -65,7 +65,8 @@ widgetCardItem.DataContext is not WidgetCardItem item || // Create a new Flyout var itemContextMenuFlyout = new CommandBarFlyout() { - Placement = FlyoutPlacementMode.Full + Placement = FlyoutPlacementMode.Right, + AlwaysExpanded = true }; // Hook events @@ -78,7 +79,19 @@ widgetCardItem.DataContext is not WidgetCardItem item || // Get items for the flyout var menuItems = GetItemMenuItems(item, QuickAccessService.IsItemPinned(item.Path), fileTagsCardItem is not null && fileTagsCardItem.IsFolder); - var (_, secondaryElements) = ContextFlyoutModelToElementHelper.GetAppBarItemsFromModel(menuItems); + var (primaryElements, secondaryElements) = ContextFlyoutModelToElementHelper.GetAppBarItemsFromModel(menuItems); + + // Workaround for WinUI (#5508) - AppBarButtons don't auto-close CommandBarFlyout + var closeHandler = new RoutedEventHandler((s, e) => itemContextMenuFlyout.Hide()); + primaryElements + .OfType() + .ForEach(button => button.Click += closeHandler); + primaryElements + .OfType() + .ForEach(button => button.Click += closeHandler); + + // Add menu items to the primary flyout + primaryElements.ForEach(itemContextMenuFlyout.PrimaryCommands.Add); // Set max width of the flyout secondaryElements @@ -99,16 +112,6 @@ widgetCardItem.DataContext is not WidgetCardItem item || // Command methods - public async Task ExecuteOpenInNewTabCommand(WidgetCardItem? item) - { - await NavigationHelpers.OpenPathInNewTab(item?.Path ?? string.Empty, false); - } - - public async Task ExecuteOpenInNewWindowCommand(WidgetCardItem? item) - { - await NavigationHelpers.OpenPathInNewWindowAsync(item?.Path ?? string.Empty); - } - public virtual async Task ExecutePinToSidebarCommand(WidgetCardItem? item) { await QuickAccessService.PinToSidebarAsync(item?.Path ?? string.Empty); diff --git a/src/Files.App/ViewModels/UserControls/Widgets/DrivesWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/DrivesWidgetViewModel.cs index 91c6ee42a611..d364f5f9e8e0 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/DrivesWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/DrivesWidgetViewModel.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Input; using Microsoft.UI.Xaml.Controls; @@ -13,24 +13,22 @@ namespace Files.App.ViewModels.UserControls.Widgets /// /// Represents view model of . /// - public sealed class DrivesWidgetViewModel : BaseWidgetViewModel, IWidgetViewModel + public sealed partial class DrivesWidgetViewModel : BaseWidgetViewModel, IWidgetViewModel { // Properties public ObservableCollection Items { get; } = []; public string WidgetName => nameof(DrivesWidget); - public string AutomationProperties => "Drives".GetLocalizedResource(); - public string WidgetHeader => "Drives".GetLocalizedResource(); + public string AutomationProperties => Strings.Drives.GetLocalizedResource(); + public string WidgetHeader => Strings.Drives.GetLocalizedResource(); public bool IsWidgetSettingEnabled => UserSettingsService.GeneralSettingsService.ShowDrivesWidget; public bool ShowMenuFlyout => false; public MenuFlyoutItem? MenuFlyoutItem => null; // Commands - private ICommand FormatDriveCommand { get; } = null!; private ICommand EjectDeviceCommand { get; } = null!; - private ICommand OpenInNewPaneCommand { get; } = null!; private ICommand DisconnectNetworkDriveCommand { get; } = null!; // Constructor @@ -41,19 +39,23 @@ public DrivesWidgetViewModel() DrivesViewModel.Drives.CollectionChanged += Drives_CollectionChanged; - OpenInNewTabCommand = new AsyncRelayCommand(ExecuteOpenInNewTabCommand); - OpenInNewWindowCommand = new AsyncRelayCommand(ExecuteOpenInNewWindowCommand); PinToSidebarCommand = new AsyncRelayCommand(ExecutePinToSidebarCommand); UnpinFromSidebarCommand = new AsyncRelayCommand(ExecuteUnpinFromSidebarCommand); - FormatDriveCommand = new RelayCommand(ExecuteFormatDriveCommand); - EjectDeviceCommand = new AsyncRelayCommand(ExecuteEjectDeviceCommand); - OpenInNewPaneCommand = new AsyncRelayCommand(ExecuteOpenInNewPaneCommand); + EjectDeviceCommand = new RelayCommand(ExecuteEjectDeviceCommand); OpenPropertiesCommand = new RelayCommand(ExecuteOpenPropertiesCommand); DisconnectNetworkDriveCommand = new RelayCommand(ExecuteDisconnectNetworkDriveCommand); + + UserSettingsService.OnSettingChangedEvent += UserSettingsService_OnSettingChangedEvent; } // Methods + private async void UserSettingsService_OnSettingChangedEvent(object? sender, SettingChangedEventArgs e) + { + if (e.SettingName == nameof(UserSettingsService.FoldersSettingsService.SizeUnitFormat)) + await RefreshWidgetAsync(); + } + public async Task RefreshWidgetAsync() { var updateTasks = Items.Select(item => item.Item.UpdatePropertiesAsync()); @@ -68,12 +70,15 @@ public async Task NavigateToPath(string path) var ctrlPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down); if (ctrlPressed) { - await NavigationHelpers.OpenPathInNewTab(path, false); + await NavigationHelpers.OpenPathInNewTab(path); return; } - ContentPageContext.ShellPage!.NavigateWithArguments( - ContentPageContext.ShellPage!.InstanceViewModel.FolderSettings.GetLayoutType(path), + if (ContentPageContext.ShellPage is not IShellPage shellPage) + return; + + shellPage.NavigateWithArguments( + shellPage.InstanceViewModel.FolderSettings.GetLayoutType(path), new() { NavPathParam = path }); } @@ -91,58 +96,79 @@ public override List GetItemMenuItems(WidgetCard return new List() { - new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenInNewTabFromHomeAction).Build(), - new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenInNewWindowFromHomeAction).Build(), - new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenInNewPaneFromHomeAction).Build(), + new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenInNewTabFromHome) + { + IsVisible = UserSettingsService.GeneralSettingsService.ShowOpenInNewTab && CommandManager.OpenInNewTabFromHome.IsExecutable + }.Build(), + new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenInNewWindowFromHome) + { + IsVisible = UserSettingsService.GeneralSettingsService.ShowOpenInNewWindow && CommandManager.OpenInNewWindowFromHome.IsExecutable + }.Build(), + new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenInNewPaneFromHome) + { + IsVisible = UserSettingsService.GeneralSettingsService.ShowOpenInNewPane && CommandManager.OpenInNewPaneFromHome.IsExecutable + }.Build(), + new ContextMenuFlyoutItemViewModelBuilder(CommandManager.CopyItemFromHome) + { + IsPrimary = true, + IsVisible = CommandManager.CopyItemFromHome.IsExecutable + }.Build(), new() { - Text = "PinFolderToSidebar".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() { OpacityIconStyle = "Icons.Pin.16x16" }, + Text = Strings.Properties.GetLocalizedResource(), + ThemedIconModel = new ThemedIconModel() { ThemedIconStyle = "App.ThemedIcons.Properties" }, + Command = OpenPropertiesCommand, + CommandParameter = item, + IsPrimary = true + }, + new() + { + Text = Strings.PinFolderToSidebar.GetLocalizedResource(), + ThemedIconModel = new ThemedIconModel() { ThemedIconStyle = "App.ThemedIcons.FavoritePin" }, Command = PinToSidebarCommand, CommandParameter = item, - ShowItem = !isPinned + ShowItem = !isPinned && UserSettingsService.GeneralSettingsService.ShowPinToSideBar }, new() { - Text = "UnpinFolderFromSidebar".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() { OpacityIconStyle = "Icons.Unpin.16x16" }, + Text = Strings.UnpinFolderFromSidebar.GetLocalizedResource(), + ThemedIconModel = new ThemedIconModel() { ThemedIconStyle = "App.ThemedIcons.FavoritePinRemove" }, Command = UnpinFromSidebarCommand, CommandParameter = item, ShowItem = isPinned }, new() { - Text = "Eject".GetLocalizedResource(), + Text = Strings.Eject.GetLocalizedResource(), Command = EjectDeviceCommand, CommandParameter = item, ShowItem = options?.ShowEjectDevice ?? false }, new() { - Text = "FormatDriveText".GetLocalizedResource(), - Command = FormatDriveCommand, - CommandParameter = item, - ShowItem = options?.ShowFormatDrive ?? false - }, - new() - { - Text = "Properties".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() { OpacityIconStyle = "ColorIconProperties" }, - Command = OpenPropertiesCommand, - CommandParameter = item - }, - new() - { - Text = "TurnOnBitLocker".GetLocalizedResource(), + Text = Strings.TurnOnBitLocker.GetLocalizedResource(), Tag = "TurnOnBitLockerPlaceholder", IsEnabled = false }, new() { - Text = "ManageBitLocker".GetLocalizedResource(), + Text = Strings.ManageBitLocker.GetLocalizedResource(), Tag = "ManageBitLockerPlaceholder", IsEnabled = false }, + new ContextMenuFlyoutItemViewModel() + { + ItemType = ContextMenuFlyoutItemType.Separator, + ShowItem = (UserSettingsService.GeneralSettingsService.ShowOpenTerminal && CommandManager.OpenTerminalFromHome.IsExecutable) || + CommandManager.OpenStorageSenseFromHome.IsExecutable || + CommandManager.FormatDriveFromHome.IsExecutable + }, + new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenTerminalFromHome) + { + IsVisible = UserSettingsService.GeneralSettingsService.ShowOpenTerminal && CommandManager.OpenTerminalFromHome.IsExecutable + }.Build(), + new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenStorageSenseFromHome).Build(), + new ContextMenuFlyoutItemViewModelBuilder(CommandManager.FormatDriveFromHome).Build(), new() { ItemType = ContextMenuFlyoutItemType.Separator, @@ -150,7 +176,7 @@ public override List GetItemMenuItems(WidgetCard }, new() { - Text = "Loading".GetLocalizedResource(), + Text = Strings.Loading.GetLocalizedResource(), Glyph = "\xE712", Items = [], ID = "ItemOverflow", @@ -162,26 +188,12 @@ public override List GetItemMenuItems(WidgetCard // Command methods - private async Task ExecuteEjectDeviceCommand(WidgetDriveCardItem? item) + private void ExecuteEjectDeviceCommand(WidgetDriveCardItem? item) { if (item is null) return; - var result = await DriveHelpers.EjectDeviceAsync(item.Item.Path); - await UIHelpers.ShowDeviceEjectResultAsync(item.Item.Type, result); - } - - private async Task ExecuteOpenInNewPaneCommand(WidgetDriveCardItem? item) - { - if (item is null || await DriveHelpers.CheckEmptyDrive(item.Item.Path)) - return; - - ContentPageContext.ShellPage!.PaneHolder?.OpenSecondaryPane(item.Item.Path); - } - - private void ExecuteFormatDriveCommand(WidgetDriveCardItem? item) - { - Win32Helper.OpenFormatDriveDialog(item?.Path ?? string.Empty); + DriveHelpers.EjectDeviceAsync(item.Item.Path); } private void ExecuteOpenPropertiesCommand(WidgetDriveCardItem? item) diff --git a/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs index 835289ddc3a0..262f88ecc7c7 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs @@ -1,9 +1,8 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Controls; using System.IO; -using System.Windows.Input; using Windows.Storage; namespace Files.App.ViewModels.UserControls.Widgets @@ -13,13 +12,15 @@ namespace Files.App.ViewModels.UserControls.Widgets /// public sealed partial class FileTagsWidgetViewModel : BaseWidgetViewModel, IWidgetViewModel { + private CancellationTokenSource _updateCTS; + // Properties public ObservableCollection Containers { get; } = []; public string WidgetName => nameof(FileTagsWidget); - public string WidgetHeader => "FileTags".GetLocalizedResource(); - public string AutomationProperties => "FileTags".GetLocalizedResource(); + public string WidgetHeader => Strings.FileTags.GetLocalizedResource(); + public string AutomationProperties => Strings.FileTags.GetLocalizedResource(); public bool IsWidgetSettingEnabled => UserSettingsService.GeneralSettingsService.ShowFileTagsWidget; public bool ShowMenuFlyout => false; public MenuFlyoutItem? MenuFlyoutItem => null; @@ -28,9 +29,6 @@ public sealed partial class FileTagsWidgetViewModel : BaseWidgetViewModel, IWidg public static event EventHandler>? SelectedTaggedItemsChanged; - // Commands - - private ICommand OpenInNewPaneCommand { get; set; } = null!; // Constructor @@ -38,12 +36,11 @@ public FileTagsWidgetViewModel() { _ = InitializeWidget(); - OpenInNewTabCommand = new AsyncRelayCommand(ExecuteOpenInNewTabCommand); - OpenInNewWindowCommand = new AsyncRelayCommand(ExecuteOpenInNewWindowCommand); + FileTagsSettingsService.OnTagsUpdated += FileTagsSettingsService_OnTagsUpdated; + PinToSidebarCommand = new AsyncRelayCommand(ExecutePinToSidebarCommand); UnpinFromSidebarCommand = new AsyncRelayCommand(ExecuteUnpinFromSidebarCommand); OpenFileLocationCommand = new RelayCommand(ExecuteOpenFileLocationCommand); - OpenInNewPaneCommand = new RelayCommand(ExecuteOpenInNewPaneCommand); OpenPropertiesCommand = new RelayCommand(ExecuteOpenPropertiesCommand); } @@ -53,39 +50,72 @@ public async Task InitializeWidget() { await foreach (var item in FileTagsService.GetTagsAsync()) { - var container = new WidgetFileTagsContainerItem(item.Uid) - { - Name = item.Name, - Color = item.Color - }; + CreateTagContainerItem(item); + } + } + + public async Task RefreshWidgetAsync() + { + _updateCTS?.Cancel(); + _updateCTS = new CancellationTokenSource(); + await foreach (var item in FileTagsService.GetTagsAsync()) + { + if (_updateCTS.IsCancellationRequested) + break; - Containers.Add(container); - _ = container.InitAsync(); + var matchingItem = Containers.FirstOrDefault(c => c.Uid == item.Uid); + if (matchingItem is null) + { + CreateTagContainerItem(item); + } + else + { + matchingItem.Name = item.Name; + matchingItem.Color = item.Color; + matchingItem.Tags.Clear(); + _ = matchingItem.InitAsync(_updateCTS.Token); + } } } - public Task RefreshWidgetAsync() + private void CreateTagContainerItem(TagViewModel tag) { - return Task.CompletedTask; + var container = new WidgetFileTagsContainerItem(tag.Uid) + { + Name = tag.Name, + Color = tag.Color + }; + + Containers.Add(container); + _ = container.InitAsync(); } public override List GetItemMenuItems(WidgetCardItem item, bool isPinned, bool isFolder = false) { return new List() { - new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenInNewTabFromHomeAction).Build(), - new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenInNewWindowFromHomeAction).Build(), - new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenInNewPaneFromHomeAction).Build(), + new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenInNewTabFromHome) + { + IsVisible = UserSettingsService.GeneralSettingsService.ShowOpenInNewTab && CommandManager.OpenInNewTabFromHome.IsExecutable + }.Build(), + new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenInNewWindowFromHome) + { + IsVisible = UserSettingsService.GeneralSettingsService.ShowOpenInNewWindow && CommandManager.OpenInNewWindowFromHome.IsExecutable + }.Build(), + new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenInNewPaneFromHome) + { + IsVisible = UserSettingsService.GeneralSettingsService.ShowOpenInNewPane && CommandManager.OpenInNewPaneFromHome.IsExecutable + }.Build(), new() { - Text = "OpenWith".GetLocalizedResource(), - OpacityIcon = new() { OpacityIconStyle = "ColorIconOpenWith" }, + Text = Strings.OpenWith.GetLocalizedResource(), + ThemedIconModel = new() { ThemedIconStyle = "App.ThemedIcons.OpenWith" }, Tag = "OpenWithPlaceholder", ShowItem = !isFolder }, new() { - Text = "OpenFileLocation".GetLocalizedResource(), + Text = Strings.OpenFileLocation.GetLocalizedResource(), Glyph = "\uED25", Command = OpenFileLocationCommand, CommandParameter = item, @@ -93,34 +123,43 @@ public override List GetItemMenuItems(WidgetCard }, new() { - Text = "PinFolderToSidebar".GetLocalizedResource(), - OpacityIcon = new() { OpacityIconStyle = "Icons.Pin.16x16" }, + Text = Strings.PinFolderToSidebar.GetLocalizedResource(), + ThemedIconModel = new() { ThemedIconStyle = "App.ThemedIcons.FavoritePin" }, Command = PinToSidebarCommand, CommandParameter = item, - ShowItem = !isPinned && isFolder + ShowItem = !isPinned && isFolder && UserSettingsService.GeneralSettingsService.ShowPinToSideBar }, new() { - Text = "UnpinFolderFromSidebar".GetLocalizedResource(), - OpacityIcon = new() { OpacityIconStyle = "Icons.Unpin.16x16" }, + Text = Strings.UnpinFolderFromSidebar.GetLocalizedResource(), + ThemedIconModel = new() { ThemedIconStyle = "App.ThemedIcons.FavoritePinRemove" }, Command = UnpinFromSidebarCommand, CommandParameter = item, - ShowItem = isPinned && isFolder + ShowItem = isPinned && isFolder && UserSettingsService.GeneralSettingsService.ShowPinToSideBar }, new() { - Text = "SendTo".GetLocalizedResource(), + Text = Strings.SendTo.GetLocalizedResource(), Tag = "SendToPlaceholder", ShowItem = UserSettingsService.GeneralSettingsService.ShowSendToMenu }, new() { - Text = "Properties".GetLocalizedResource(), - OpacityIcon = new() { OpacityIconStyle = "ColorIconProperties" }, + Text = Strings.Properties.GetLocalizedResource(), + ThemedIconModel = new() { ThemedIconStyle = "App.ThemedIcons.Properties" }, Command = OpenPropertiesCommand, CommandParameter = item, ShowItem = isFolder }, + new ContextMenuFlyoutItemViewModel() + { + ItemType = ContextMenuFlyoutItemType.Separator, + ShowItem = UserSettingsService.GeneralSettingsService.ShowOpenTerminal && CommandManager.OpenTerminalFromHome.IsExecutable, + }, + new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenTerminalFromHome) + { + IsVisible = UserSettingsService.GeneralSettingsService.ShowOpenTerminal && CommandManager.OpenTerminalFromHome.IsExecutable + }.Build(), new() { ItemType = ContextMenuFlyoutItemType.Separator, @@ -128,7 +167,7 @@ public override List GetItemMenuItems(WidgetCard }, new() { - Text = "Loading".GetLocalizedResource(), + Text = Strings.Loading.GetLocalizedResource(), Glyph = "\xE712", Items = [], ID = "ItemOverflow", @@ -157,7 +196,7 @@ private void ExecuteOpenPropertiesCommand(WidgetCardItem? item) ItemPath = (item.Item as WidgetFileTagCardItem)?.Path ?? string.Empty, ItemNameRaw = (item.Item as WidgetFileTagCardItem)?.Name ?? string.Empty, PrimaryItemAttribute = StorageItemTypes.Folder, - ItemType = "Folder".GetLocalizedResource(), + ItemType = Strings.Folder.GetLocalizedResource(), }; FilePropertiesHelpers.OpenPropertiesWindow(listedItem, ContentPageContext.ShellPage!); @@ -166,11 +205,6 @@ private void ExecuteOpenPropertiesCommand(WidgetCardItem? item) flyout!.Closed += flyoutClosed; } - private void ExecuteOpenInNewPaneCommand(WidgetCardItem? item) - { - ContentPageContext.ShellPage!.PaneHolder?.OpenSecondaryPane(item?.Path ?? string.Empty); - } - private void ExecuteOpenFileLocationCommand(WidgetCardItem? item) { var itemPath = Directory.GetParent(item?.Path ?? string.Empty)?.FullName ?? string.Empty; @@ -186,10 +220,16 @@ private void ExecuteOpenFileLocationCommand(WidgetCardItem? item) }); } + private async void FileTagsSettingsService_OnTagsUpdated(object? sender, EventArgs e) + { + await RefreshWidgetAsync(); + } + // Disposer public void Dispose() { + FileTagsSettingsService.OnTagsUpdated -= FileTagsSettingsService_OnTagsUpdated; } } } diff --git a/src/Files.App/ViewModels/UserControls/Widgets/NetworkLocationsWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/NetworkLocationsWidgetViewModel.cs index e447497d3463..f8442fb176f1 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/NetworkLocationsWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/NetworkLocationsWidgetViewModel.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Input; using Microsoft.UI.Xaml.Controls; @@ -13,21 +13,21 @@ namespace Files.App.ViewModels.UserControls.Widgets /// /// Represents view model of . /// - public sealed class NetworkLocationsWidgetViewModel : BaseWidgetViewModel, IWidgetViewModel + public sealed partial class NetworkLocationsWidgetViewModel : BaseWidgetViewModel, IWidgetViewModel { // Properties public ObservableCollection Items { get; } = []; public string WidgetName => nameof(NetworkLocationsWidget); - public string AutomationProperties => "NetworkLocations".GetLocalizedResource(); - public string WidgetHeader => "NetworkLocations".GetLocalizedResource(); + public string AutomationProperties => Strings.NetworkLocations.GetLocalizedResource(); + public string WidgetHeader => Strings.NetworkLocations.GetLocalizedResource(); public bool IsWidgetSettingEnabled => UserSettingsService.GeneralSettingsService.ShowNetworkLocationsWidget; public bool ShowMenuFlyout => true; public MenuFlyoutItem? MenuFlyoutItem => new() { Icon = new FontIcon() { Glyph = "\uE710" }, - Text = "DrivesWidgetOptionsFlyoutMapNetDriveMenuItem/Text".GetLocalizedResource(), + Text = Strings.DrivesWidgetOptionsFlyoutMapNetDriveMenuItem_Text.GetLocalizedResource(), Command = MapNetworkDriveCommand }; @@ -40,9 +40,7 @@ public bool IsNoNetworkLocations // Commands - private ICommand FormatDriveCommand { get; } = null!; private ICommand EjectDeviceCommand { get; } = null!; - private ICommand OpenInNewPaneCommand { get; } = null!; private ICommand MapNetworkDriveCommand { get; } = null!; private ICommand DisconnectNetworkDriveCommand { get; } = null!; @@ -56,13 +54,9 @@ public NetworkLocationsWidgetViewModel() DrivesViewModel.Drives.CollectionChanged += Drives_CollectionChanged; NetworkService.Shortcuts.CollectionChanged += Shortcuts_CollectionChanged; - OpenInNewTabCommand = new AsyncRelayCommand(ExecuteOpenInNewTabCommand); - OpenInNewWindowCommand = new AsyncRelayCommand(ExecuteOpenInNewWindowCommand); PinToSidebarCommand = new AsyncRelayCommand(ExecutePinToSidebarCommand); UnpinFromSidebarCommand = new AsyncRelayCommand(ExecuteUnpinFromSidebarCommand); - FormatDriveCommand = new RelayCommand(ExecuteFormatDriveCommand); - EjectDeviceCommand = new AsyncRelayCommand(ExecuteEjectDeviceCommand); - OpenInNewPaneCommand = new AsyncRelayCommand(ExecuteOpenInNewPaneCommand); + EjectDeviceCommand = new RelayCommand(ExecuteEjectDeviceCommand); OpenPropertiesCommand = new RelayCommand(ExecuteOpenPropertiesCommand); DisconnectNetworkDriveCommand = new RelayCommand(ExecuteDisconnectNetworkDriveCommand); MapNetworkDriveCommand = new AsyncRelayCommand(ExecuteMapNetworkDriveCommand); @@ -84,7 +78,7 @@ public async Task NavigateToPath(string path) var ctrlPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down); if (ctrlPressed) { - await NavigationHelpers.OpenPathInNewTab(path, false); + await NavigationHelpers.OpenPathInNewTab(path); return; } @@ -107,75 +101,64 @@ public override List GetItemMenuItems(WidgetCard return new List() { - new() + new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenInNewTabFromHome) { - Text = "OpenInNewTab".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() { OpacityIconStyle = "ColorIconOpenInNewTab" }, - Command = OpenInNewTabCommand, - CommandParameter = item, - ShowItem = UserSettingsService.GeneralSettingsService.ShowOpenInNewTab - }, - new() + IsVisible = UserSettingsService.GeneralSettingsService.ShowOpenInNewTab && CommandManager.OpenInNewTabFromHome.IsExecutable + }.Build(), + new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenInNewWindowFromHome) { - Text = "OpenInNewWindow".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() { OpacityIconStyle = "ColorIconOpenInNewWindow" }, - Command = OpenInNewWindowCommand, - CommandParameter = item, - ShowItem = UserSettingsService.GeneralSettingsService.ShowOpenInNewWindow - }, + IsVisible = UserSettingsService.GeneralSettingsService.ShowOpenInNewWindow && CommandManager.OpenInNewWindowFromHome.IsExecutable + }.Build(), + new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenInNewPaneFromHome) + { + IsVisible = UserSettingsService.GeneralSettingsService.ShowOpenInNewPane && CommandManager.OpenInNewPaneFromHome.IsExecutable + }.Build(), + new ContextMenuFlyoutItemViewModelBuilder(CommandManager.CopyItemFromHome) + { + IsPrimary = true, + IsVisible = CommandManager.CopyItemFromHome.IsExecutable + }.Build(), new() { - Text = "OpenInNewPane".GetLocalizedResource(), - Command = OpenInNewPaneCommand, + Text = Strings.Properties.GetLocalizedResource(), + ThemedIconModel = new ThemedIconModel() { ThemedIconStyle = "App.ThemedIcons.Properties" }, + Command = OpenPropertiesCommand, CommandParameter = item, - ShowItem = UserSettingsService.GeneralSettingsService.ShowOpenInNewPane + IsPrimary = true }, new() { - Text = "PinFolderToSidebar".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() { OpacityIconStyle = "Icons.Pin.16x16" }, + Text = Strings.PinFolderToSidebar.GetLocalizedResource(), + ThemedIconModel = new ThemedIconModel() { ThemedIconStyle = "App.ThemedIcons.FavoritePin" }, Command = PinToSidebarCommand, CommandParameter = item, - ShowItem = !isPinned + ShowItem = !isPinned && UserSettingsService.GeneralSettingsService.ShowPinToSideBar }, new() { - Text = "UnpinFolderFromSidebar".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() { OpacityIconStyle = "Icons.Unpin.16x16" }, + Text = Strings.UnpinFolderFromSidebar.GetLocalizedResource(), + ThemedIconModel = new ThemedIconModel() { ThemedIconStyle = "App.ThemedIcons.FavoritePinRemove" }, Command = UnpinFromSidebarCommand, CommandParameter = item, - ShowItem = isPinned + ShowItem = isPinned && UserSettingsService.GeneralSettingsService.ShowPinToSideBar }, new() { - Text = "Eject".GetLocalizedResource(), + Text = Strings.Eject.GetLocalizedResource(), Command = EjectDeviceCommand, CommandParameter = item, ShowItem = options?.ShowEjectDevice ?? false }, + new ContextMenuFlyoutItemViewModelBuilder(CommandManager.FormatDriveFromHome).Build(), new() { - Text = "FormatDriveText".GetLocalizedResource(), - Command = FormatDriveCommand, - CommandParameter = item, - ShowItem = options?.ShowFormatDrive ?? false - }, - new() - { - Text = "Properties".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() { OpacityIconStyle = "ColorIconProperties" }, - Command = OpenPropertiesCommand, - CommandParameter = item - }, - new() - { - Text = "TurnOnBitLocker".GetLocalizedResource(), + Text = Strings.TurnOnBitLocker.GetLocalizedResource(), Tag = "TurnOnBitLockerPlaceholder", IsEnabled = false }, new() { - Text = "ManageBitLocker".GetLocalizedResource(), + Text = Strings.ManageBitLocker.GetLocalizedResource(), Tag = "ManageBitLockerPlaceholder", IsEnabled = false }, @@ -186,7 +169,7 @@ public override List GetItemMenuItems(WidgetCard }, new() { - Text = "Loading".GetLocalizedResource(), + Text = Strings.Loading.GetLocalizedResource(), Glyph = "\xE712", Items = [], ID = "ItemOverflow", @@ -203,21 +186,12 @@ public void DisableWidget() // Command methods - private async Task ExecuteEjectDeviceCommand(WidgetDriveCardItem? item) + private void ExecuteEjectDeviceCommand(WidgetDriveCardItem? item) { if (item is null) return; - var result = await DriveHelpers.EjectDeviceAsync(item.Item.Path); - await UIHelpers.ShowDeviceEjectResultAsync(item.Item.Type, result); - } - - private async Task ExecuteOpenInNewPaneCommand(WidgetDriveCardItem? item) - { - if (item is null || await DriveHelpers.CheckEmptyDrive(item.Item.Path)) - return; - - ContentPageContext.ShellPage!.PaneHolder?.OpenSecondaryPane(item.Item.Path); + DriveHelpers.EjectDeviceAsync(item.Item.Path); } private Task ExecuteMapNetworkDriveCommand() @@ -255,7 +229,7 @@ private void ExecuteDisconnectNetworkDriveCommand(WidgetDriveCardItem? item) NetworkService.DisconnectNetworkDrive(item.Item); } - private async Task UpdateItems(ObservableCollection source) + private async Task UpdateItems(ObservableCollection source) { await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(async () => { @@ -280,7 +254,7 @@ await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(async () => IsNoNetworkLocations = !Items.Any(); }); - } + } // Event methods diff --git a/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs index a9a94b8b0bf3..195e851df2f9 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs @@ -1,104 +1,153 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Input; using Microsoft.UI.Xaml.Controls; using System.Collections.Specialized; -using System.IO; -using System.Windows.Input; using Windows.Storage; using Windows.System; using Windows.UI.Core; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.System.Com; +using Windows.Win32.System.WinRT; +using Windows.Win32.UI.Shell; namespace Files.App.ViewModels.UserControls.Widgets { /// /// Represents view model of . /// - public sealed class QuickAccessWidgetViewModel : BaseWidgetViewModel, IWidgetViewModel + public sealed partial class QuickAccessWidgetViewModel : BaseWidgetViewModel, IWidgetViewModel { // Properties public ObservableCollection Items { get; } = []; public string WidgetName => nameof(QuickAccessWidget); - public string AutomationProperties => "QuickAccess".GetLocalizedResource(); - public string WidgetHeader => "QuickAccess".GetLocalizedResource(); + public string AutomationProperties => Strings.QuickAccess.GetLocalizedResource(); + public string WidgetHeader => Strings.QuickAccess.GetLocalizedResource(); public bool IsWidgetSettingEnabled => UserSettingsService.GeneralSettingsService.ShowQuickAccessWidget; public bool ShowMenuFlyout => false; public MenuFlyoutItem? MenuFlyoutItem => null; - // Commands + // Fields - public ICommand OpenInNewPaneCommand { get; set; } = null!; + // TODO: Replace with IMutableFolder.GetWatcherAsync() once it gets implemented in IWindowsStorable + private readonly SystemIO.FileSystemWatcher _quickAccessFolderWatcher; // Constructor public QuickAccessWidgetViewModel() { - _ = InitializeWidget(); - Items.CollectionChanged += Items_CollectionChanged; - OpenInNewTabCommand = new AsyncRelayCommand(ExecuteOpenInNewTabCommand); - OpenInNewWindowCommand = new AsyncRelayCommand(ExecuteOpenInNewWindowCommand); - OpenInNewPaneCommand = new RelayCommand(ExecuteOpenInNewPaneCommand); OpenPropertiesCommand = new RelayCommand(ExecuteOpenPropertiesCommand); PinToSidebarCommand = new AsyncRelayCommand(ExecutePinToSidebarCommand); UnpinFromSidebarCommand = new AsyncRelayCommand(ExecuteUnpinFromSidebarCommand); - } - // Methods + _quickAccessFolderWatcher = new() + { + Path = SystemIO.Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Microsoft", "Windows", "Recent", "AutomaticDestinations"), + Filter = "f01b4d95cf55d32a.automaticDestinations-ms", + NotifyFilter = SystemIO.NotifyFilters.LastAccess | SystemIO.NotifyFilters.LastWrite | SystemIO.NotifyFilters.FileName + }; - private async Task InitializeWidget() - { - var itemsToAdd = await QuickAccessService.GetPinnedFoldersAsync(); - ModifyItemAsync(this, new(itemsToAdd.ToArray(), false) { Reset = true }); + _quickAccessFolderWatcher.Changed += async (s, e) => + { + await RefreshWidgetAsync(); + }; - App.QuickAccessManager.UpdateQuickAccessWidget += ModifyItemAsync; + _quickAccessFolderWatcher.EnableRaisingEvents = true; } + // Methods + public Task RefreshWidgetAsync() { - return Task.CompletedTask; + return MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(async () => + { + foreach (var item in Items) + item.Dispose(); + + Items.Clear(); + + await foreach (IWindowsStorable folder in HomePageContext.HomeFolder.GetQuickAccessFolderAsync(default)) + { + folder.GetPropertyValue("System.Home.IsPinned", out var isPinned); + folder.TryGetShellTooltip(out var tooltip); + + Items.Insert( + Items.Count, + new WidgetFolderCardItem( + folder, + folder.GetDisplayName(SIGDN.SIGDN_PARENTRELATIVEFORUI), + isPinned, + tooltip ?? string.Empty)); + } + }); } public override List GetItemMenuItems(WidgetCardItem item, bool isPinned, bool isFolder = false) { return new List() { - new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenInNewTabFromHomeAction).Build(), - new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenInNewWindowFromHomeAction).Build(), - new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenInNewPaneFromHomeAction).Build(), + new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenInNewTabFromHome) + { + IsVisible = UserSettingsService.GeneralSettingsService.ShowOpenInNewTab && CommandManager.OpenInNewTabFromHome.IsExecutable + }.Build(), + new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenInNewWindowFromHome) + { + IsVisible = UserSettingsService.GeneralSettingsService.ShowOpenInNewWindow && CommandManager.OpenInNewWindowFromHome.IsExecutable + }.Build(), + new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenInNewPaneFromHome) + { + IsVisible = UserSettingsService.GeneralSettingsService.ShowOpenInNewPane && CommandManager.OpenInNewPaneFromHome.IsExecutable + }.Build(), + new ContextMenuFlyoutItemViewModelBuilder(CommandManager.CopyItemFromHome) + { + IsPrimary = true, + IsVisible = CommandManager.CopyItemFromHome.IsExecutable + }.Build(), new() { - Text = "PinFolderToSidebar".GetLocalizedResource(), - OpacityIcon = new() { OpacityIconStyle = "Icons.Pin.16x16" }, + Text = Strings.Properties.GetLocalizedResource(), + ThemedIconModel = new() { ThemedIconStyle = "App.ThemedIcons.Properties" }, + Command = OpenPropertiesCommand, + CommandParameter = item, + IsPrimary = true + }, + new() + { + Text = Strings.PinFolderToSidebar.GetLocalizedResource(), + ThemedIconModel = new() { ThemedIconStyle = "App.ThemedIcons.FavoritePin" }, Command = PinToSidebarCommand, CommandParameter = item, - ShowItem = !isPinned + ShowItem = !isPinned && UserSettingsService.GeneralSettingsService.ShowPinToSideBar }, new() { - Text = "UnpinFolderFromSidebar".GetLocalizedResource(), - OpacityIcon = new() { OpacityIconStyle = "Icons.Unpin.16x16" }, + Text = Strings.UnpinFolderFromSidebar.GetLocalizedResource(), + ThemedIconModel = new() { ThemedIconStyle = "App.ThemedIcons.FavoritePinRemove" }, Command = UnpinFromSidebarCommand, CommandParameter = item, - ShowItem = isPinned + ShowItem = isPinned && UserSettingsService.GeneralSettingsService.ShowPinToSideBar }, new() { - Text = "SendTo".GetLocalizedResource(), + Text = Strings.SendTo.GetLocalizedResource(), Tag = "SendToPlaceholder", ShowItem = UserSettingsService.GeneralSettingsService.ShowSendToMenu - }, - new() + }, + new ContextMenuFlyoutItemViewModel() { - Text = "Properties".GetLocalizedResource(), - OpacityIcon = new() { OpacityIconStyle = "ColorIconProperties" }, - Command = OpenPropertiesCommand, - CommandParameter = item + ItemType = ContextMenuFlyoutItemType.Separator, + ShowItem = UserSettingsService.GeneralSettingsService.ShowOpenTerminal && CommandManager.OpenTerminalFromHome.IsExecutable }, + new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenTerminalFromHome) + { + IsVisible = UserSettingsService.GeneralSettingsService.ShowOpenTerminal && CommandManager.OpenTerminalFromHome.IsExecutable + }.Build(), new() { ItemType = ContextMenuFlyoutItemType.Separator, @@ -106,7 +155,7 @@ public override List GetItemMenuItems(WidgetCard }, new() { - Text = "Loading".GetLocalizedResource(), + Text = Strings.Loading.GetLocalizedResource(), Glyph = "\xE712", Items = [], ID = "ItemOverflow", @@ -116,92 +165,12 @@ public override List GetItemMenuItems(WidgetCard }.Where(x => x.ShowItem).ToList(); } - private async void ModifyItemAsync(object? sender, ModifyQuickAccessEventArgs? e) - { - if (e is null) - return; - - await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(async () => - { - if (e.Reset) - { - // Find the intersection between the two lists and determine whether to remove or add - var originalItems = Items.ToList(); - var itemsToRemove = originalItems.Where(x => !e.Paths.Contains(x.Path)); - var itemsToAdd = e.Paths.Where(x => !originalItems.Any(y => y.Path == x)); - - // Remove items - foreach (var itemToRemove in itemsToRemove) - Items.Remove(itemToRemove); - - // Add items - foreach (var itemToAdd in itemsToAdd) - { - var interimItems = Items.ToList(); - var item = await App.QuickAccessManager.Model.CreateLocationItemFromPathAsync(itemToAdd); - var lastIndex = Items.IndexOf(interimItems.FirstOrDefault(x => !x.IsPinned)); - var isPinned = (bool?)e.Items.Where(x => x.FilePath == itemToAdd).FirstOrDefault()?.Properties["System.Home.IsPinned"] ?? false; - if (interimItems.Any(x => x.Path == itemToAdd)) - continue; - - Items.Insert(isPinned && lastIndex >= 0 ? Math.Min(lastIndex, Items.Count) : Items.Count, new WidgetFolderCardItem(item, Path.GetFileName(item.Text), isPinned) - { - Path = item.Path, - }); - } - - return; - } - if (e.Reorder) - { - // Remove pinned items - foreach (var itemToRemove in Items.ToList().Where(x => x.IsPinned)) - Items.Remove(itemToRemove); - - // Add pinned items in the new order - foreach (var itemToAdd in e.Paths) - { - var interimItems = Items.ToList(); - var item = await App.QuickAccessManager.Model.CreateLocationItemFromPathAsync(itemToAdd); - var lastIndex = Items.IndexOf(interimItems.FirstOrDefault(x => !x.IsPinned)); - if (interimItems.Any(x => x.Path == itemToAdd)) - continue; - - Items.Insert(lastIndex >= 0 ? Math.Min(lastIndex, Items.Count) : Items.Count, new WidgetFolderCardItem(item, Path.GetFileName(item.Text), true) - { - Path = item.Path, - }); - } - - return; - } - if (e.Add) - { - foreach (var itemToAdd in e.Paths) - { - var interimItems = Items.ToList(); - var item = await App.QuickAccessManager.Model.CreateLocationItemFromPathAsync(itemToAdd); - var lastIndex = Items.IndexOf(interimItems.FirstOrDefault(x => !x.IsPinned)); - if (interimItems.Any(x => x.Path == itemToAdd)) - continue; - Items.Insert(e.Pin && lastIndex >= 0 ? Math.Min(lastIndex, Items.Count) : Items.Count, new WidgetFolderCardItem(item, Path.GetFileName(item.Text), e.Pin) // Add just after the Recent Folders - { - Path = item.Path, - }); - } - } - else - foreach (var itemToRemove in Items.ToList().Where(x => e.Paths.Contains(x.Path))) - Items.Remove(itemToRemove); - }); - } - public async Task NavigateToPath(string path) { var ctrlPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down); if (ctrlPressed) { - await NavigationHelpers.OpenPathInNewTab(path, false); + await NavigationHelpers.OpenPathInNewTab(path); return; } @@ -225,39 +194,71 @@ private async void Items_CollectionChanged(object? sender, NotifyCollectionChang public override async Task ExecutePinToSidebarCommand(WidgetCardItem? item) { - if (item is null || item.Path is null) + if (item is not WidgetFolderCardItem folderCardItem || folderCardItem.Path is null) return; - await QuickAccessService.PinToSidebarAsync(item.Path); + var lastPinnedItemIndex = Items.LastOrDefault(x => x.IsPinned) is { } lastPinnedItem ? Items.IndexOf(lastPinnedItem) : 0; + var currentPinnedItemIndex = Items.IndexOf(folderCardItem); - ModifyItemAsync(this, new(new[] { item.Path }, false)); + if (currentPinnedItemIndex is -1) + return; - var items = (await QuickAccessService.GetPinnedFoldersAsync()) - .Where(link => !((bool?)link.Properties["System.Home.IsPinned"] ?? false)); + HRESULT hr = default; + using ComPtr pAgileReference = default; - var recentItem = items.Where(x => !Items.ToList().Select(y => y.Path).Contains(x.FilePath)).FirstOrDefault(); - if (recentItem is not null) + unsafe { - ModifyItemAsync(this, new(new[] { recentItem.FilePath }, true) { Pin = false }); + hr = PInvoke.RoGetAgileReference(AgileReferenceOptions.AGILEREFERENCE_DEFAULT, IID.IID_IShellItem, (IUnknown*)folderCardItem.Item.ThisPtr, pAgileReference.GetAddressOf()); } + + // Pin to Quick Access on Windows + hr = await STATask.Run(() => + { + unsafe + { + IShellItem* pShellItem = null; + hr = pAgileReference.Get()->Resolve(IID.IID_IShellItem, (void**)&pShellItem); + using var windowsFile = new WindowsFile(pShellItem); + // NOTE: "pintohome" is an undocumented verb, which calls an undocumented COM class, windows.storage.dll!CPinToFrequentExecute : public IExecuteCommand, ... + return windowsFile.TryInvokeContextMenuVerb("pintohome"); + } + }, App.Logger); + + // The file watcher will update the collection automatically } public override async Task ExecuteUnpinFromSidebarCommand(WidgetCardItem? item) { - if (item is null || item.Path is null) + if (item is not WidgetFolderCardItem folderCardItem || folderCardItem.Path is null) return; - await QuickAccessService.UnpinFromSidebarAsync(item.Path); + HRESULT hr = default; + using ComPtr pAgileReference = default; - ModifyItemAsync(this, new(new[] { item.Path }, false)); - } + unsafe + { + hr = PInvoke.RoGetAgileReference(AgileReferenceOptions.AGILEREFERENCE_DEFAULT, IID.IID_IShellItem, (IUnknown*)folderCardItem.Item.ThisPtr, pAgileReference.GetAddressOf()); + } - private void ExecuteOpenInNewPaneCommand(WidgetFolderCardItem? item) - { - if (item is null || item.Path is null) + // Unpin from Quick Access on Windows + hr = await STATask.Run(() => + { + unsafe + { + IShellItem* pShellItem = null; + hr = pAgileReference.Get()->Resolve(IID.IID_IShellItem, (void**)&pShellItem); + using var windowsFile = new WindowsFile(pShellItem); + + // NOTE: "unpinfromhome" is an undocumented verb, which calls an undocumented COM class, windows.storage.dll!CRemoveFromFrequentPlacesExecute : public IExecuteCommand, ... + // NOTE: "remove" is for some shell folders where the "unpinfromhome" may not work + return windowsFile.TryInvokeContextMenuVerbs(["unpinfromhome", "remove"], true); + } + }, App.Logger); + + if (hr.ThrowIfFailedOnDebug().Failed) return; - ContentPageContext.ShellPage!.PaneHolder?.OpenSecondaryPane(item.Path); + // The file watcher will update the collection automatically } private void ExecuteOpenPropertiesCommand(WidgetFolderCardItem? item) @@ -274,15 +275,15 @@ private void ExecuteOpenPropertiesCommand(WidgetFolderCardItem? item) ListedItem listedItem = new(null!) { - ItemPath = item.Item.Path, - ItemNameRaw = item.Item.Text, + ItemPath = item.Path, + ItemNameRaw = item.Text, PrimaryItemAttribute = StorageItemTypes.Folder, - ItemType = "Folder".GetLocalizedResource(), + ItemType = Strings.Folder.GetLocalizedResource(), }; - if (!string.Equals(item.Item.Path, Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.OrdinalIgnoreCase)) + if (!string.Equals(item.Path, Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.OrdinalIgnoreCase)) { - BaseStorageFolder matchingStorageFolder = await ContentPageContext.ShellPage!.ShellViewModel.GetFolderFromPathAsync(item.Item.Path); + BaseStorageFolder matchingStorageFolder = await ContentPageContext.ShellPage!.ShellViewModel.GetFolderFromPathAsync(item.Path); if (matchingStorageFolder is not null) { var syncStatus = await ContentPageContext.ShellPage!.ShellViewModel.CheckCloudDriveSyncStatusAsync(matchingStorageFolder); @@ -300,7 +301,8 @@ private void ExecuteOpenPropertiesCommand(WidgetFolderCardItem? item) public void Dispose() { - App.QuickAccessManager.UpdateQuickAccessWidget -= ModifyItemAsync; + foreach (var item in Items) + item.Dispose(); } } } diff --git a/src/Files.App/ViewModels/UserControls/Widgets/RecentFilesWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/RecentFilesWidgetViewModel.cs index d2cee3e2e0a4..4d7b7bc8e2d3 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/RecentFilesWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/RecentFilesWidgetViewModel.cs @@ -1,8 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.Extensions.Logging; using Microsoft.UI.Xaml.Controls; +using Microsoft.Win32; using System.Collections.Specialized; using System.IO; using Windows.Foundation.Metadata; @@ -12,7 +13,7 @@ namespace Files.App.ViewModels.UserControls.Widgets /// /// Represents view model of . /// - public sealed class RecentFilesWidgetViewModel : BaseWidgetViewModel, IWidgetViewModel + public sealed partial class RecentFilesWidgetViewModel : BaseWidgetViewModel, IWidgetViewModel { // Fields @@ -24,8 +25,8 @@ public sealed class RecentFilesWidgetViewModel : BaseWidgetViewModel, IWidgetVie public ObservableCollection Items { get; } = []; public string WidgetName => nameof(RecentFilesWidget); - public string AutomationProperties => "RecentFiles".GetLocalizedResource(); - public string WidgetHeader => "RecentFiles".GetLocalizedResource(); + public string AutomationProperties => Strings.RecentFiles.GetLocalizedResource(); + public string WidgetHeader => Strings.RecentFiles.GetLocalizedResource(); public bool IsWidgetSettingEnabled => UserSettingsService.GeneralSettingsService.ShowRecentFilesWidget; public bool ShowMenuFlyout => false; public MenuFlyoutItem? MenuFlyoutItem => null; @@ -54,7 +55,7 @@ public RecentFilesWidgetViewModel() // recent files could have changed while widget wasn't loaded _ = RefreshWidgetAsync(); - App.RecentItemsManager.RecentFilesChanged += Manager_RecentFilesChanged; + WindowsRecentItemsService.RecentFilesChanged += Manager_RecentFilesChanged; RemoveRecentItemCommand = new AsyncRelayCommand(ExecuteRemoveRecentItemCommand); ClearAllItemsCommand = new AsyncRelayCommand(ExecuteClearRecentItemsCommand); @@ -66,61 +67,68 @@ public RecentFilesWidgetViewModel() public async Task RefreshWidgetAsync() { - IsRecentFilesDisabledInWindows = App.RecentItemsManager.CheckIsRecentFilesEnabled() is false; - await App.RecentItemsManager.UpdateRecentFilesAsync(); + IsRecentFilesDisabledInWindows = !CheckIsRecentItemsEnabled(); + await WindowsRecentItemsService.UpdateRecentFilesAsync(); } + public override List GetItemMenuItems(WidgetCardItem item, bool isPinned, bool isFolder = false) { return new List() { + new ContextMenuFlyoutItemViewModelBuilder(CommandManager.CopyItemFromHome) + { + IsPrimary = true, + IsVisible = CommandManager.CopyItemFromHome.IsExecutable + }.Build(), + new() + { + Text = Strings.Properties.GetLocalizedResource(), + ThemedIconModel = new() { ThemedIconStyle = "App.ThemedIcons.Properties" }, + Command = OpenPropertiesCommand, + CommandParameter = item, + IsPrimary = true + }, new() { - Text = "OpenWith".GetLocalizedResource(), - OpacityIcon = new() { OpacityIconStyle = "ColorIconOpenWith" }, + Text = Strings.OpenWith.GetLocalizedResource(), + ThemedIconModel = new() { ThemedIconStyle = "App.ThemedIcons.OpenWith" }, Tag = "OpenWithPlaceholder", }, new() { - Text = "RecentItemRemove/Text".GetLocalizedResource(), + Text = Strings.RecentItemRemove_Text.GetLocalizedResource(), Glyph = "\uE738", Command = RemoveRecentItemCommand, CommandParameter = item }, new() { - Text = "RecentItemClearAll/Text".GetLocalizedResource(), + Text = Strings.RecentItemClearAll_Text.GetLocalizedResource(), Glyph = "\uE74D", Command = ClearAllItemsCommand }, new() { - Text = "OpenFileLocation".GetLocalizedResource(), + Text = Strings.OpenFileLocation.GetLocalizedResource(), Glyph = "\uED25", Command = OpenFileLocationCommand, CommandParameter = item }, new() { - Text = "SendTo".GetLocalizedResource(), + Text = Strings.SendTo.GetLocalizedResource(), Tag = "SendToPlaceholder", ShowItem = UserSettingsService.GeneralSettingsService.ShowSendToMenu }, new() - { - Text = "Properties".GetLocalizedResource(), - OpacityIcon = new() { OpacityIconStyle = "ColorIconProperties" }, - Command = OpenPropertiesCommand, - CommandParameter = item - }, - new() { ItemType = ContextMenuFlyoutItemType.Separator, Tag = "OverflowSeparator", }, new() { - Text = "Loading".GetLocalizedResource(), + Text = Strings.Loading.GetLocalizedResource(), Glyph = "\xE712", Items = [], ID = "ItemOverflow", @@ -178,7 +186,7 @@ private async Task UpdateRecentFilesListAsync(NotifyCollectionChangedEventArgs e // case NotifyCollectionChangedAction.Reset: default: - var recentFiles = App.RecentItemsManager.RecentFiles; // already sorted, add all in order + var recentFiles = WindowsRecentItemsService.RecentFiles; // already sorted, add all in order if (!recentFiles.SequenceEqual(Items)) { Items.Clear(); @@ -232,6 +240,22 @@ public void NavigateToPath(string path) catch (Exception) { } } + public bool CheckIsRecentItemsEnabled() + { + using var explorerSubKey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer"); + using var advSubkey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced"); + using var userPolicySubkey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer"); + using var sysPolicySubkey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer"); + var policySubkey = userPolicySubkey ?? sysPolicySubkey; + + if (Convert.ToBoolean(explorerSubKey?.GetValue("ShowRecent", true)) && + Convert.ToBoolean(advSubkey?.GetValue("Start_TrackDocs", true)) && + !Convert.ToBoolean(policySubkey?.GetValue("NoRecentDocsHistory", false))) + return true; + + return false; + } + // Event methods private async void Manager_RecentFilesChanged(object? sender, NotifyCollectionChangedEventArgs e) @@ -254,7 +278,10 @@ private async Task ExecuteRemoveRecentItemCommand(RecentItem? item) try { - await App.RecentItemsManager.UnpinFromRecentFiles(item); + await Task.Run(() => + { + return WindowsRecentItemsService.Remove(item); + }); } finally { @@ -265,13 +292,10 @@ private async Task ExecuteRemoveRecentItemCommand(RecentItem? item) private async Task ExecuteClearRecentItemsCommand() { await _refreshRecentFilesSemaphore.WaitAsync(); + try { - Items.Clear(); - bool success = App.RecentItemsManager.ClearRecentItems(); - - if (success) - IsEmptyRecentFilesTextVisible = true; + WindowsRecentItemsService.Clear(); } finally { @@ -284,8 +308,8 @@ private void ExecuteOpenFileLocationCommand(RecentItem? item) if (item is null) return; - var itemPath = Directory.GetParent(item.RecentPath)?.FullName ?? string.Empty; - var itemName = Path.GetFileName(item.RecentPath); + var itemPath = Directory.GetParent(item.Path)?.FullName ?? string.Empty; + var itemName = Path.GetFileName(item.Path); ContentPageContext.ShellPage!.NavigateWithArguments( ContentPageContext.ShellPage!.InstanceViewModel.FolderSettings.GetLayoutType(itemPath), @@ -314,8 +338,8 @@ private void ExecuteOpenPropertiesCommand(RecentItem? item) { ContentDialog dialog = new() { - Title = "CannotAccessPropertiesTitle".GetLocalizedResource(), - Content = "CannotAccessPropertiesContent".GetLocalizedResource(), + Title = Strings.CannotAccessPropertiesTitle.GetLocalizedResource(), + Content = Strings.CannotAccessPropertiesContent.GetLocalizedResource(), PrimaryButtonText = "Ok".GetLocalizedResource() }; @@ -338,7 +362,10 @@ private void ExecuteOpenPropertiesCommand(RecentItem? item) public void Dispose() { - App.RecentItemsManager.RecentFilesChanged -= Manager_RecentFilesChanged; + WindowsRecentItemsService.RecentFilesChanged -= Manager_RecentFilesChanged; + + foreach (var item in Items) + item.Dispose(); } } } diff --git a/src/Files.App/Views/HomePage.xaml b/src/Files.App/Views/HomePage.xaml index b70eae7c9a71..878ffb41c945 100644 --- a/src/Files.App/Views/HomePage.xaml +++ b/src/Files.App/Views/HomePage.xaml @@ -1,4 +1,4 @@ - + - - - + + + @@ -422,7 +422,40 @@ - + + + + + + + + + + + + + + + + + + + + @@ -514,6 +547,10 @@ + + + + diff --git a/src/Files.App/Views/HomePage.xaml.cs b/src/Files.App/Views/HomePage.xaml.cs index f77b0d8b7fa3..80da0462deb0 100644 --- a/src/Files.App/Views/HomePage.xaml.cs +++ b/src/Files.App/Views/HomePage.xaml.cs @@ -1,7 +1,9 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Input; using Microsoft.UI.Xaml.Navigation; namespace Files.App.Views @@ -10,6 +12,7 @@ public sealed partial class HomePage : Page, IDisposable { // Dependency injections + private readonly ICommandManager Commands = Ioc.Default.GetRequiredService(); public HomeViewModel ViewModel { get; } = Ioc.Default.GetRequiredService(); // Properties @@ -42,16 +45,16 @@ protected override async void OnNavigatedTo(NavigationEventArgs e) AppInstance.InstanceViewModel.IsPageTypeLibrary = false; AppInstance.InstanceViewModel.GitRepositoryPath = null; AppInstance.InstanceViewModel.IsGitRepository = false; + AppInstance.InstanceViewModel.IsPageTypeReleaseNotes = false; + AppInstance.InstanceViewModel.IsPageTypeSettings = false; AppInstance.ToolbarViewModel.CanRefresh = true; AppInstance.ToolbarViewModel.CanGoBack = AppInstance.CanNavigateBackward; AppInstance.ToolbarViewModel.CanGoForward = AppInstance.CanNavigateForward; AppInstance.ToolbarViewModel.CanNavigateToParent = false; - AppInstance.ToolbarViewModel.RefreshRequested -= ToolbarViewModel_RefreshRequested; - AppInstance.ToolbarViewModel.RefreshRequested += ToolbarViewModel_RefreshRequested; - // Set path of working directory empty await AppInstance.ShellViewModel.SetWorkingDirectoryAsync("Home"); + AppInstance.ShellViewModel.CheckForBackgroundImage(); AppInstance.SlimContentPage?.StatusBarViewModel.UpdateGitInfo(false, string.Empty, null); @@ -59,7 +62,7 @@ protected override async void OnNavigatedTo(NavigationEventArgs e) string componentLabel = parameters?.NavPathParam == "Home" - ? "Home".GetLocalizedResource() + ? Strings.Home.GetLocalizedResource() : parameters?.NavPathParam ?? string.Empty; @@ -69,6 +72,7 @@ protected override async void OnNavigatedTo(NavigationEventArgs e) { Title = componentLabel, Path = tag, + ChevronToolTip = string.Format(Strings.BreadcrumbBarChevronButtonToolTip.GetLocalizedResource(), componentLabel), }; AppInstance.ToolbarViewModel.PathComponents.Add(item); @@ -76,25 +80,23 @@ protected override async void OnNavigatedTo(NavigationEventArgs e) base.OnNavigatedTo(e); } - protected override void OnNavigatedFrom(NavigationEventArgs e) + private void ScrollViewer_RightTapped(object sender, RightTappedRoutedEventArgs e) { - Dispose(); + if (sender is FrameworkElement element) + HomePageContextMenu.ShowAt(element, e.GetPosition(element)); + + e.Handled = true; } - private async void ToolbarViewModel_RefreshRequested(object? sender, EventArgs e) + protected override void OnNavigatedFrom(NavigationEventArgs e) { - AppInstance.ToolbarViewModel.CanRefresh = false; - - await Task.WhenAll(ViewModel.WidgetItems.Select(w => w.WidgetItemModel.RefreshWidgetAsync())); - - AppInstance.ToolbarViewModel.CanRefresh = true; + Dispose(); } // Disposer public void Dispose() { - AppInstance.ToolbarViewModel.RefreshRequested -= ToolbarViewModel_RefreshRequested; ViewModel?.Dispose(); } } diff --git a/src/Files.App/Views/Layouts/BaseGroupableLayoutPage.cs b/src/Files.App/Views/Layouts/BaseGroupableLayoutPage.cs index 009315e3f747..62de8527bb8e 100644 --- a/src/Files.App/Views/Layouts/BaseGroupableLayoutPage.cs +++ b/src/Files.App/Views/Layouts/BaseGroupableLayoutPage.cs @@ -1,7 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using CommunityToolkit.WinUI.UI; +using CommunityToolkit.WinUI; using Files.App.ViewModels.Layouts; using Microsoft.UI.Input; using Microsoft.UI.Xaml; @@ -138,7 +138,7 @@ protected virtual async Task ReloadSelectedItemIconAsync() await ParentShellPageInstance.ShellViewModel.LoadExtendedItemPropertiesAsync(ParentShellPageInstance.SlimContentPage.SelectedItem); if (ParentShellPageInstance.ShellViewModel.EnabledGitProperties is not GitProperties.None && - ParentShellPageInstance.SlimContentPage.SelectedItem is GitItem gitItem) + ParentShellPageInstance.SlimContentPage.SelectedItem is IGitItem gitItem) { await ParentShellPageInstance.ShellViewModel.LoadGitPropertiesAsync(gitItem); } @@ -161,7 +161,7 @@ protected virtual async Task ReloadSelectedItemsIconAsync() { await Task.WhenAll(ParentShellPageInstance.SlimContentPage.SelectedItems.Select(item => { - if (item is GitItem gitItem) + if (item is IGitItem gitItem) return ParentShellPageInstance.ShellViewModel.LoadGitPropertiesAsync(gitItem); return Task.CompletedTask; @@ -171,10 +171,20 @@ await Task.WhenAll(ParentShellPageInstance.SlimContentPage.SelectedItems.Select( protected virtual void ItemManipulationModel_FocusFileListInvoked(object? sender, EventArgs e) { - var focusedElement = (FrameworkElement)FocusManager.GetFocusedElement(MainWindow.Instance.Content.XamlRoot); - var isFileListFocused = DependencyObjectHelpers.FindParent(focusedElement) == ItemsControl; - if (!isFileListFocused) - ListViewBase.Focus(FocusState.Programmatic); + try + { + if (App.AppModel.IsMainWindowClosed) + return; + + var focusedElement = (FrameworkElement)FocusManager.GetFocusedElement(MainWindow.Instance.Content.XamlRoot); + var isFileListFocused = DependencyObjectHelpers.FindParent(focusedElement) == ItemsControl; + if (!isFileListFocused) + ListViewBase.Focus(FocusState.Programmatic); + } + catch + { + // Handle exception in case the window is closed during the operation + } } protected virtual void ItemManipulationModel_SelectAllItemsInvoked(object? sender, EventArgs e) @@ -289,7 +299,7 @@ protected virtual async void RenameTextBox_LostFocus(object sender, RoutedEventA catch (COMException) { - } + } } // Methods @@ -312,6 +322,11 @@ protected async void RenameTextBox_KeyDown(object sender, KeyRoutedEventArgs e) await CommitRenameAsync(textBox); e.Handled = true; break; + case VirtualKey.Home: + textBox.SelectionStart = 0; + textBox.SelectionLength = 0; + e.Handled = true; + break; case VirtualKey.Up: if (!isShiftPressed) textBox.SelectionStart = 0; diff --git a/src/Files.App/Views/Layouts/BaseLayoutPage.cs b/src/Files.App/Views/Layouts/BaseLayoutPage.cs index 9ca1d77258b1..31ca131deece 100644 --- a/src/Files.App/Views/Layouts/BaseLayoutPage.cs +++ b/src/Files.App/Views/Layouts/BaseLayoutPage.cs @@ -1,7 +1,8 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using CommunityToolkit.WinUI.UI; +using CommunityToolkit.WinUI; +using Files.App.Controls; using Files.App.Helpers.ContextFlyouts; using Files.App.UserControls.Menus; using Files.App.ViewModels.Layouts; @@ -14,6 +15,7 @@ using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices.ComTypes; +using Vanara.Extensions; using Vanara.PInvoke; using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer.DragDrop; @@ -37,8 +39,11 @@ public abstract class BaseLayoutPage : Page, IBaseLayoutPage, INotifyPropertyCha protected IFileTagsSettingsService FileTagsSettingsService { get; } = Ioc.Default.GetService()!; protected IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetService()!; + protected ILayoutSettingsService LayoutSettingsService { get; } = Ioc.Default.GetService()!; protected ICommandManager Commands { get; } = Ioc.Default.GetRequiredService(); public InfoPaneViewModel InfoPaneViewModel { get; } = Ioc.Default.GetRequiredService(); + protected readonly IWindowContext WindowContext = Ioc.Default.GetRequiredService(); + protected readonly IStorageTrashBinService StorageTrashBinService = Ioc.Default.GetRequiredService(); // ViewModels @@ -63,6 +68,7 @@ public abstract class BaseLayoutPage : Page, IBaseLayoutPage, INotifyPropertyCha private CancellationTokenSource? groupingCancellationToken; private bool shiftPressed; + private bool itemDragging; private ListedItem? dragOverItem = null; private ListedItem? hoveredItem = null; @@ -70,8 +76,8 @@ public abstract class BaseLayoutPage : Page, IBaseLayoutPage, INotifyPropertyCha // Properties - protected AddressToolbar? NavToolbar - => (MainWindow.Instance.Content as Frame)?.FindDescendant(); + protected NavigationToolbar? NavToolbar + => (MainWindow.Instance.Content as Frame)?.FindDescendant(); public LayoutPreferencesManager? FolderSettings => ParentShellPageInstance?.InstanceViewModel.FolderSettings; @@ -82,10 +88,8 @@ public CurrentInstanceViewModel? InstanceViewModel public static AppModel AppModel => App.AppModel; - // NOTE: Dragging makes the app crash when run as admin. (#12390) - // For more information, visit https://github.com/microsoft/terminal/issues/12017#issuecomment-1004129669 public bool AllowItemDrag - => !ElevationHelpers.IsAppRunAsAdmin(); + => WindowContext.CanDragAndDrop; public CommandBarFlyout ItemContextMenuFlyout { get; set; } = new() { @@ -216,14 +220,31 @@ public string JumpString } } + private bool isSelectedItemsSorted = false; private List? selectedItems = []; public List? SelectedItems { - get => selectedItems; + get + { + if (!isSelectedItemsSorted) + { + var orderedItems = SortingHelper.OrderFileList(selectedItems, FolderSettings.DirectorySortOption, FolderSettings.DirectorySortDirection, FolderSettings.SortDirectoriesAlongsideFiles, FolderSettings.SortFilesFirst).ToList(); + selectedItems = orderedItems; + isSelectedItemsSorted = true; + } + + return SelectedItem is null || !selectedItems!.Contains(SelectedItem) + ? selectedItems + : selectedItems + .SkipWhile(x => x != SelectedItem) + .Concat(selectedItems.TakeWhile(x => x != SelectedItem)) + .ToList(); + } internal set { if (value != selectedItems) { + isSelectedItemsSorted = false; selectedItems = value; if (selectedItems?.Count == 0 || selectedItems?[0] is null) @@ -244,7 +265,7 @@ internal set UpdateSelectionSize(); SelectedItemsPropertiesViewModel.SelectedItemsCount = selectedItems.Count; - SelectedItemsPropertiesViewModel.SelectedItemsCountString = "SelectedItems".GetLocalizedFormatResource(selectedItems!.Count); + SelectedItemsPropertiesViewModel.SelectedItemsCountString = Strings.SelectedItems.GetLocalizedFormatResource(selectedItems!.Count); if (selectedItems.Count == 1) { @@ -261,7 +282,6 @@ internal set NotifyPropertyChanged(nameof(SelectedItems)); } - ParentShellPageInstance!.ToolbarViewModel.SelectedItems = value; } } @@ -368,6 +388,12 @@ protected virtual void BaseFolderSettings_LayoutModeChangeRequested(object? send } ParentShellPageInstance.ShellViewModel.UpdateEmptyTextType(); + + // Focus on the active pane in case it was lost during the layout switch. + // Allthough the focus is also set from SetSelectedItemsOnNavigation, + // that is only called when switching between a Grid based layout and Details, + // not between different Grid based layouts (eg. List and Cards). + ParentShellPageInstance!.PaneHolder.FocusActivePane(); } } @@ -421,6 +447,8 @@ protected override async void OnNavigatedTo(NavigationEventArgs e) ParentShellPageInstance.InstanceViewModel.IsPageTypeZipFolder = ZipStorageFolder.IsZipPath(workingDir); ParentShellPageInstance.InstanceViewModel.IsPageTypeLibrary = LibraryManager.IsLibraryPath(workingDir); ParentShellPageInstance.InstanceViewModel.IsPageTypeSearchResults = false; + ParentShellPageInstance.InstanceViewModel.IsPageTypeReleaseNotes = false; + ParentShellPageInstance.InstanceViewModel.IsPageTypeSettings = false; ParentShellPageInstance.ToolbarViewModel.PathControlDisplayText = navigationArguments.NavPathParam; if (ParentShellPageInstance.InstanceViewModel.FolderSettings.DirectorySortOption == SortOption.Path) @@ -454,16 +482,17 @@ protected override async void OnNavigatedTo(NavigationEventArgs e) ParentShellPageInstance.InstanceViewModel.IsPageTypeZipFolder = ZipStorageFolder.IsZipPath(workingDir); ParentShellPageInstance.InstanceViewModel.IsPageTypeLibrary = LibraryManager.IsLibraryPath(workingDir); ParentShellPageInstance.InstanceViewModel.IsPageTypeSearchResults = true; + ParentShellPageInstance.InstanceViewModel.IsPageTypeReleaseNotes = false; + ParentShellPageInstance.InstanceViewModel.IsPageTypeSettings = false; if (!navigationArguments.IsLayoutSwitch) { var displayName = App.LibraryManager.TryGetLibrary(navigationArguments.SearchPathParam, out var lib) ? lib.Text : navigationArguments.SearchPathParam; - await ParentShellPageInstance.UpdatePathUIToWorkingDirectoryAsync(null, string.Format("SearchPagePathBoxOverrideText".GetLocalizedResource(), navigationArguments.SearchQuery, displayName)); + await ParentShellPageInstance.UpdatePathUIToWorkingDirectoryAsync(null, string.Format(Strings.SearchPagePathBoxOverrideText.GetLocalizedResource(), navigationArguments.SearchQuery, displayName)); var searchInstance = new Utils.Storage.FolderSearch { Query = navigationArguments.SearchQuery, Folder = navigationArguments.SearchPathParam, - ThumbnailSize = InstanceViewModel!.FolderSettings.GetRoundedIconSize(), }; _ = ParentShellPageInstance.ShellViewModel.SearchAsync(searchInstance); @@ -483,7 +512,7 @@ protected override async void OnNavigatedTo(NavigationEventArgs e) BaseContextMenuFlyout.Opening += BaseContextFlyout_Opening; } - public void SetSelectedItemsOnNavigation() + public async void SetSelectedItemsOnNavigation() { try { @@ -499,10 +528,14 @@ navigationArguments.SelectItems is not null && ItemManipulationModel.SetSelectedItems(listedItemsToSelect); ItemManipulationModel.FocusSelectedItems(); } - else if (navigationArguments is not null && navigationArguments.FocusOnNavigation) + else if (navigationArguments is not null && ParentShellPageInstance!.InstanceViewModel.FolderSettings.LayoutMode is not FolderLayoutModes.ColumnView) { - // Set focus on layout specific file list control - ItemManipulationModel.FocusFileList(); + // Delay to ensure the new layout is loaded + if (navigationArguments.IsLayoutSwitch) + await Task.Delay(100); + + // Focus on the active pane in case it was lost during navigation + ParentShellPageInstance!.PaneHolder.FocusActivePane(); } } catch (Exception) { } @@ -685,7 +718,7 @@ public void UpdateSelectionSize() var isSizeKnown = !items.Any(item => string.IsNullOrEmpty(item.FileSize)); if (isSizeKnown) { - long size = items.Sum(item => item.FileSizeBytes); + decimal size = items.Sum(item => item.FileSizeBytes); SelectedItemsPropertiesViewModel.ItemSizeBytes = size; SelectedItemsPropertiesViewModel.ItemSize = size.ToSizeString(); } @@ -738,13 +771,28 @@ private void AddNewFileTagsToMenu(CommandBarFlyout contextMenu) contextMenu.SecondaryCommands.Insert(index, new AppBarSeparator()); contextMenu.SecondaryCommands.Insert(index + 1, new AppBarButton() { - Label = "EditTags".GetLocalizedResource(), - Content = new OpacityIcon() + Label = Strings.EditTags.GetLocalizedResource(), + Content = new ThemedIcon() { - Style = (Style)Application.Current.Resources["ColorIconTag"], + Style = (Style)Application.Current.Resources["App.ThemedIcons.TagEdit"], }, Flyout = fileTagsContextMenu }); + + fileTagsContextMenu.TagsChanged += RequireTagGroupsUpdate; + fileTagsContextMenu.Closed += HandleClosed; + + async void RequireTagGroupsUpdate(object? sender, EventArgs e) + { + if (ParentShellPageInstance is not null) + await ParentShellPageInstance.ShellViewModel.RefreshTagGroups(); + } + + void HandleClosed(object? sender, object e) + { + fileTagsContextMenu.TagsChanged -= RequireTagGroupsUpdate; + fileTagsContextMenu.Closed -= HandleClosed; + } } private async Task AddShellMenuItemsAsync(List shellMenuItems, CommandBarFlyout contextMenuFlyout, bool shiftPressed) @@ -824,7 +872,7 @@ private async Task AddShellMenuItemsAsync(List s if (overflowItemFlyout.Items.Count > 0 && UserSettingsService.GeneralSettingsService.MoveShellExtensionsToSubMenu) { - overflowItem.Label = "ShowMoreOptions".GetLocalizedResource(); + overflowItem.Label = Strings.ShowMoreOptions.GetLocalizedResource(); overflowItem.IsEnabled = true; } else @@ -865,11 +913,11 @@ private async Task AddShellMenuItemsAsync(List s openWithOverflow.Visibility = Visibility.Visible; // TODO delete this when https://github.com/microsoft/microsoft-ui-xaml/issues/9409 is resolved - openWithOverflow.Content = new OpacityIconModel() + openWithOverflow.Content = new ThemedIconModel() { - OpacityIconStyle = "ColorIconOpenWith" - }.ToOpacityIcon(); - openWithOverflow.Label = "OpenWith".GetLocalizedResource(); + ThemedIconStyle = "App.ThemedIcons.OpenWith" + }.ToThemedIcon(); + openWithOverflow.Label = Strings.OpenWith.GetLocalizedResource(); } } @@ -902,7 +950,7 @@ private async Task AddShellMenuItemsAsync(List s // Filter mainShellMenuItems that have a non-null LoadSubMenuAction var mainItemsWithSubMenu = mainShellMenuItems.Where(x => x.LoadSubMenuAction is not null); - + var mainSubMenuTasks = mainItemsWithSubMenu.Select(async item => { await item.LoadSubMenuAction(); @@ -917,7 +965,7 @@ private async Task AddShellMenuItemsAsync(List s await item.LoadSubMenuAction(); ShellContextFlyoutFactory.AddItemsToOverflowMenu(overflowItem, item); }); - + itemsControl?.Items.OfType().ForEach(item => { // Enable CharacterEllipsis text trimming for menu items @@ -943,7 +991,7 @@ private async Task AddShellMenuItemsAsync(List s clickAction(flyout.Items); } }); - + await Task.WhenAll(mainSubMenuTasks.Concat(overflowSubMenuTasks)); } @@ -971,7 +1019,12 @@ protected virtual void FileList_DragItemsStarting(object sender, DragItemsStarti { try { - var shellItemList = SafetyExtensions.IgnoreExceptions(() => e.Items.OfType().Select(x => new VanaraWindowsShell.ShellItem(x.ItemPath)).ToArray()); + var itemList = e.Items.OfType().ToList(); + var firstItem = itemList.FirstOrDefault(); + var sortedItems = SortingHelper.OrderFileList(itemList, FolderSettings.DirectorySortOption, FolderSettings.DirectorySortDirection, FolderSettings.SortDirectoriesAlongsideFiles, FolderSettings.SortFilesFirst).ToList(); + var orderedItems = sortedItems.SkipWhile(x => x != firstItem).Concat(sortedItems.TakeWhile(x => x != firstItem)).ToList(); + + var shellItemList = SafetyExtensions.IgnoreExceptions(() => orderedItems.Select(x => new VanaraWindowsShell.ShellItem(x.ItemPath)).ToArray()); if (shellItemList?[0].FileSystemPath is not null && !InstanceViewModel.IsPageTypeSearchResults) { var iddo = shellItemList[0].Parent.GetChildrenUIObjects(HWND.NULL, shellItemList); @@ -987,9 +1040,13 @@ protected virtual void FileList_DragItemsStarting(object sender, DragItemsStarti else { // Only support IStorageItem capable paths - var storageItemList = e.Items.OfType().Where(x => !(x.IsHiddenItem && x.IsLinkItem && x.IsRecycleBinItem && x.IsShortcut)).Select(x => VirtualStorageItem.FromListedItem(x)); + var storageItemList = orderedItems.Where(x => !(x.IsHiddenItem && x.IsLinkItem && x.IsRecycleBinItem && x.IsShortcut)).Select(x => VirtualStorageItem.FromListedItem(x)); e.Data.SetStorageItems(storageItemList, false); } + + // Set can window to front (#13255) + MainWindow.Instance.SetCanWindowToFront(false); + itemDragging = true; } catch (Exception) { @@ -997,6 +1054,13 @@ protected virtual void FileList_DragItemsStarting(object sender, DragItemsStarti } } + protected virtual void FileList_DragItemsCompleted(ListViewBase sender, DragItemsCompletedEventArgs args) + { + // Set can window to front (#13255) + itemDragging = false; + MainWindow.Instance.SetCanWindowToFront(true); + } + private void Item_DragLeave(object sender, DragEventArgs e) { var item = GetItemFromElement(sender); @@ -1038,41 +1102,41 @@ private async void Item_DragOver(object sender, DragEventArgs e) if (item.IsExecutable || item.IsScriptFile) { - e.DragUIOverride.Caption = $"{"OpenWith".GetLocalizedResource()} {item.Name}"; + e.DragUIOverride.Caption = $"{Strings.OpenWith.GetLocalizedResource()} {item.Name}"; e.AcceptedOperation = DataPackageOperation.Link; } // Items from the same drive as this folder are dragged into this folder, so we move the items instead of copy else if (e.Modifiers.HasFlag(DragDropModifiers.Alt) || e.Modifiers.HasFlag(DragDropModifiers.Control | DragDropModifiers.Shift)) { - e.DragUIOverride.Caption = string.Format("LinkToFolderCaptionText".GetLocalizedResource(), item.Name); + e.DragUIOverride.Caption = string.Format(Strings.LinkToFolderCaptionText.GetLocalizedResource(), item.Name); e.AcceptedOperation = DataPackageOperation.Link; } else if (e.Modifiers.HasFlag(DragDropModifiers.Control)) { - e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), item.Name); + e.DragUIOverride.Caption = string.Format(Strings.CopyToFolderCaptionText.GetLocalizedResource(), item.Name); e.AcceptedOperation = DataPackageOperation.Copy; } else if (e.Modifiers.HasFlag(DragDropModifiers.Shift)) { - e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalizedResource(), item.Name); + e.DragUIOverride.Caption = string.Format(Strings.MoveToFolderCaptionText.GetLocalizedResource(), item.Name); // Some applications such as Edge can't raise the drop event by the Move flag (#14008), so we set the Copy flag as well. e.AcceptedOperation = DataPackageOperation.Move | DataPackageOperation.Copy; } else if (draggedItems.Any(x => x.Item is ZipStorageFile || x.Item is ZipStorageFolder) || ZipStorageFolder.IsZipPath(item.ItemPath)) { - e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), item.Name); + e.DragUIOverride.Caption = string.Format(Strings.CopyToFolderCaptionText.GetLocalizedResource(), item.Name); e.AcceptedOperation = DataPackageOperation.Copy; } else if (draggedItems.AreItemsInSameDrive(item.ItemPath)) { - e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalizedResource(), item.Name); + e.DragUIOverride.Caption = string.Format(Strings.MoveToFolderCaptionText.GetLocalizedResource(), item.Name); // Some applications such as Edge can't raise the drop event by the Move flag (#14008), so we set the Copy flag as well. e.AcceptedOperation = DataPackageOperation.Move | DataPackageOperation.Copy; } else { - e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), item.Name); + e.DragUIOverride.Caption = string.Format(Strings.CopyToFolderCaptionText.GetLocalizedResource(), item.Name); e.AcceptedOperation = DataPackageOperation.Copy; } } @@ -1095,7 +1159,7 @@ private async void Item_DragOver(object sender, DragEventArgs e) Commands.OpenItem.ExecuteAsync(); } }, - TimeSpan.FromMilliseconds(1000), false); + TimeSpan.FromMilliseconds(Constants.DragAndDrop.HoverToOpenTimespan), false); } } } @@ -1108,15 +1172,24 @@ private async void Item_DragOver(object sender, DragEventArgs e) protected virtual async void Item_Drop(object sender, DragEventArgs e) { var deferral = e.GetDeferral(); - e.Handled = true; + try + { + _ = e.Data.Properties; + var exists = e.Data.Properties.TryGetValue("Files_ActionBinder", out var val); + _ = val; + } + catch (NullReferenceException) + { + // e.Data or e.Data.Properties is null, continue without the property check + } + // Reset dragged over item dragOverItem = null; - var item = GetItemFromElement(sender); if (item is not null) - await ParentShellPageInstance!.FilesystemHelpers.PerformOperationTypeAsync(e.AcceptedOperation, e.DataView, (item as ShortcutItem)?.TargetPath ?? item.ItemPath, false, true, item.IsExecutable, item.IsScriptFile); + await ParentShellPageInstance!.FilesystemHelpers.PerformOperationTypeAsync(e.AcceptedOperation, e.DataView, (item as IShortcutItem)?.TargetPath ?? item.ItemPath, false, true, item.IsExecutable, item.IsScriptFile); deferral.Complete(); } @@ -1125,6 +1198,10 @@ protected void FileList_ContainerContentChanging(ListViewBase sender, ContainerC { RefreshContainer(args.ItemContainer, args.InRecycleQueue); RefreshItem(args.ItemContainer, args.Item, args.InRecycleQueue, args); + + // Set can window to front (#13255) + itemDragging = false; + MainWindow.Instance.SetCanWindowToFront(true); } private void RefreshContainer(SelectorItem container, bool inRecycleQueue) @@ -1132,6 +1209,8 @@ private void RefreshContainer(SelectorItem container, bool inRecycleQueue) container.PointerPressed -= FileListItem_PointerPressed; container.PointerEntered -= FileListItem_PointerEntered; container.PointerExited -= FileListItem_PointerExited; + container.Tapped -= FileListItem_Tapped; + container.DoubleTapped -= FileListItem_DoubleTapped; container.RightTapped -= FileListItem_RightTapped; if (inRecycleQueue) @@ -1141,12 +1220,11 @@ private void RefreshContainer(SelectorItem container, bool inRecycleQueue) else { container.PointerPressed += FileListItem_PointerPressed; + container.PointerEntered += FileListItem_PointerEntered; + container.PointerExited += FileListItem_PointerExited; + container.Tapped += FileListItem_Tapped; + container.DoubleTapped += FileListItem_DoubleTapped; container.RightTapped += FileListItem_RightTapped; - if (UserSettingsService.FoldersSettingsService.SelectFilesOnHover) - { - container.PointerEntered += FileListItem_PointerEntered; - container.PointerExited += FileListItem_PointerExited; - } } } @@ -1163,32 +1241,53 @@ private void RefreshItem(SelectorItem container, object item, bool inRecycleQueu { InitializeDrag(container, listedItem); + if (listedItem.PreloadedIconData is not null && listedItem.FileImage is null) + _ = ApplyPreloadedIconAsync(listedItem); + if (!listedItem.ItemPropertiesInitialized) { uint callbackPhase = 3; args.RegisterUpdateCallback(callbackPhase, async (s, c) => { await ParentShellPageInstance!.ShellViewModel.LoadExtendedItemPropertiesAsync(listedItem); - if (ParentShellPageInstance.ShellViewModel.EnabledGitProperties is not GitProperties.None && listedItem is GitItem gitItem) + if (ParentShellPageInstance.ShellViewModel.EnabledGitProperties is not GitProperties.None && listedItem is IGitItem gitItem) await ParentShellPageInstance.ShellViewModel.LoadGitPropertiesAsync(gitItem); }); } } } - protected static void FileListItem_PointerPressed(object sender, PointerRoutedEventArgs e) + private static async Task ApplyPreloadedIconAsync(ListedItem item) + { + var image = await item.PreloadedIconData.ToBitmapAsync(); + if (image is not null) + item.FileImage = image; + } + + protected internal void FileListItem_PointerPressed(object sender, PointerRoutedEventArgs e) { + // Set can window to front and bring the window to the front if necessary (#13255) + if ((!itemDragging) && MainWindow.Instance.SetCanWindowToFront(true)) + Win32Helper.BringToForegroundEx(new(MainWindow.Instance.WindowHandle)); + if (sender is not SelectorItem selectorItem) return; - if (selectorItem.IsSelected && e.KeyModifiers == VirtualKeyModifiers.Control) + if (selectorItem.IsSelected) { - selectorItem.IsSelected = false; + if (e.KeyModifiers == VirtualKeyModifiers.Control) + { + selectorItem.IsSelected = false; - // Prevent issues arising caused by the default handlers attempting to select the item that has just been deselected by ctrl + click - e.Handled = true; + // Prevent issues arising caused by the default handlers attempting to select the item that has just been deselected by ctrl + click + e.Handled = true; + } + else + { + SelectedItem = GetItemFromElement(sender); + } } - else if (!selectorItem.IsSelected && e.GetCurrentPoint(selectorItem).Properties.IsLeftButtonPressed) + else if (e.GetCurrentPoint(selectorItem).Properties.IsLeftButtonPressed) { selectorItem.IsSelected = true; } @@ -1196,6 +1295,10 @@ protected static void FileListItem_PointerPressed(object sender, PointerRoutedEv protected internal void FileListItem_PointerEntered(object sender, PointerRoutedEventArgs e) { + // Set can window to front (#13255) + if (sender is SelectorItem selectorItem && selectorItem.IsSelected) + MainWindow.Instance.SetCanWindowToFront(false); + if (!UserSettingsService.FoldersSettingsService.SelectFilesOnHover) return; @@ -1242,6 +1345,10 @@ selectedItems is not null && protected internal void FileListItem_PointerExited(object sender, PointerRoutedEventArgs e) { + // Set can window to front (#13255) + if (!itemDragging) + MainWindow.Instance.SetCanWindowToFront(true); + if (!UserSettingsService.FoldersSettingsService.SelectFilesOnHover) return; @@ -1249,8 +1356,26 @@ protected internal void FileListItem_PointerExited(object sender, PointerRoutedE hoveredItem = null; } + protected void FileListItem_Tapped(object sender, TappedRoutedEventArgs e) + { + // Set can window to front and bring the window to the front if necessary (#13255) + if ((!itemDragging) && MainWindow.Instance.SetCanWindowToFront(true)) + Win32Helper.BringToForegroundEx(new(MainWindow.Instance.WindowHandle)); + } + + protected void FileListItem_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e) + { + // Set can window to front and bring the window to the front if necessary (#13255) + if ((!itemDragging) && MainWindow.Instance.SetCanWindowToFront(true)) + Win32Helper.BringToForegroundEx(new(MainWindow.Instance.WindowHandle)); + } + protected void FileListItem_RightTapped(object sender, RightTappedRoutedEventArgs e) { + // Set can window to front and bring the window to the front if necessary (#13255) + if ((!itemDragging) && MainWindow.Instance.SetCanWindowToFront(true)) + Win32Helper.BringToForegroundEx(new(MainWindow.Instance.WindowHandle)); + var rightClickedItem = GetItemFromElement(sender); if (rightClickedItem is not null && !((SelectorItem)sender).IsSelected) @@ -1263,7 +1388,7 @@ protected void InitializeDrag(UIElement container, ListedItem item) return; UninitializeDrag(container); - if ((item.PrimaryItemAttribute == StorageItemTypes.Folder && !RecycleBinHelpers.IsPathUnderRecycleBin(item.ItemPath)) + if ((item.PrimaryItemAttribute == StorageItemTypes.Folder && !StorageTrashBinService.IsUnderTrashBin(item.ItemPath)) || item.IsExecutable || item.IsScriptFile) { @@ -1284,7 +1409,6 @@ protected void UninitializeDrag(UIElement element) public virtual void Dispose() { - InfoPaneViewModel?.Dispose(); UnhookBaseEvents(); } diff --git a/src/Files.App/Views/Layouts/ColumnLayoutPage.xaml b/src/Files.App/Views/Layouts/ColumnLayoutPage.xaml index 680b0800822f..067dd267ca46 100644 --- a/src/Files.App/Views/Layouts/ColumnLayoutPage.xaml +++ b/src/Files.App/Views/Layouts/ColumnLayoutPage.xaml @@ -1,19 +1,22 @@ - + @@ -82,6 +85,7 @@ @@ -215,7 +219,7 @@ Loaded="Grid_Loaded" PointerEntered="Grid_PointerEntered"> - + @@ -223,14 +227,13 @@ + Tag="IconBox"> - @@ -263,7 +266,10 @@ HorizontalAlignment="Left" VerticalAlignment="Bottom" Visibility="{x:Bind IsShortcut}"> - + - + Style="{x:Bind ((cloud:CloudDriveSyncStatusUI)SyncStatusUI).ThemedIconStyle, Mode=OneWay}" /> @@ -355,7 +361,7 @@ diff --git a/src/Files.App/Views/Layouts/ColumnLayoutPage.xaml.cs b/src/Files.App/Views/Layouts/ColumnLayoutPage.xaml.cs index c5f55acfb441..8693039bf05d 100644 --- a/src/Files.App/Views/Layouts/ColumnLayoutPage.xaml.cs +++ b/src/Files.App/Views/Layouts/ColumnLayoutPage.xaml.cs @@ -1,7 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using CommunityToolkit.WinUI.UI; +using CommunityToolkit.WinUI; using Files.App.UserControls.Selection; using Microsoft.UI.Dispatching; using Microsoft.UI.Input; @@ -50,6 +50,20 @@ public int RowHeight get => LayoutSizeKindHelper.GetColumnsViewRowHeight(UserSettingsService.LayoutSettingsService.ColumnsViewSize); } + /// + /// Icon Box size layout. The value is increased by 4px to account for icon overlays. + /// + public int IconBoxSize + { + get => (int)(LayoutSizeKindHelper.GetIconSize(FolderLayoutModes.ColumnView) + 4); + } + + /// + /// This reference is used to prevent unnecessary icon reloading by only reloading icons when their + /// size changes, even if the layout size changes (since some layout sizes share the same icon size). + /// + private uint currentIconSize; + // Constructor public ColumnLayoutPage() : base() @@ -66,9 +80,20 @@ public ColumnLayoutPage() : base() // Methods + private void OnItemLoadStatusChanged(object sender, ItemLoadStatusChangedEventArgs args) + { + if (args.Status is ItemLoadStatusChangedEventArgs.ItemLoadStatus.Complete) + { + var currentBladeIndex = (ParentShellPageInstance is ColumnShellPage associatedColumnShellPage) ? associatedColumnShellPage.ColumnParams?.Column ?? 0 : 0; + this.FindAscendant()?.SetWidth(currentBladeIndex); + } + } + + private void FileList_Loaded(object sender, RoutedEventArgs e) { ContentScroller = FileList.FindDescendant(x => x.Name == "ScrollViewer"); + ParentShellPageInstance.ShellViewModel.ItemLoadStatusChanged += OnItemLoadStatusChanged; } private void ColumnViewBase_GotFocus(object sender, RoutedEventArgs e) @@ -149,6 +174,8 @@ protected override void OnNavigatedTo(NavigationEventArgs eventArgs) base.OnNavigatedTo(eventArgs); + currentIconSize = LayoutSizeKindHelper.GetIconSize(FolderLayoutModes.ColumnView); + UserSettingsService.LayoutSettingsService.PropertyChanged += LayoutSettingsService_PropertyChanged; SetItemContainerStyle(); @@ -170,6 +197,7 @@ protected override void OnNavigatingFrom(NavigatingCancelEventArgs e) { base.OnNavigatingFrom(e); UserSettingsService.LayoutSettingsService.PropertyChanged -= LayoutSettingsService_PropertyChanged; + ParentShellPageInstance.ShellViewModel.ItemLoadStatusChanged -= OnItemLoadStatusChanged; } private void LayoutSettingsService_PropertyChanged(object? sender, PropertyChangedEventArgs e) @@ -180,12 +208,36 @@ private void LayoutSettingsService_PropertyChanged(object? sender, PropertyChang var previousOffset = ContentScroller?.VerticalOffset; NotifyPropertyChanged(nameof(RowHeight)); + NotifyPropertyChanged(nameof(IconBoxSize)); // Update the container style to match the item size SetItemContainerStyle(); // Restore correct scroll position ContentScroller?.ChangeView(null, previousOffset, null); + + // Check if icons need to be reloaded + var newIconSize = LayoutSizeKindHelper.GetIconSize(FolderLayoutModes.ColumnView); + if (newIconSize != currentIconSize) + { + currentIconSize = newIconSize; + _ = ReloadItemIconsAsync(); + } + } + } + + private async Task ReloadItemIconsAsync() + { + if (ParentShellPageInstance is null) + return; + + ParentShellPageInstance.ShellViewModel.CancelExtendedPropertiesLoading(); + var filesAndFolders = ParentShellPageInstance.ShellViewModel.FilesAndFolders.ToList(); + foreach (ListedItem listedItem in filesAndFolders) + { + listedItem.ItemPropertiesInitialized = false; + if (FileList.ContainerFromItem(listedItem) is not null) + await ParentShellPageInstance.ShellViewModel.LoadExtendedItemPropertiesAsync(listedItem); } } @@ -297,8 +349,9 @@ protected override void FileList_SelectionChanged(object sender, SelectionChange return; // Open the selected folder if selected through tap - if (UserSettingsService.FoldersSettingsService.ColumnLayoutOpenFoldersWithOneClick && !isDraggingSelectionRectangle) - ItemInvoked?.Invoke(new ColumnParam { Source = this, NavPathParam = (SelectedItem is ShortcutItem sht ? sht.TargetPath : SelectedItem.ItemPath), ListView = FileList }, EventArgs.Empty); + if ((UserSettingsService.FoldersSettingsService.OpenFoldersWithOneClick == OpenFoldersWithOneClickEnum.Always || + UserSettingsService.FoldersSettingsService.OpenFoldersWithOneClick == OpenFoldersWithOneClickEnum.OnlyInColumnsView) && + !isDraggingSelectionRectangle) ItemInvoked?.Invoke(new ColumnParam { Source = this, NavPathParam = (SelectedItem is IShortcutItem sht ? sht.TargetPath : SelectedItem.ItemPath), ListView = FileList }, EventArgs.Empty); else CloseFolder(); } @@ -352,7 +405,7 @@ ParentShellPageInstance is null || e.Handled = true; if (IsItemSelected && SelectedItem?.PrimaryItemAttribute == StorageItemTypes.Folder) - ItemInvoked?.Invoke(new ColumnParam { Source = this, NavPathParam = (SelectedItem is ShortcutItem sht ? sht.TargetPath : SelectedItem.ItemPath), ListView = FileList }, EventArgs.Empty); + ItemInvoked?.Invoke(new ColumnParam { Source = this, NavPathParam = (SelectedItem is IShortcutItem sht ? sht.TargetPath : SelectedItem.ItemPath), ListView = FileList }, EventArgs.Empty); } else if (e.Key == VirtualKey.Enter && e.KeyStatus.IsMenuKeyDown) { @@ -361,8 +414,7 @@ ParentShellPageInstance is null || } else if (e.Key == VirtualKey.Space) { - if (!ParentShellPageInstance.ToolbarViewModel.IsEditModeEnabled) - e.Handled = true; + e.Handled = true; } else if (e.KeyStatus.IsMenuKeyDown && (e.Key == VirtualKey.Left || e.Key == VirtualKey.Right || e.Key == VirtualKey.Up)) { @@ -387,9 +439,6 @@ ParentShellPageInstance is null || } else if (e.Key == VirtualKey.Left) // Left arrow: select parent folder (previous column) { - if (ParentShellPageInstance is not null && ParentShellPageInstance.ToolbarViewModel.IsEditModeEnabled) - return; - var currentBladeIndex = (ParentShellPageInstance is ColumnShellPage associatedColumnShellPage) ? associatedColumnShellPage.ColumnParams.Column : 0; this.FindAscendant()?.MoveFocusToPreviousBlade(currentBladeIndex); FileList.SelectedItem = null; @@ -398,9 +447,6 @@ ParentShellPageInstance is null || } else if (e.Key == VirtualKey.Right) // Right arrow: switch focus to next column { - if (ParentShellPageInstance is not null && ParentShellPageInstance.ToolbarViewModel.IsEditModeEnabled) - return; - var currentBladeIndex = (ParentShellPageInstance is ColumnShellPage associatedColumnShellPage) ? associatedColumnShellPage.ColumnParams.Column : 0; this.FindAscendant()?.MoveFocusToNextBlade(currentBladeIndex + 1); e.Handled = true; @@ -422,8 +468,8 @@ private async void FileList_DoubleTapped(object sender, DoubleTappedRoutedEventA await Commands.OpenItem.ExecuteAsync(); break; case StorageItemTypes.Folder: - if (!UserSettingsService.FoldersSettingsService.ColumnLayoutOpenFoldersWithOneClick) - ItemInvoked?.Invoke(new ColumnParam { Source = this, NavPathParam = (item is ShortcutItem sht ? sht.TargetPath : item.ItemPath), ListView = FileList }, EventArgs.Empty); + if (UserSettingsService.FoldersSettingsService.OpenFoldersWithOneClick is not OpenFoldersWithOneClickEnum.Always and not OpenFoldersWithOneClickEnum.OnlyInColumnsView) + ItemInvoked?.Invoke(new ColumnParam { Source = this, NavPathParam = (item is IShortcutItem sht ? sht.TargetPath : item.ItemPath), ListView = FileList }, EventArgs.Empty); break; default: if (UserSettingsService.FoldersSettingsService.DoubleClickToGoUp) @@ -493,6 +539,11 @@ private async void FileList_ItemTapped(object sender, TappedRoutedEventArgs e) else { CloseFolder(); + + // Clear selection when clicking empty area via touch + // https://github.com/files-community/Files/issues/15051 + if (e.PointerDeviceType == PointerDeviceType.Touch) + ItemManipulationModel.ClearSelection(); } } @@ -547,8 +598,8 @@ protected override void BaseFolderSettings_LayoutModeChangeRequested(object? sen case FolderLayoutModes.ListView: parent.FolderSettings.ToggleLayoutModeList(true); break; - case FolderLayoutModes.TilesView: - parent.FolderSettings.ToggleLayoutModeTiles(true); + case FolderLayoutModes.CardsView: + parent.FolderSettings.ToggleLayoutModeCards(true); break; case FolderLayoutModes.GridView: parent.FolderSettings.ToggleLayoutModeGridView(true); @@ -566,7 +617,7 @@ protected override void SelectionRectangle_SelectionEnded(object? sender, EventA if (SelectedItems?.Count is 1 && SelectedItem is not null && SelectedItem.PrimaryItemAttribute is StorageItemTypes.Folder) - ItemInvoked?.Invoke(new ColumnParam { Source = this, NavPathParam = (SelectedItem is ShortcutItem sht ? sht.TargetPath : SelectedItem.ItemPath), ListView = FileList }, EventArgs.Empty); + ItemInvoked?.Invoke(new ColumnParam { Source = this, NavPathParam = (SelectedItem is IShortcutItem sht ? sht.TargetPath : SelectedItem.ItemPath), ListView = FileList }, EventArgs.Empty); base.SelectionRectangle_SelectionEnded(sender, e); } diff --git a/src/Files.App/Views/Layouts/ColumnsLayoutPage.xaml b/src/Files.App/Views/Layouts/ColumnsLayoutPage.xaml index fb4e78e1325d..75cfee8be929 100644 --- a/src/Files.App/Views/Layouts/ColumnsLayoutPage.xaml +++ b/src/Files.App/Views/Layouts/ColumnsLayoutPage.xaml @@ -1,12 +1,11 @@ - + - - - + + + diff --git a/src/Files.App/Views/Layouts/ColumnsLayoutPage.xaml.cs b/src/Files.App/Views/Layouts/ColumnsLayoutPage.xaml.cs index 9bbf70e47c57..ea1989230cda 100644 --- a/src/Files.App/Views/Layouts/ColumnsLayoutPage.xaml.cs +++ b/src/Files.App/Views/Layouts/ColumnsLayoutPage.xaml.cs @@ -1,8 +1,8 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using CommunityToolkit.WinUI.UI; -using CommunityToolkit.WinUI.UI.Controls; +using CommunityToolkit.WinUI; +using Files.App.Controls; using Files.App.ViewModels.Layouts; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -81,6 +81,18 @@ private void ColumnViewBase_ItemInvoked(object? sender, EventArgs e) } } + public void SetWidth(int index) + { + var activeBlades = ColumnHost.ActiveBlades; + if (index < 0 || activeBlades is null || index >= activeBlades.Count) + return; + + var blade = activeBlades[index]; + blade?.SetWidth(); + + ColumnHost.ScrollToEnd(); + } + private void ContentChanged(IShellPage p) { (ParentShellPageInstance as ModernShellPage)?.RaiseContentChanged(p, p.TabBarItemParameter); @@ -152,9 +164,10 @@ public override void Dispose() { base.Dispose(); - var columnHostItems = ColumnHost.Items.OfType().Select(blade => blade.Content as Frame); + var columnHostItems = ColumnHost.Items.OfType().Select(blade => blade.Content as Frame).ToList(); foreach (var frame in columnHostItems) { + // Unsubscribe all event handlers BEFORE disposing to prevent race conditions if (frame?.Content is ColumnShellPage shPage) { shPage.ContentChanged -= ColumnViewBrowser_ContentChanged; @@ -169,6 +182,7 @@ public override void Dispose() if (frame?.Content is UIElement element) element.GotFocus -= ColumnViewBrowser_GotFocus; + // Dispose content AFTER unsubscribing all event handlers if (frame?.Content is IDisposable disposable) disposable.Dispose(); } @@ -197,9 +211,7 @@ public void DismissOtherBlades(int index) { var frame = ColumnHost.ActiveBlades[index + 1].Content as Frame; - if (frame?.Content is IDisposable disposableContent) - disposableContent.Dispose(); - + // Unsubscribe event handlers BEFORE disposing to prevent race conditions if ((frame?.Content as ColumnShellPage)?.SlimContentPage is ColumnLayoutPage columnLayout) { columnLayout.ItemInvoked -= ColumnViewBase_ItemInvoked; @@ -210,6 +222,10 @@ public void DismissOtherBlades(int index) (frame?.Content as UIElement).GotFocus -= ColumnViewBrowser_GotFocus; (frame?.Content as ColumnShellPage).ContentChanged -= ColumnViewBrowser_ContentChanged; + // Dispose content AFTER unsubscribing event handlers + if (frame?.Content is IDisposable disposableContent) + disposableContent.Dispose(); + ColumnHost.Items.RemoveAt(index + 1); ColumnHost.ActiveBlades.RemoveAt(index + 1); } @@ -351,6 +367,7 @@ public void SetSelectedPathOrNavigate(string navigationPath, Type sourcePageType { if (navArgs is not null && navArgs.IsSearchResultPage) { + navArgs.AssociatedTabInstance = ParentShellPageInstance; ParentShellPageInstance?.NavigateToPath(navArgs.SearchPathParam, typeof(DetailsLayoutPage), navArgs); return; } diff --git a/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml b/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml index 1943c7b9cd42..8f5557a9b628 100644 --- a/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml +++ b/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml @@ -1,34 +1,36 @@ - + - - - - - - + + + + + + @@ -53,7 +55,7 @@ + + + + + @@ -769,7 +1011,7 @@ x:Name="FileList" Padding="8" VerticalContentAlignment="Stretch" - tui:ScrollViewerExtensions.EnableMiddleClickScrolling="{x:Bind IsMiddleClickToScrollEnabled, Mode=OneWay}" + ext:ScrollViewerMiddleClickExtensions.EnableMiddleClickScrolling="{x:Bind IsMiddleClickToScrollEnabled, Mode=OneWay}" wctanimations:ItemsReorderAnimation.Duration="0:0:0.350" x:FieldModifier="public" AllowDrop="{x:Bind InstanceViewModel.IsPageTypeSearchResults, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}" @@ -777,6 +1019,7 @@ ContainerContentChanging="FileList_ContainerContentChanging" DoubleTapped="FileList_DoubleTapped" DragEnter="ItemsLayout_DragEnter" + DragItemsCompleted="FileList_DragItemsCompleted" DragItemsStarting="FileList_DragItemsStarting" DragLeave="ItemsLayout_DragLeave" DragOver="ItemsLayout_DragOver" @@ -801,6 +1044,7 @@ diff --git a/src/Files.App/Views/Layouts/GridLayoutPage.xaml.cs b/src/Files.App/Views/Layouts/GridLayoutPage.xaml.cs index ba0c6a3c7633..920e93469b12 100644 --- a/src/Files.App/Views/Layouts/GridLayoutPage.xaml.cs +++ b/src/Files.App/Views/Layouts/GridLayoutPage.xaml.cs @@ -1,7 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using CommunityToolkit.WinUI.UI; +using CommunityToolkit.WinUI; using Files.App.UserControls.Selection; using Microsoft.UI.Input; using Microsoft.UI.Xaml; @@ -23,7 +23,12 @@ public sealed partial class GridLayoutPage : BaseGroupableLayoutPage { // Fields + /// + /// This reference is used to prevent unnecessary icon reloading by only reloading icons when their + /// size changes, even if the layout size changes (since some layout sizes share the same icon size). + /// private uint currentIconSize; + private volatile bool shouldSetVerticalScrollMode; // Properties @@ -34,42 +39,108 @@ public sealed partial class GridLayoutPage : BaseGroupableLayoutPage protected override SemanticZoom RootZoom => RootGridZoom; + // List View properties + /// /// Row height in the List View layout /// - public int RowHeightListView + public int RowHeightListView => + LayoutSizeKindHelper.GetListViewRowHeight(LayoutSettingsService.ListViewSize); + + /// + /// Icon Box size in the List View layout. The value is increased by 4px to account for icon overlays. + /// + public int IconBoxSizeListView => + (int)(LayoutSizeKindHelper.GetIconSize(FolderLayoutModes.ListView) + 4); + + + + // Grid View properties + + /// + /// Item width in the Grid View layout + /// + public int ItemWidthGridView => + LayoutSizeKindHelper.GetGridViewItemWidth(LayoutSettingsService.GridViewSize); + + + + // Cards View properties + + /// + /// Gets the details box width for the Cards View layout based on the card size. + /// + public int CardsViewDetailsBoxWidth => LayoutSettingsService.CardsViewSize switch { - get => LayoutSizeKindHelper.GetListViewRowHeight(UserSettingsService.LayoutSettingsService.ListViewSize); - } + CardsViewSizeKind.Small => 196, + CardsViewSizeKind.Medium => 240, + CardsViewSizeKind.Large => 280, + CardsViewSizeKind.ExtraLarge => 320, + _ => 300 + }; /// - /// Item width in the Tiles View layout + /// Gets the details box height for the Cards View layout based on the card size. /// - public int ItemWidthTilesView + public int CardsViewDetailsBoxHeight => LayoutSettingsService.CardsViewSize switch { - get => LayoutSizeKindHelper.GetTilesViewItemWidth(UserSettingsService.LayoutSettingsService.TilesViewSize); - } + CardsViewSizeKind.Small => 104, + CardsViewSizeKind.Medium => 144, + CardsViewSizeKind.Large => 144, + CardsViewSizeKind.ExtraLarge => 128, + _ => 128 + }; /// - /// Item width in the Grid View layout + /// Gets the icon box height for the Cards View layout based on the card size. /// - public int ItemWidthGridView + public int CardsViewIconBoxHeight => LayoutSettingsService.CardsViewSize switch { - get => LayoutSizeKindHelper.GetGridViewItemWidth(UserSettingsService.LayoutSettingsService.GridViewSize); - } + CardsViewSizeKind.Small => 104, + CardsViewSizeKind.Medium => 96, + CardsViewSizeKind.Large => 128, + CardsViewSizeKind.ExtraLarge => 160, + _ => 128 + }; - public bool IsPointerOver + /// + /// Gets the icon box width for the Cards View layout based on the card size. + /// + public int CardsViewIconBoxWidth => LayoutSettingsService.CardsViewSize switch { - get => (bool)GetValue(IsPointerOverProperty); - set => SetValue(IsPointerOverProperty, value); - } + CardsViewSizeKind.Small => 104, + CardsViewSizeKind.Medium => 240, + CardsViewSizeKind.Large => 280, + CardsViewSizeKind.ExtraLarge => 320, + _ => 128 + }; + + /// + /// Gets the orientation of cards in the Cards View layout. + /// + public Orientation CardsViewOrientation => UserSettingsService.LayoutSettingsService.CardsViewSize == CardsViewSizeKind.Small + ? Orientation.Horizontal + : Orientation.Vertical; + + /// + /// Gets the maximum lines for item names in the Cards View layout. + /// + public int CardsViewItemNameMaxLines => + LayoutSettingsService.CardsViewSize == CardsViewSizeKind.ExtraLarge ? 1 : 2; + + /// + /// Gets the visibility for the contextual property string in the Cards View layout. + /// + public bool CardsViewShowContextualProperty => + LayoutSettingsService.CardsViewSize != CardsViewSizeKind.Small; + + /// + /// Gets the icon size for items in the Cards View layout. + /// + public int CardsViewIconSize => + (int)LayoutSizeKindHelper.GetIconSize(FolderLayoutModes.CardsView); + - public static readonly DependencyProperty IsPointerOverProperty = - DependencyProperty.Register( - nameof(IsPointerOver), - typeof(bool), - typeof(GridLayoutPage), - new PropertyMetadata(false)); // Constructor @@ -127,7 +198,7 @@ protected override void OnNavigatedTo(NavigationEventArgs eventArgs) base.OnNavigatedTo(eventArgs); - currentIconSize = FolderSettings.GetRoundedIconSize(); + currentIconSize = LayoutSizeKindHelper.GetIconSize(FolderSettings.LayoutMode); FolderSettings.LayoutModeChangeRequested -= FolderSettings_LayoutModeChangeRequested; FolderSettings.LayoutModeChangeRequested += FolderSettings_LayoutModeChangeRequested; @@ -140,7 +211,7 @@ protected override void OnNavigatedTo(NavigationEventArgs eventArgs) var parameters = (NavigationArguments)eventArgs.Parameter; if (parameters.IsLayoutSwitch) - ReloadItemIconsAsync(); + _ = ReloadItemIconsAsync(); } protected override void OnNavigatingFrom(NavigatingCancelEventArgs e) @@ -162,50 +233,39 @@ private void LayoutSettingsService_PropertyChanged(object? sender, PropertyChang if (e.PropertyName == nameof(ILayoutSettingsService.ListViewSize)) { NotifyPropertyChanged(nameof(RowHeightListView)); + NotifyPropertyChanged(nameof(IconBoxSizeListView)); // Update the container style to match the item size SetItemContainerStyle(); - - FolderSettings_IconHeightChanged(); + FolderSettings_IconSizeChanged(); } - if (e.PropertyName == nameof(ILayoutSettingsService.TilesViewSize)) + if (e.PropertyName == nameof(ILayoutSettingsService.CardsViewSize)) { - NotifyPropertyChanged(nameof(ItemWidthTilesView)); - // Update the container style to match the item size SetItemContainerStyle(); - FolderSettings_IconHeightChanged(); + FolderSettings_IconSizeChanged(); } if (e.PropertyName == nameof(ILayoutSettingsService.GridViewSize)) { - NotifyPropertyChanged(nameof(ItemWidthGridView)); - // Update the container style to match the item size SetItemContainerStyle(); - - FolderSettings_IconHeightChanged(); + FolderSettings_IconSizeChanged(); } // Restore correct scroll position ContentScroller?.ChangeView(previousHorizontalOffset, previousVerticalOffset, null); } - private async void FolderSettings_LayoutModeChangeRequested(object? sender, LayoutModeEventArgs e) + private void FolderSettings_LayoutModeChangeRequested(object? sender, LayoutModeEventArgs e) { if (FolderSettings.LayoutMode == FolderLayoutModes.ListView - || FolderSettings.LayoutMode == FolderLayoutModes.TilesView + || FolderSettings.LayoutMode == FolderLayoutModes.CardsView || FolderSettings.LayoutMode == FolderLayoutModes.GridView) { // Set ItemTemplate SetItemTemplate(); SetItemContainerStyle(); - - var requestedIconSize = FolderSettings.GetRoundedIconSize(); - if (requestedIconSize != currentIconSize) - { - currentIconSize = requestedIconSize; - await ReloadItemIconsAsync(); - } + FolderSettings_IconSizeChanged(); } } @@ -214,7 +274,7 @@ private void SetItemTemplate() var newFileListStyle = FolderSettings.LayoutMode switch { FolderLayoutModes.ListView => (Style)Resources["VerticalLayoutGridView"], - FolderLayoutModes.TilesView => (Style)Resources["HorizontalLayoutGridView"], + FolderLayoutModes.CardsView => (Style)Resources["HorizontalLayoutGridView"], _ => (Style)Resources["HorizontalLayoutGridView"] }; @@ -233,8 +293,8 @@ private void SetItemTemplate() case FolderLayoutModes.ListView: FileList.ItemTemplate = ListViewBrowserTemplate; break; - case FolderLayoutModes.TilesView: - FileList.ItemTemplate = TilesBrowserTemplate; + case FolderLayoutModes.CardsView: + FileList.ItemTemplate = CardsBrowserTemplate; break; default: FileList.ItemTemplate = GridViewBrowserTemplate; @@ -244,21 +304,32 @@ private void SetItemTemplate() private void SetItemContainerStyle() { - if (FolderSettings?.LayoutMode == FolderLayoutModes.ListView && UserSettingsService.LayoutSettingsService.ListViewSize == ListViewSizeKind.Compact) + if (FolderSettings?.LayoutMode == FolderLayoutModes.CardsView || FolderSettings?.LayoutMode == FolderLayoutModes.GridView) { // Toggle style to force item size to update - FileList.ItemContainerStyle = DefaultItemContainerStyle; + FileList.ItemContainerStyle = LocalListItemContainerStyle; // Set correct style - FileList.ItemContainerStyle = CompactListItemContainerStyle; + FileList.ItemContainerStyle = LocalRegularItemContainerStyle; } - else + else if (FolderSettings?.LayoutMode == FolderLayoutModes.ListView) { - // Toggle style to force item size to update - FileList.ItemContainerStyle = CompactListItemContainerStyle; + if (UserSettingsService.LayoutSettingsService.ListViewSize == ListViewSizeKind.Compact) + { + // Toggle style to force item size to update + FileList.ItemContainerStyle = LocalRegularItemContainerStyle; - // Set correct style - FileList.ItemContainerStyle = DefaultItemContainerStyle; + // Set correct style + FileList.ItemContainerStyle = LocalCompactListItemContainerStyle; + } + else + { + // Toggle style to force item size to update + FileList.ItemContainerStyle = LocalCompactListItemContainerStyle; + + // Set correct style + FileList.ItemContainerStyle = LocalListItemContainerStyle; + } } } @@ -297,7 +368,7 @@ override public void StartRenameItem() TextBox? textBox = null; - // Handle layout differences between tiles browser and photo album + // Grid View if (FolderSettings.LayoutMode == FolderLayoutModes.GridView) { if (gridViewItem.FindDescendant("EditPopup") is not Popup popup) @@ -312,6 +383,7 @@ override public void StartRenameItem() popup.IsOpen = true; OldItemName = textBlock.Text; } + // List View else if (FolderSettings.LayoutMode == FolderLayoutModes.ListView) { textBox = gridViewItem.FindDescendant("ListViewTextBoxItemName") as TextBox; @@ -330,6 +402,7 @@ override public void StartRenameItem() return; } } + // Cards View else { textBox = gridViewItem.FindDescendant("TileViewTextBoxItemName") as TextBox; @@ -338,12 +411,10 @@ override public void StartRenameItem() textBox.Text = textBlock.Text; OldItemName = textBlock.Text; - textBlock.Visibility = Visibility.Collapsed; textBox.Visibility = Visibility.Visible; if (textBox.FindParent() is null) { - textBlock.Visibility = Visibility.Visible; textBox.Visibility = Visibility.Collapsed; return; } @@ -366,7 +437,7 @@ private void ItemNameTextBox_BeforeTextChanging(TextBox textBox, TextBoxBeforeTe if (!IsRenamingItem) return; - ValidateItemNameInputTextAsync(textBox, args, (showError) => + _ = ValidateItemNameInputTextAsync(textBox, args, (showError) => { FileNameTeachingTip.Visibility = showError ? Visibility.Visible : Visibility.Collapsed; FileNameTeachingTip.IsOpen = showError; @@ -392,7 +463,7 @@ protected override void EndRename(TextBox textBox) if (textBlock is not null) textBlock.Opacity = (textBlock.DataContext as ListedItem)!.Opacity; } - else if (FolderSettings.LayoutMode == FolderLayoutModes.TilesView || FolderSettings.LayoutMode == FolderLayoutModes.ListView) + else if (FolderSettings.LayoutMode == FolderLayoutModes.CardsView || FolderSettings.LayoutMode == FolderLayoutModes.ListView) { TextBlock? textBlock = gridViewItem.FindDescendant("ItemName") as TextBlock; @@ -445,7 +516,7 @@ protected override async void FileList_PreviewKeyDown(object sender, KeyRoutedEv foreach (ListedItem? folder in folders) { if (folder is not null) - await NavigationHelpers.OpenPathInNewTab(folder.ItemPath, false); + await NavigationHelpers.OpenPathInNewTab(folder.ItemPath); } } else if (ctrlPressed && shiftPressed) @@ -460,8 +531,7 @@ protected override async void FileList_PreviewKeyDown(object sender, KeyRoutedEv } else if (e.Key == VirtualKey.Space) { - if (!ParentShellPageInstance.ToolbarViewModel.IsEditModeEnabled) - e.Handled = true; + e.Handled = true; } else if (e.KeyStatus.IsMenuKeyDown && (e.Key == VirtualKey.Left || e.Key == VirtualKey.Right || e.Key == VirtualKey.Up)) { @@ -487,17 +557,14 @@ protected override async void FileList_PreviewKeyDown(object sender, KeyRoutedEv protected override bool CanGetItemFromElement(object element) => element is GridViewItem; - private async void FolderSettings_IconHeightChanged() + private void FolderSettings_IconSizeChanged() { - // Get new icon size - var requestedIconSize = FolderSettings.GetRoundedIconSize(); - - // Prevents reloading icons when the icon size hasn't changed - if (requestedIconSize != currentIconSize) + // Check if icons need to be reloaded + var newIconSize = LayoutSizeKindHelper.GetIconSize(FolderSettings.LayoutMode); + if (newIconSize != currentIconSize) { - // Update icon size before refreshing - currentIconSize = requestedIconSize; - await ReloadItemIconsAsync(); + currentIconSize = newIconSize; + _ = ReloadItemIconsAsync(); } } @@ -519,7 +586,7 @@ private async Task ReloadItemIconsAsync() { await Task.WhenAll(filesAndFolders.Select(item => { - if (item is GitItem gitItem) + if (item is IGitItem gitItem) return ParentShellPageInstance.ShellViewModel.LoadGitPropertiesAsync(gitItem); return Task.CompletedTask; @@ -535,7 +602,14 @@ private async void FileList_ItemTapped(object sender, TappedRoutedEventArgs e) var item = (e.OriginalSource as FrameworkElement)?.DataContext as ListedItem; if (item is null) + { + // Clear selection when clicking empty area via touch + // https://github.com/files-community/Files/issues/15051 + if (e.PointerDeviceType == PointerDeviceType.Touch) + ItemManipulationModel.ClearSelection(); + return; + } // Skip code if the control or shift key is pressed or if the user is using multiselect if (ctrlPressed || @@ -547,7 +621,8 @@ private async void FileList_ItemTapped(object sender, TappedRoutedEventArgs e) } // Check if the setting to open items with a single click is turned on - if (UserSettingsService.FoldersSettingsService.OpenItemsWithOneClick) + if ((item.PrimaryItemAttribute is StorageItemTypes.File && UserSettingsService.FoldersSettingsService.OpenItemsWithOneClick) || + (item.PrimaryItemAttribute is StorageItemTypes.Folder && UserSettingsService.FoldersSettingsService.OpenFoldersWithOneClick is OpenFoldersWithOneClickEnum.Always)) { ResetRenameDoubleClick(); await Commands.OpenItem.ExecuteAsync(); @@ -586,9 +661,10 @@ private async void FileList_DoubleTapped(object sender, DoubleTappedRoutedEventA { // Skip opening selected items if the double tap doesn't capture an item if ((e.OriginalSource as FrameworkElement)?.DataContext is ListedItem item && - !UserSettingsService.FoldersSettingsService.OpenItemsWithOneClick) + ((item.PrimaryItemAttribute == StorageItemTypes.File && !UserSettingsService.FoldersSettingsService.OpenItemsWithOneClick) || + (item.PrimaryItemAttribute == StorageItemTypes.Folder && UserSettingsService.FoldersSettingsService.OpenFoldersWithOneClick is not OpenFoldersWithOneClickEnum.Always))) await Commands.OpenItem.ExecuteAsync(); - else if (UserSettingsService.FoldersSettingsService.DoubleClickToGoUp) + else if ((e.OriginalSource as FrameworkElement)?.DataContext is not ListedItem && UserSettingsService.FoldersSettingsService.DoubleClickToGoUp) await Commands.NavigateUp.ExecuteAsync(); ResetRenameDoubleClick(); @@ -604,10 +680,18 @@ checkBox.DataContext is ListedItem item && private void ItemSelected_Unchecked(object sender, RoutedEventArgs e) { - if (sender is CheckBox checkBox && - checkBox.DataContext is ListedItem item && - FileList.SelectedItems.Contains(item)) + if (sender is not CheckBox checkBox) + return; + + if (checkBox.DataContext is ListedItem item && FileList.SelectedItems.Contains(item)) FileList.SelectedItems.Remove(item); + + // Workaround for #17298 + checkBox.IsTabStop = false; + checkBox.IsEnabled = false; + checkBox.IsEnabled = true; + checkBox.IsTabStop = true; + FileList.Focus(FocusState.Programmatic); } private new void FileList_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args) @@ -696,7 +780,7 @@ private void SelectionCheckbox_PointerCanceled(object sender, PointerRoutedEvent // To avoid crashes, disable scrolling when drag-and-drop if grouped. (#14484) private bool ShouldDisableScrollingWhenDragAndDrop => - FolderSettings?.LayoutMode is FolderLayoutModes.GridView or FolderLayoutModes.TilesView && + FolderSettings?.LayoutMode is FolderLayoutModes.GridView or FolderLayoutModes.CardsView && (ParentShellPageInstance?.ShellViewModel.FilesAndFolders.IsGrouped ?? false); protected override void FileList_DragItemsStarting(object sender, DragItemsStartingEventArgs e) @@ -753,4 +837,4 @@ private void UpdateCheckboxVisibility(object sender, bool isPointerOver) } } } -} +} \ No newline at end of file diff --git a/src/Files.App/Views/Layouts/IBaseLayoutPage.cs b/src/Files.App/Views/Layouts/IBaseLayoutPage.cs index 2e2a8bea9875..b90e500f6c46 100644 --- a/src/Files.App/Views/Layouts/IBaseLayoutPage.cs +++ b/src/Files.App/Views/Layouts/IBaseLayoutPage.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.ViewModels.Layouts; using Microsoft.UI.Xaml.Controls; diff --git a/src/Files.App/Views/MainPage.xaml b/src/Files.App/Views/MainPage.xaml index c96d511cea4e..3859b26271c2 100644 --- a/src/Files.App/Views/MainPage.xaml +++ b/src/Files.App/Views/MainPage.xaml @@ -1,19 +1,20 @@ - + @@ -30,6 +32,24 @@ + + + ms-appx:///Assets/Sidebar/EmptySidebar_48_ThemeLight.svg + ms-appx:///Assets/Sidebar/EmptySidebar_100_ThemeLight.svg + ms-appx:///Assets/Sidebar/EmptySidebar_200_ThemeLight.svg + + + ms-appx:///Assets/Sidebar/EmptySidebar_48_ThemeDark.svg + ms-appx:///Assets/Sidebar/EmptySidebar_100_ThemeDark.svg + ms-appx:///Assets/Sidebar/EmptySidebar_200_ThemeDark.svg + + + ms-appx:///Assets/Sidebar/EmptySidebar_48_ThemeDark.svg + ms-appx:///Assets/Sidebar/EmptySidebar_100_ThemeDark.svg + ms-appx:///Assets/Sidebar/EmptySidebar_200_ThemeDark.svg + + + True False @@ -54,140 +74,123 @@ - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + + - - - - - - - - - - - - - - + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + - + + + + - + - + - + - + + - - + @@ -300,6 +452,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App/Views/MainPage.xaml.cs b/src/Files.App/Views/MainPage.xaml.cs index 812b763361e7..41f9a1efee5f 100644 --- a/src/Files.App/Views/MainPage.xaml.cs +++ b/src/Files.App/Views/MainPage.xaml.cs @@ -1,25 +1,22 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. -using CommunityToolkit.WinUI.Helpers; -using CommunityToolkit.WinUI.UI; -using CommunityToolkit.WinUI.UI.Controls; -using Files.App.UserControls.Sidebar; +using CommunityToolkit.WinUI; +using Files.App.Controls; using Microsoft.Extensions.Logging; using Microsoft.UI.Dispatching; using Microsoft.UI.Input; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Navigation; -using Sentry; -using System.Data; -using Windows.ApplicationModel; -using Windows.ApplicationModel.DataTransfer; +using System.Runtime.InteropServices; using Windows.Foundation.Metadata; using Windows.Graphics; -using Windows.Services.Store; -using WinRT.Interop; +using Windows.UI.Input; +using WinUIEx; +using GridSplitter = Files.App.Controls.GridSplitter; using VirtualKey = Windows.System.VirtualKey; namespace Files.App.Views @@ -27,25 +24,15 @@ namespace Files.App.Views public sealed partial class MainPage : Page { private IGeneralSettingsService generalSettingsService { get; } = Ioc.Default.GetRequiredService(); + private readonly IContentPageContext ContentPageContext = Ioc.Default.GetRequiredService(); public IUserSettingsService UserSettingsService { get; } - - public ICommandManager Commands { get; } - - public IWindowContext WindowContext { get; } - + private readonly IWindowContext WindowContext = Ioc.Default.GetRequiredService(); + private readonly ICommandManager Commands = Ioc.Default.GetRequiredService(); public SidebarViewModel SidebarAdaptiveViewModel { get; } - public MainPageViewModel ViewModel { get; } - public StatusCenterViewModel OngoingTasksViewModel { get; } - - public static AppModel AppModel - => App.AppModel; - private bool keyReleased = true; - private bool isAppRunningAsAdmin => ElevationHelpers.IsAppRunAsAdmin(); - private DispatcherQueueTimer _updateDateDisplayTimer; public MainPage() @@ -54,15 +41,15 @@ public MainPage() // Dependency Injection UserSettingsService = Ioc.Default.GetRequiredService(); - Commands = Ioc.Default.GetRequiredService(); - WindowContext = Ioc.Default.GetRequiredService(); SidebarAdaptiveViewModel = Ioc.Default.GetRequiredService(); SidebarAdaptiveViewModel.PaneFlyout = (MenuFlyout)Resources["SidebarContextMenu"]; ViewModel = Ioc.Default.GetRequiredService(); - OngoingTasksViewModel = Ioc.Default.GetRequiredService(); - if (FilePropertiesHelpers.FlowDirectionSettingIsRightToLeft) + if (AppLanguageHelper.IsPreferredLanguageRtl) + { + MainWindow.Instance.SetExtendedWindowStyle(ExtendedWindowStyle.LayoutRtl); FlowDirection = FlowDirection.RightToLeft; + } ViewModel.PropertyChanged += ViewModel_PropertyChanged; UserSettingsService.OnSettingChangedEvent += UserSettingsService_OnSettingChangedEvent; @@ -70,50 +57,21 @@ public MainPage() _updateDateDisplayTimer = DispatcherQueue.CreateTimer(); _updateDateDisplayTimer.Interval = TimeSpan.FromSeconds(1); _updateDateDisplayTimer.Tick += UpdateDateDisplayTimer_Tick; - } - - private async Task PromptForReviewAsync() - { - var promptForReviewDialog = new ContentDialog - { - Title = "ReviewFiles".ToLocalized(), - Content = "ReviewFilesContent".ToLocalized(), - PrimaryButtonText = "Yes".ToLocalized(), - SecondaryButtonText = "No".ToLocalized() - }; - if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) - promptForReviewDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; - - var result = await promptForReviewDialog.TryShowAsync(); - - if (result == ContentDialogResult.Primary) - { - try - { - var storeContext = StoreContext.GetDefault(); - InitializeWithWindow.Initialize(storeContext, MainWindow.Instance.WindowHandle); - var storeRateAndReviewResult = await storeContext.RequestRateAndReviewAppAsync(); - - App.Logger.LogInformation($"STORE: review request status: {storeRateAndReviewResult.Status}"); - - UserSettingsService.ApplicationSettingsService.ClickedToReviewApp = true; - } - catch (Exception) { } - } + ApplySidebarWidthState(); } private async Task AppRunningAsAdminPromptAsync() { var runningAsAdminPrompt = new ContentDialog { - Title = "FilesRunningAsAdmin".ToLocalized(), - Content = "FilesRunningAsAdminContent".ToLocalized(), + Title = Strings.FilesRunningAsAdmin.ToLocalized(), + Content = Strings.FilesRunningAsAdminContent.ToLocalized(), PrimaryButtonText = "Ok".ToLocalized(), - SecondaryButtonText = "DontShowAgain".ToLocalized() + SecondaryButtonText = Strings.DontShowAgain.ToLocalized() }; - var result = await runningAsAdminPrompt.TryShowAsync(); + var result = await SetContentDialogRoot(runningAsAdminPrompt).TryShowAsync(); if (result == ContentDialogResult.Secondary) UserSettingsService.ApplicationSettingsService.ShowRunningAsAdminPrompt = false; @@ -132,15 +90,19 @@ private void UserSettingsService_OnSettingChangedEvent(object? sender, SettingCh { switch (e.SettingName) { - case nameof(IInfoPaneSettingsService.IsEnabled): + case nameof(IInfoPaneSettingsService.IsInfoPaneEnabled): LoadPaneChanged(); break; + case nameof(IAppearanceSettingsService.SidebarWidth): + ApplySidebarWidthState(); + break; } } private void HorizontalMultitaskingControl_Loaded(object sender, RoutedEventArgs e) { TabControl.DragArea.SizeChanged += (_, _) => MainWindow.Instance.RaiseSetTitleBarDragRegion(SetTitleBarDragRegion); + TabControl.SizeChanged += (_, _) => MainWindow.Instance.RaiseSetTitleBarDragRegion(SetTitleBarDragRegion); if (ViewModel.MultitaskingControl is not TabBar) { ViewModel.MultitaskingControl = TabControl; @@ -174,21 +136,31 @@ public async void TabItemContent_ContentChanged(object? sender, TabBarItemParame AppLifecycleHelper.SaveSessionTabs(); } + public async void MultitaskingControl_CurrentInstanceChanged(object? sender, CurrentInstanceChangedEventArgs e) { - if (SidebarAdaptiveViewModel.PaneHolder is not null) + // Add null check for the event args and CurrentInstance + if (e?.CurrentInstance == null) + return; + + // Safely unsubscribe from previous instance + if (SidebarAdaptiveViewModel?.PaneHolder is not null) SidebarAdaptiveViewModel.PaneHolder.PropertyChanged -= PaneHolder_PropertyChanged; var navArgs = e.CurrentInstance.TabBarItemParameter?.NavigationParameter; - if (e.CurrentInstance is IShellPanesPage currentInstance) + + if (e.CurrentInstance is IShellPanesPage currentInstance && SidebarAdaptiveViewModel != null) { SidebarAdaptiveViewModel.PaneHolder = currentInstance; SidebarAdaptiveViewModel.PaneHolder.PropertyChanged += PaneHolder_PropertyChanged; } - SidebarAdaptiveViewModel.NotifyInstanceRelatedPropertiesChanged((navArgs as PaneNavigationArguments)?.LeftPaneNavPathParam); - if (SidebarAdaptiveViewModel.PaneHolder?.ActivePaneOrColumn.SlimContentPage?.StatusBarViewModel is not null) - SidebarAdaptiveViewModel.PaneHolder.ActivePaneOrColumn.SlimContentPage.StatusBarViewModel.ShowLocals = true; + SidebarAdaptiveViewModel?.NotifyInstanceRelatedPropertiesChanged((navArgs as PaneNavigationArguments)?.LeftPaneNavPathParam); + + // Safely access nested properties with null checks + var statusBarViewModel = SidebarAdaptiveViewModel?.PaneHolder?.ActivePaneOrColumn?.SlimContentPage?.StatusBarViewModel; + if (statusBarViewModel is not null) + statusBarViewModel.ShowLocals = true; UpdateStatusBarProperties(); UpdateNavToolbarProperties(); @@ -198,6 +170,11 @@ public async void MultitaskingControl_CurrentInstanceChanged(object? sender, Cur e.CurrentInstance.ContentChanged += TabItemContent_ContentChanged; await NavigationHelpers.UpdateInstancePropertiesAsync(navArgs); + + // Focus the content of the selected tab item (this also avoids an issue where the Omnibar sometimes steals the focus) + await Task.Delay(100); + if (!App.AppModel.IsMainWindowClosed && ContentPageContext?.ShellPage?.PaneHolder != null) + ContentPageContext.ShellPage.PaneHolder.FocusActivePane(); } private void PaneHolder_PropertyChanged(object? sender, PropertyChangedEventArgs e) @@ -228,7 +205,7 @@ private void UpdateNavToolbarProperties() protected override void OnNavigatedTo(NavigationEventArgs e) { - ViewModel.OnNavigatedToAsync(e); + _ = ViewModel.OnNavigatedToAsync(e); } protected override async void OnPreviewKeyDown(KeyRoutedEventArgs e) => await OnPreviewKeyDownAsync(e); @@ -248,13 +225,22 @@ private async Task OnPreviewKeyDownAsync(KeyRoutedEventArgs e) default: var currentModifiers = HotKeyHelpers.GetCurrentKeyModifiers(); HotKey hotKey = new((Keys)e.Key, currentModifiers); + var source = e.OriginalSource as DependencyObject; // A textbox takes precedence over certain hotkeys. - if (e.OriginalSource is DependencyObject source && source.FindAscendantOrSelf() is not null) + if (source?.FindAscendantOrSelf() is not null) break; // Execute command for hotkey var command = Commands[hotKey]; + + if (command.Code is CommandCodes.OpenItem && (source?.FindAscendantOrSelf() is not null || source?.FindAscendantOrSelf() is not null)) + break; + + // Prevent ctrl + c from overriding copy in textblocks + if (currentModifiers == KeyModifiers.Ctrl && e.Key is VirtualKey.C && (FrameworkElement)FocusManager.GetFocusedElement(MainWindow.Instance.Content.XamlRoot) is TextBlock) + break; + if (command.Code is not CommandCodes.None && keyReleased) { keyReleased = false; @@ -307,23 +293,12 @@ private void Page_Loaded(object sender, RoutedEventArgs e) if ( AppLifecycleHelper.AppEnvironment is not AppEnvironment.Dev && - isAppRunningAsAdmin && + WindowContext.IsRunningAsAdmin && UserSettingsService.ApplicationSettingsService.ShowRunningAsAdminPrompt ) { DispatcherQueue.TryEnqueue(async () => await AppRunningAsAdminPromptAsync()); } - - // ToDo put this in a StartupPromptService - if (Package.Current.Id.Name != "49306atecsolution.FilesUWP" || UserSettingsService.ApplicationSettingsService.ClickedToReviewApp) - return; - - var totalLaunchCount = SystemInformation.Instance.TotalLaunchCount; - if (totalLaunchCount is 15 or 30 or 60) - { - // Prompt user to review app in the Store - DispatcherQueue.TryEnqueue(async () => await PromptForReviewAsync()); - } } private void PreviewPane_Loaded(object sender, RoutedEventArgs e) @@ -340,6 +315,8 @@ private void UpdateDateDisplayTimer_Tick(object sender, object e) { if (!App.AppModel.IsMainWindowClosed) InfoPane?.ViewModel.UpdateDateDisplay(); + else + App.Logger.LogWarning("UpdateDateDisplayTimer_Tick: Timer firing after window closed!"); } private void Page_SizeChanged(object sender, SizeChangedEventArgs e) @@ -365,20 +342,11 @@ private void SidebarControl_Loaded(object sender, RoutedEventArgs e) private void RootGrid_SizeChanged(object sender, SizeChangedEventArgs e) => LoadPaneChanged(); - /// - /// Call this function to update the positioning of the preview pane. - /// This is a workaround as the VisualStateManager causes problems. - /// private void UpdatePositioning() { if (InfoPane is null || !ViewModel.ShouldPreviewPaneBeActive) { - PaneRow.MinHeight = 0; - PaneRow.MaxHeight = double.MaxValue; - PaneRow.Height = new GridLength(0); - PaneColumn.MinWidth = 0; - PaneColumn.MaxWidth = double.MaxValue; - PaneColumn.Width = new GridLength(0); + VisualStateManager.GoToState(this, "InfoPanePositionNone", true); } else { @@ -386,42 +354,17 @@ private void UpdatePositioning() switch (InfoPane.Position) { case PreviewPanePositions.None: - PaneRow.MinHeight = 0; - PaneRow.Height = new GridLength(0); - PaneColumn.MinWidth = 0; - PaneColumn.Width = new GridLength(0); + VisualStateManager.GoToState(this, "InfoPanePositionNone", true); break; case PreviewPanePositions.Right: - InfoPane.SetValue(Grid.RowProperty, 1); - InfoPane.SetValue(Grid.ColumnProperty, 2); - PaneSplitter.SetValue(Grid.RowProperty, 1); - PaneSplitter.SetValue(Grid.ColumnProperty, 1); - PaneSplitter.Width = 2; - PaneSplitter.Height = RootGrid.ActualHeight; - PaneSplitter.GripperCursor = GridSplitter.GripperCursorType.SizeWestEast; - PaneSplitter.ChangeCursor(InputSystemCursor.Create(InputSystemCursorShape.SizeWestEast)); - PaneColumn.MinWidth = InfoPane.MinWidth; - PaneColumn.MaxWidth = InfoPane.MaxWidth; - PaneColumn.Width = new GridLength(UserSettingsService.InfoPaneSettingsService.VerticalSizePx, GridUnitType.Pixel); - PaneRow.MinHeight = 0; - PaneRow.MaxHeight = double.MaxValue; - PaneRow.Height = new GridLength(0); + InfoPaneSizer.ChangeCursor(InputSystemCursor.Create(InputSystemCursorShape.SizeWestEast)); + InfoPaneColumnDefinition.Width = new(UserSettingsService.InfoPaneSettingsService.VerticalSizePx); + VisualStateManager.GoToState(this, "InfoPanePositionRight", true); break; case PreviewPanePositions.Bottom: - InfoPane.SetValue(Grid.RowProperty, 3); - InfoPane.SetValue(Grid.ColumnProperty, 0); - PaneSplitter.SetValue(Grid.RowProperty, 2); - PaneSplitter.SetValue(Grid.ColumnProperty, 0); - PaneSplitter.Height = 2; - PaneSplitter.Width = RootGrid.ActualWidth; - PaneSplitter.GripperCursor = GridSplitter.GripperCursorType.SizeNorthSouth; - PaneSplitter.ChangeCursor(InputSystemCursor.Create(InputSystemCursorShape.SizeNorthSouth)); - PaneColumn.MinWidth = 0; - PaneColumn.MaxWidth = double.MaxValue; - PaneColumn.Width = new GridLength(0); - PaneRow.MinHeight = InfoPane.MinHeight; - PaneRow.MaxHeight = InfoPane.MaxHeight; - PaneRow.Height = new GridLength(UserSettingsService.InfoPaneSettingsService.HorizontalSizePx, GridUnitType.Pixel); + InfoPaneSizer.ChangeCursor(InputSystemCursor.Create(InputSystemCursorShape.SizeNorthSouth)); + InfoPaneRowDefinition.Height = new(UserSettingsService.InfoPaneSettingsService.HorizontalSizePx); + VisualStateManager.GoToState(this, "InfoPanePositionBottom", true); break; } } @@ -442,17 +385,28 @@ private void PaneSplitter_ManipulationCompleted(object sender, ManipulationCompl this.ChangeCursor(InputSystemCursor.Create(InputSystemCursorShape.Arrow)); } + private void ApplySidebarWidthState() + { + if (UserSettingsService.AppearanceSettingsService.SidebarWidth > 360) + VisualStateManager.GoToState(this, "LargeSidebarWidthState", true); + else if (UserSettingsService.AppearanceSettingsService.SidebarWidth > 280) + VisualStateManager.GoToState(this, "MediumSidebarWidthState", true); + else + VisualStateManager.GoToState(this, "SmallSidebarWidthState", true); + } + private void LoadPaneChanged() { try { var isHomePage = !(SidebarAdaptiveViewModel.PaneHolder?.ActivePane?.InstanceViewModel?.IsPageTypeNotHome ?? false); + var isReleaseNotesPage = SidebarAdaptiveViewModel.PaneHolder?.ActivePane?.InstanceViewModel?.IsPageTypeReleaseNotes ?? false; var isMultiPane = SidebarAdaptiveViewModel.PaneHolder?.IsMultiPaneActive ?? false; var isBigEnough = !App.AppModel.IsMainWindowClosed && (MainWindow.Instance.Bounds.Width > 450 && MainWindow.Instance.Bounds.Height > 450 || RootGrid.ActualWidth > 700 && MainWindow.Instance.Bounds.Height > 360); - ViewModel.ShouldPreviewPaneBeDisplayed = (!isHomePage || isMultiPane) && isBigEnough; - ViewModel.ShouldPreviewPaneBeActive = UserSettingsService.InfoPaneSettingsService.IsEnabled && ViewModel.ShouldPreviewPaneBeDisplayed; + ViewModel.ShouldPreviewPaneBeDisplayed = ((!isHomePage && !isReleaseNotesPage) || isMultiPane) && isBigEnough; + ViewModel.ShouldPreviewPaneBeActive = UserSettingsService.InfoPaneSettingsService.IsInfoPaneEnabled && ViewModel.ShouldPreviewPaneBeDisplayed; ViewModel.ShouldViewControlBeDisplayed = SidebarAdaptiveViewModel.PaneHolder?.ActivePane?.InstanceViewModel?.IsPageTypeNotHome ?? false; UpdatePositioning(); @@ -497,16 +451,59 @@ private void RootGrid_PreviewKeyDown(object sender, KeyRoutedEventArgs e) private void PaneSplitter_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e) { - this.ChangeCursor(InputSystemCursor.Create(PaneSplitter.GripperCursor == GridSplitter.GripperCursorType.SizeWestEast ? + this.ChangeCursor(InputSystemCursor.Create(InfoPane.Position == PreviewPanePositions.Right ? InputSystemCursorShape.SizeWestEast : InputSystemCursorShape.SizeNorthSouth)); } - private void TogglePaneButton_Click(object sender, RoutedEventArgs e) + private void SettingsButton_AccessKeyInvoked(UIElement sender, AccessKeyInvokedEventArgs args) + { + // Suppress access key invocation if any dialog is open + if (VisualTreeHelper.GetOpenPopupsForXamlRoot(MainWindow.Instance.Content.XamlRoot).Any()) + args.Handled = true; + } + + private void Page_PointerReleased(object sender, PointerRoutedEventArgs e) + { + // Workaround for issue where clicking an empty area in the window (toolbar, title bar etc) prevents keyboard + // shortcuts from working properly, see https://github.com/microsoft/microsoft-ui-xaml/issues/6467 + DispatcherQueue.TryEnqueue(() => ContentPageContext.ShellPage?.PaneHolder.FocusActivePane()); + } + + private void SidebarControl_ItemContextInvoked(object sender, ItemContextInvokedArgs e) { - if (SidebarControl.DisplayMode == SidebarDisplayMode.Minimal) + SidebarAdaptiveViewModel.HandleItemContextInvokedAsync(sender, e); + } + + private async void SidebarControl_ItemDragOver(object sender, ItemDragOverEventArgs e) + { + var deferral = e.RawEvent.GetDeferral(); + + await SafetyExtensions.IgnoreExceptions(async () => { - SidebarControl.IsPaneOpen = !SidebarControl.IsPaneOpen; - } + await SidebarAdaptiveViewModel.HandleItemDragOverAsync(e); + }, App.Logger); + + deferral.Complete(); + } + + private async void SidebarControl_ItemDropped(object sender, ItemDroppedEventArgs e) + { + var deferral = e.RawEvent.GetDeferral(); + + await SafetyExtensions.IgnoreExceptions(async () => + { + await SidebarAdaptiveViewModel.HandleItemDroppedAsync(e); + }, App.Logger); + + deferral.Complete(); + } + + private void SidebarControl_ItemInvoked(object sender, ItemInvokedEventArgs e) + { + if (sender is not SidebarItem { Item: ISidebarItemModel item }) + return; + + SidebarAdaptiveViewModel.HandleItemInvokedAsync(item, e.PointerUpdateKind); } } -} +} \ No newline at end of file diff --git a/src/Files.App/Views/Properties/CompatibilityPage.xaml b/src/Files.App/Views/Properties/CompatibilityPage.xaml index 533aa5995ddd..6d05ba1fb3c6 100644 --- a/src/Files.App/Views/Properties/CompatibilityPage.xaml +++ b/src/Files.App/Views/Properties/CompatibilityPage.xaml @@ -1,4 +1,4 @@ - + - @@ -30,79 +29,66 @@ - - - + + + + + - + - + - + - + - - - + + + - - - + + + - - - + + + - - - + + + - + - + - + - + diff --git a/src/Files.App/Views/Properties/CompatibilityPage.xaml.cs b/src/Files.App/Views/Properties/CompatibilityPage.xaml.cs index 2dc0071fa296..0134b6a391ee 100644 --- a/src/Files.App/Views/Properties/CompatibilityPage.xaml.cs +++ b/src/Files.App/Views/Properties/CompatibilityPage.xaml.cs @@ -1,8 +1,7 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.ViewModels.Properties; -using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Navigation; namespace Files.App.Views.Properties diff --git a/src/Files.App/Views/Properties/CustomizationPage.xaml b/src/Files.App/Views/Properties/CustomizationPage.xaml index fe789cd90203..8277320756b2 100644 --- a/src/Files.App/Views/Properties/CustomizationPage.xaml +++ b/src/Files.App/Views/Properties/CustomizationPage.xaml @@ -1,4 +1,4 @@ - + @@ -81,10 +80,7 @@ - + @@ -593,34 +592,28 @@ - - - - - + Command="{x:Bind ViewModel.CleanupDriveCommand}" + Header="{helpers:ResourceString Name=CleanupDrive}" + IsClickEnabled="True"> + + + + - - - - - + Command="{x:Bind ViewModel.FormatDriveCommand}" + Header="{helpers:ResourceString Name=FormatDrive}" + IsClickEnabled="True"> + + + + + + @@ -733,6 +728,33 @@ AutomationProperties.Name="{helpers:ResourceString Name=Hidden}" IsChecked="{x:Bind ViewModel.IsHiddenEditedValue, Mode=TwoWay}" /> + + + + + + + + diff --git a/src/Files.App/Views/Properties/GeneralPage.xaml.cs b/src/Files.App/Views/Properties/GeneralPage.xaml.cs index 2bea485133bc..d100014c2b6c 100644 --- a/src/Files.App/Views/Properties/GeneralPage.xaml.cs +++ b/src/Files.App/Views/Properties/GeneralPage.xaml.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.ViewModels.Properties; using Microsoft.UI.Dispatching; @@ -8,7 +8,6 @@ using System.IO; using Windows.Storage; using Windows.Win32; -using Files.App.Helpers; namespace Files.App.Views.Properties { @@ -63,6 +62,7 @@ public override async Task SaveChangesAsync() CombinedProperties properties => await SaveCombinedAsync(properties.List), FileProperties properties => await SaveBaseAsync(properties.Item), FolderProperties properties => await SaveBaseAsync(properties.Item), + _ => throw new UnreachableException() }; bool GetNewName(out string newName) @@ -128,6 +128,8 @@ async Task SaveCombinedAsync(IList fileOrFolders) var itemMM = AppInstance?.SlimContentPage?.ItemManipulationModel; if (itemMM is not null) // null on homepage { + ViewModel.IsContentCompressed = ViewModel.IsContentCompressedEditedValue; + foreach (var fileOrFolder in fileOrFolders) { if (ViewModel.IsHiddenEditedValue is not null) @@ -181,6 +183,7 @@ await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => ViewModel.IsReadOnly = ViewModel.IsReadOnlyEditedValue; ViewModel.IsHidden = ViewModel.IsHiddenEditedValue; + ViewModel.IsContentCompressed = ViewModel.IsContentCompressedEditedValue; if (!GetNewName(out var newName)) return true; diff --git a/src/Files.App/Views/Properties/HashesPage.xaml b/src/Files.App/Views/Properties/HashesPage.xaml index 0949f5b0fa2c..6bcdc9aa8557 100644 --- a/src/Files.App/Views/Properties/HashesPage.xaml +++ b/src/Files.App/Views/Properties/HashesPage.xaml @@ -1,4 +1,4 @@ - + + + + + + + + + + + + + + + + + - + - - + + - - + - + - - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - + + - - + + - + - + IsOn="{x:Bind ViewModel.ShowTabActions, Mode=TwoWay}" /> + - - - + + + - + + + + + + + + + + + + + + + + + + + + + + + + + - + AutomationProperties.AutomationControlType="Custom" + AutomationProperties.Name="{helpers:ResourceString Name=ShowStatusBar}" + IsOn="{x:Bind ViewModel.ShowStatusBar, Mode=TwoWay}" /> + diff --git a/src/Files.App/Views/Settings/AppearancePage.xaml.cs b/src/Files.App/Views/Settings/AppearancePage.xaml.cs index a324206ea7ec..90d59c8cc40c 100644 --- a/src/Files.App/Views/Settings/AppearancePage.xaml.cs +++ b/src/Files.App/Views/Settings/AppearancePage.xaml.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.ViewModels.Settings; using Microsoft.UI.Xaml.Controls; diff --git a/src/Files.App/Views/Settings/DevToolsPage.xaml b/src/Files.App/Views/Settings/DevToolsPage.xaml index aedde1493cb3..683185e26f9d 100644 --- a/src/Files.App/Views/Settings/DevToolsPage.xaml +++ b/src/Files.App/Views/Settings/DevToolsPage.xaml @@ -1,26 +1,30 @@ - + - - - - + + @@ -33,11 +37,6 @@ HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Spacing="4"> - - - - - - - + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + AllowDrop="True" + CanReorderItems="True" + IsItemClickEnabled="False" + ItemsSource="{x:Bind ViewModel.PagesOnStartupList, Mode=TwoWay}" + SelectedIndex="{x:Bind ViewModel.SelectedPageIndex, Mode=TwoWay}" + SelectionMode="None"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - + x:Name="AlwaysSwitchToNewlyOpenedTab" + AutomationProperties.Name="{helpers:ResourceString Name=AlwaysSwitchToNewlyOpenedTab}" + IsOn="{x:Bind ViewModel.AlwaysSwitchToNewlyOpenedTab, Mode=TwoWay}" /> + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + AutomationProperties.Name="{helpers:ResourceString Name=ShowFlattenOptions}" + IsOn="{x:Bind ViewModel.ShowFlattenOptions, Mode=TwoWay}" + /> + --> + + + + + + + + + + + + + + + + + + + + + + - - + + - - - + + + + + + + + + + + + + + diff --git a/src/Files.App/Views/Settings/GeneralPage.xaml.cs b/src/Files.App/Views/Settings/GeneralPage.xaml.cs index bc24836cb932..8113c7d24b02 100644 --- a/src/Files.App/Views/Settings/GeneralPage.xaml.cs +++ b/src/Files.App/Views/Settings/GeneralPage.xaml.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Files.App.ViewModels.Settings; using Microsoft.UI.Xaml; diff --git a/src/Files.App/Views/Settings/LayoutPage.xaml b/src/Files.App/Views/Settings/LayoutPage.xaml index a9ad988ce829..901832a9e3e7 100644 --- a/src/Files.App/Views/Settings/LayoutPage.xaml +++ b/src/Files.App/Views/Settings/LayoutPage.xaml @@ -1,24 +1,20 @@ - + - - - - @@ -31,11 +27,6 @@ HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Spacing="4"> - - - - - - - + + - + - + IsOn="{x:Bind ViewModel.SyncFolderPreferencesAcrossDirectories, Mode=TwoWay}" /> + - - + + - + - + - + - - + + - + @@ -97,33 +84,28 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - + + - + @@ -134,31 +116,28 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App/Views/Settings/LayoutPage.xaml.cs b/src/Files.App/Views/Settings/LayoutPage.xaml.cs index 903b9e8a944e..3d346171fa49 100644 --- a/src/Files.App/Views/Settings/LayoutPage.xaml.cs +++ b/src/Files.App/Views/Settings/LayoutPage.xaml.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. using Microsoft.UI.Xaml.Controls; diff --git a/src/Files.App/Views/Settings/TagsPage.xaml b/src/Files.App/Views/Settings/TagsPage.xaml index 1215486eae86..41bf338e0f3c 100644 --- a/src/Files.App/Views/Settings/TagsPage.xaml +++ b/src/Files.App/Views/Settings/TagsPage.xaml @@ -1,17 +1,17 @@ - + @@ -38,11 +38,6 @@ HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Spacing="4"> - - - - - - - + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/Files.App.UITests/MainWindow.xaml.cs b/tests/Files.App.UITests/MainWindow.xaml.cs index eebd61795287..87b3bf3800cd 100644 --- a/tests/Files.App.UITests/MainWindow.xaml.cs +++ b/tests/Files.App.UITests/MainWindow.xaml.cs @@ -1,36 +1,78 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. +// Copyright (c) Files Community +// Licensed under the MIT License. +using Files.App.UITests.Views; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls.Primitives; -using Microsoft.UI.Xaml.Data; -using Microsoft.UI.Xaml.Input; -using Microsoft.UI.Xaml.Media; -using Microsoft.UI.Xaml.Navigation; using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using Windows.Foundation; -using Windows.Foundation.Collections; namespace Files.App.UITests { - /// - /// An empty window that can be used on its own or navigated to within a Frame. - /// public sealed partial class MainWindow : Window { - public MainWindow() + private static MainWindow? _Instance; + public static MainWindow Instance => _Instance ??= new(); + + private MainWindow() { - this.InitializeComponent(); + InitializeComponent(); + + ExtendsContentIntoTitleBar = true; + MainFrame.Navigate(typeof(MainPage)); + + // Set the toggle state for theme change button + if (Content is FrameworkElement element) + { + if (element.ActualTheme is ElementTheme.Light) + { + AppThemeChangeToggleButton.IsChecked = true; + AppThemeGlyph.Glyph = "\uE706"; // Sun + } + else + { + AppThemeChangeToggleButton.IsChecked = false; + AppThemeGlyph.Glyph = "\uE708"; // Moon + } + } } - private void myButton_Click(object sender, RoutedEventArgs e) + private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args) { - myButton.Content = "Clicked"; + if (args.SelectedItem is not NavigationViewItem item || item.Tag is not string tag) + return; + + MainFrame.Navigate( + tag switch + { + nameof(ThemedIconPage) => typeof(ThemedIconPage), + nameof(ToolbarPage) => typeof(ToolbarPage), + nameof(StorageControlsPage) => typeof(StorageControlsPage), + nameof(SidebarViewPage) => typeof(SidebarViewPage), + nameof(OmnibarPage) => typeof(OmnibarPage), + nameof(BreadcrumbBarPage) => typeof(BreadcrumbBarPage), + _ => throw new InvalidOperationException("There's no applicable page associated with the given key."), + }); + + MainNavigationView.Header = item.Content.ToString(); + } + + private void AppThemeChangeToggleButton_Click(object sender, RoutedEventArgs e) + { + if (sender is not ToggleButton toggleButton || + Content is not FrameworkElement element) + return; + + if (toggleButton.IsChecked is true) + { + element.RequestedTheme = ElementTheme.Light; + AppThemeGlyph.Glyph = "\uE706"; // Sun + } + else + { + element.RequestedTheme = ElementTheme.Dark; + AppThemeGlyph.Glyph = "\uE708"; // Moon + } } } } diff --git a/tests/Files.App.UITests/Package.appxmanifest b/tests/Files.App.UITests/Package.appxmanifest index 815e8f8adff2..7b3fd995f298 100644 --- a/tests/Files.App.UITests/Package.appxmanifest +++ b/tests/Files.App.UITests/Package.appxmanifest @@ -1,5 +1,5 @@  - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/Files.App.UITests/Views/BreadcrumbBarPage.xaml.cs b/tests/Files.App.UITests/Views/BreadcrumbBarPage.xaml.cs new file mode 100644 index 000000000000..8b3deb1cc97e --- /dev/null +++ b/tests/Files.App.UITests/Views/BreadcrumbBarPage.xaml.cs @@ -0,0 +1,76 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using CommunityToolkit.WinUI; +using Files.App.Controls; +using Files.App.UITests.Data; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using System.Collections.ObjectModel; + +namespace Files.App.UITests.Views +{ + public sealed partial class BreadcrumbBarPage : Page + { + private readonly ObservableCollection DummyItems; + + [GeneratedDependencyProperty] + private partial string? ClickedItemName { get; set; } + + [GeneratedDependencyProperty] + private partial string? ClickedItemIndex { get; set; } + + [GeneratedDependencyProperty] + private partial bool IsRTLEnabled { get; set; } + + [GeneratedDependencyProperty] + private partial FlowDirection BreadcrumbBar1FlowDirection { get; set; } + + public BreadcrumbBarPage() + { + InitializeComponent(); + + DummyItems = + [ + new("Local Disk (C:)"), + new("Users"), + new("me"), + new("OneDrive"), + new("Desktop"), + new("Folder1"), + new("Folder2"), + ]; + } + + private void BreadcrumbBar1_ItemClicked(Controls.BreadcrumbBar sender, Controls.BreadcrumbBarItemClickedEventArgs args) + { + if (args.IsRootItem) + { + ClickedItemName = "Home"; + ClickedItemIndex = "Root"; + } + else + { + ClickedItemName = DummyItems[args.Index].Text; + ClickedItemIndex = $"{args.Index}"; + } + } + + private void BreadcrumbBar1_ItemDropDownFlyoutOpening(object sender, BreadcrumbBarItemDropDownFlyoutEventArgs e) + { + e.Flyout.Items.Add(new MenuFlyoutItem { Icon = new FontIcon() { Glyph = "\uE8B7" }, Text = "Item 1" }); + e.Flyout.Items.Add(new MenuFlyoutItem { Icon = new FontIcon() { Glyph = "\uE8B7" }, Text = "Item 2" }); + e.Flyout.Items.Add(new MenuFlyoutItem { Icon = new FontIcon() { Glyph = "\uE8B7" }, Text = "Item 3" }); + } + + private void BreadcrumbBar1_ItemDropDownFlyoutClosed(object sender, BreadcrumbBarItemDropDownFlyoutEventArgs e) + { + e.Flyout.Items.Clear(); + } + + partial void OnIsRTLEnabledChanged(bool newValue) + { + BreadcrumbBar1FlowDirection = IsRTLEnabled ? FlowDirection.RightToLeft : FlowDirection.LeftToRight; + } + } +} diff --git a/tests/Files.App.UITests/Views/MainPage.xaml b/tests/Files.App.UITests/Views/MainPage.xaml new file mode 100644 index 000000000000..44b76623d425 --- /dev/null +++ b/tests/Files.App.UITests/Views/MainPage.xaml @@ -0,0 +1,21 @@ + + + + + + + + + diff --git a/tests/Files.App.UITests/Views/MainPage.xaml.cs b/tests/Files.App.UITests/Views/MainPage.xaml.cs new file mode 100644 index 000000000000..ca9b51c8b97d --- /dev/null +++ b/tests/Files.App.UITests/Views/MainPage.xaml.cs @@ -0,0 +1,15 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Controls; + +namespace Files.App.UITests.Views +{ + public sealed partial class MainPage : Page + { + public MainPage() + { + InitializeComponent(); + } + } +} diff --git a/tests/Files.App.UITests/Views/OmnibarPage.xaml b/tests/Files.App.UITests/Views/OmnibarPage.xaml new file mode 100644 index 000000000000..1e3bae3bd0f7 --- /dev/null +++ b/tests/Files.App.UITests/Views/OmnibarPage.xaml @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/Files.App.UITests/Views/OmnibarPage.xaml.cs b/tests/Files.App.UITests/Views/OmnibarPage.xaml.cs new file mode 100644 index 000000000000..89cf603520c8 --- /dev/null +++ b/tests/Files.App.UITests/Views/OmnibarPage.xaml.cs @@ -0,0 +1,93 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using CommunityToolkit.WinUI; +using Files.App.Controls; +using Files.App.UITests.Data; +using Microsoft.UI.Xaml.Controls; +using System.Collections.ObjectModel; + +namespace Files.App.UITests.Views +{ + public sealed partial class OmnibarPage : Page + { + private readonly string Omnibar1_TextMemberPathForPaletteMode = nameof(OmnibarPaletteSuggestionItem.Title); + + private readonly ObservableCollection Omnibar1_PaletteSuggestions; + private readonly ObservableCollection Omnibar1_BreadcrumbBarItems; + + [GeneratedDependencyProperty(DefaultValue = "")] + private partial string Omnibar1_Text { get; set; } + + [GeneratedDependencyProperty(DefaultValue = "")] + private partial string Omnibar1_TextChangedReason { get; set; } + + [GeneratedDependencyProperty] + private partial int Omnibar1_ChosenSuggestionIndex { get; set; } + + [GeneratedDependencyProperty(DefaultValue = "")] + private partial string Omnibar1_SubmittedQuery { get; set; } + + public OmnibarPage() + { + InitializeComponent(); + + Omnibar1_PaletteSuggestions = + [ + new("Open online help page in browser", "Open online help page in browser", "Control + H"), + new("Toggle full screen", "Toggle full screen", "Control + H"), + new("Enter compact overlay", "Enter compact overlay", "Control + H"), + new("Toggle compact overlay", "Toggle compact overlay", "Control + H"), + new("Go to search box", "Go to search box", "Control + H"), + new("Focus path bar", "Focus path bar", "Control + H"), + new("Redo the last file operation", "Redo the last file operation", "Control + H"), + new("Undo the last file operation", "Undo the last file operation", "Control + H"), + new("Toggle whether to show hidden items", "Toggle whether to show hidden items", "Control + H"), + ]; + + Omnibar1_BreadcrumbBarItems = + [ + new("Local Disk (C:)"), + new("Users"), + new("me"), + new("OneDrive"), + new("Desktop"), + new("Folder1"), + new("Folder2"), + ]; + } + + private void Omnibar1_BreadcrumbBar_ItemDropDownFlyoutOpening(object sender, BreadcrumbBarItemDropDownFlyoutEventArgs e) + { + e.Flyout.Items.Add(new MenuFlyoutItem { Icon = new FontIcon() { Glyph = "\uE8B7" }, Text = "Item 1" }); + e.Flyout.Items.Add(new MenuFlyoutItem { Icon = new FontIcon() { Glyph = "\uE8B7" }, Text = "Item 2" }); + e.Flyout.Items.Add(new MenuFlyoutItem { Icon = new FontIcon() { Glyph = "\uE8B7" }, Text = "Item 3" }); + } + + private void Omnibar1_BreadcrumbBar_ItemDropDownFlyoutClosed(object sender, BreadcrumbBarItemDropDownFlyoutEventArgs e) + { + e.Flyout.Items.Clear(); + } + + private void Omnibar1_QuerySubmitted(Omnibar sender, OmnibarQuerySubmittedEventArgs args) + { + Omnibar1_ChosenSuggestionIndex = args.Item is OmnibarPaletteSuggestionItem item ? Omnibar1_PaletteSuggestions.IndexOf(item) : -1; + Omnibar1_SubmittedQuery = args.Mode.Text ?? string.Empty; + } + + private void Omnibar1_TextChanged(Omnibar sender, OmnibarTextChangedEventArgs args) + { + if (args.Reason is not OmnibarTextChangeReason.SuggestionChosen) + Omnibar1_ChosenSuggestionIndex = -1; + + Omnibar1_Text = args.Mode.Text ?? string.Empty; + Omnibar1_TextChangedReason = args.Reason.ToString(); + } + + private void Omnibar1_SuggestionChosen(Omnibar sender, OmnibarSuggestionChosenEventArgs args) + { + Omnibar1_ChosenSuggestionIndex = args.SelectedItem is OmnibarPaletteSuggestionItem item ? Omnibar1_PaletteSuggestions.IndexOf(item) : -1; + Omnibar1_SubmittedQuery = args.Mode.Text ?? string.Empty; + } + } +} diff --git a/tests/Files.App.UITests/Views/SidebarViewPage.xaml b/tests/Files.App.UITests/Views/SidebarViewPage.xaml new file mode 100644 index 000000000000..dd67c2adbc26 --- /dev/null +++ b/tests/Files.App.UITests/Views/SidebarViewPage.xaml @@ -0,0 +1,9 @@ + diff --git a/tests/Files.App.UITests/Views/SidebarViewPage.xaml.cs b/tests/Files.App.UITests/Views/SidebarViewPage.xaml.cs new file mode 100644 index 000000000000..fd4e70374d35 --- /dev/null +++ b/tests/Files.App.UITests/Views/SidebarViewPage.xaml.cs @@ -0,0 +1,16 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Controls; +using System.Collections.ObjectModel; + +namespace Files.App.UITests.Views +{ + public sealed partial class SidebarViewPage : Page + { + public SidebarViewPage() + { + InitializeComponent(); + } + } +} diff --git a/tests/Files.App.UITests/Views/StorageControlsPage.xaml b/tests/Files.App.UITests/Views/StorageControlsPage.xaml new file mode 100644 index 000000000000..d0acca6f6101 --- /dev/null +++ b/tests/Files.App.UITests/Views/StorageControlsPage.xaml @@ -0,0 +1,334 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/Files.App.UITests/Views/StorageControlsPage.xaml.cs b/tests/Files.App.UITests/Views/StorageControlsPage.xaml.cs new file mode 100644 index 000000000000..85aa38a0769a --- /dev/null +++ b/tests/Files.App.UITests/Views/StorageControlsPage.xaml.cs @@ -0,0 +1,15 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Microsoft.UI.Xaml.Controls; + +namespace Files.App.UITests.Views +{ + public sealed partial class StorageControlsPage : Page + { + public StorageControlsPage() + { + InitializeComponent(); + } + } +} diff --git a/tests/Files.App.UITests/Views/ThemedIconPage.xaml b/tests/Files.App.UITests/Views/ThemedIconPage.xaml new file mode 100644 index 000000000000..fc0f3064eebd --- /dev/null +++ b/tests/Files.App.UITests/Views/ThemedIconPage.xaml @@ -0,0 +1,857 @@ + + + + + +