Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
fc3d2c6
实现打开zip文件选择编码。目前问题:不显示文件夹,目录结构被展平
oxygen-dioxide May 18, 2026
d31bcaf
正常显示文件夹
oxygen-dioxide May 29, 2026
722fb3c
可复制、打开文件,仍然无法打开子文件夹的文件
oxygen-dioxide May 29, 2026
50cef74
Merge branch 'files-community:main' into zip-encoding-oc
oxygen-dioxide May 31, 2026
acaaa1a
编码选择框移动到右下角
oxygen-dioxide Jun 1, 2026
851da52
formatting
oxygen-dioxide Jun 1, 2026
6ca109c
Merge branch 'files-community:main' into zip-encoding-oc
oxygen-dioxide Jun 3, 2026
d0cb35f
Improved styling
yair100 Jun 4, 2026
18a2bb2
回退src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs
oxygen-dioxide Jun 5, 2026
b6881b0
ascii文件不显示编码选择框,处理非内置编码
oxygen-dioxide Jun 6, 2026
af1d4d8
Change CurrentEncoding property to non-static
oxygen-dioxide Jun 7, 2026
814c90e
多zip编码管理。bug:切换标签页时变成null
oxygen-dioxide Jun 8, 2026
8f1018a
修复切换页面时zip编码变为null
oxygen-dioxide Jun 22, 2026
3d0b867
formatting
oxygen-dioxide Jun 22, 2026
f527168
修复打开zip文件时编码菜单编码不显示选中项
oxygen-dioxide Jun 23, 2026
df1ec06
Merge branch 'files-community:main' into zip-encoding-oc
oxygen-dioxide Jun 24, 2026
add308e
Merge branch 'files-community:main' into zip-encoding-oc
oxygen-dioxide Jun 25, 2026
53f5dc4
GetBasicProperties: 支持不同编码
oxygen-dioxide Jun 25, 2026
d4dfc84
支持加密压缩包
oxygen-dioxide Jun 25, 2026
d234cfa
Merge branch 'files-community:main' into zip-encoding-oc
oxygen-dioxide Jun 26, 2026
5cf388b
修复复制文件夹仅包含一个文件的bug
oxygen-dioxide Jun 27, 2026
97b252e
Merge branch 'files-community:main' into zip-encoding-oc
oxygen-dioxide Jun 27, 2026
ae688dd
修复当用户设置使用系统默认编码时,切换到子文件夹再切换回来,重新打开编码菜单时编码被检测结果重新覆盖的bug
oxygen-dioxide Jun 28, 2026
b712c0e
Merge branch 'files-community:main' into zip-encoding-oc
oxygen-dioxide Jun 28, 2026
0f10498
SelectedItem使用OneWay
oxygen-dioxide Jun 28, 2026
25c806e
Merge branch 'files-community:main' into zip-encoding-oc
oxygen-dioxide Jun 29, 2026
060486d
Merge branch 'files-community:main' into zip-encoding-oc
oxygen-dioxide Jun 29, 2026
c186541
Merge branch 'files-community:main' into zip-encoding-oc
oxygen-dioxide Jul 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/Files.App.Launcher/Files.App.Launcher.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions);_SILENCE_EXPERIMENTAL_COROUTINE_DEPRECATION_WARNINGS</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
</ItemDefinitionGroup>
Expand All @@ -103,7 +103,7 @@
<ClCompile>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions);_SILENCE_EXPERIMENTAL_COROUTINE_DEPRECATION_WARNINGS</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
Expand Down
2 changes: 1 addition & 1 deletion src/Files.App/Data/Items/EncodingItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public EncodingItem(Encoding encoding, string name)
//reference: https://en.wikipedia.org/wiki/Windows_code_page
//East Asian
"shift_jis", //Japanese
"gb2312", //Simplified Chinese
"gb18030", //Simplified Chinese
"big5", //Traditional Chinese
"ks_c_5601-1987", //Korean

Expand Down
14 changes: 14 additions & 0 deletions src/Files.App/Data/Models/CurrentInstanceViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,20 @@ public bool IsPageTypeLibrary
}
}

private string? zipEncodingName;
public string? ZipEncodingName
{
get => zipEncodingName;
set => SetProperty(ref zipEncodingName, value);
}

private bool isZipEncodingUndetermined;
public bool IsZipEncodingUndetermined
{
get => isZipEncodingUndetermined;
set => SetProperty(ref isZipEncodingUndetermined, value);
}

public bool CanCopyPathInPage
{
get => !isPageTypeMtpDevice && !isPageTypeRecycleBin && isPageTypeNotHome && !isPageTypeSearchResults && !IsPageTypeReleaseNotes && !IsPageTypeSettings;
Expand Down
191 changes: 191 additions & 0 deletions src/Files.App/Data/Models/DirectoryPropertiesViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
// Copyright (c) Files Community
// Licensed under the MIT License.

using Files.App.Data.Items;
using Files.App.Utils.Storage;
using Files.Shared.Helpers;
using Microsoft.Extensions.Logging;
using System.ComponentModel;
using System.Text;
using System.Windows.Input;

namespace Files.App.ViewModels.UserControls
Expand All @@ -9,6 +15,8 @@ public sealed partial class StatusBarViewModel : ObservableObject
{
private IContentPageContext ContentPageContext { get; } = Ioc.Default.GetRequiredService<IContentPageContext>();
private IDevToolsSettingsService DevToolsSettingsService = Ioc.Default.GetRequiredService<IDevToolsSettingsService>();
private readonly IStorageArchiveService StorageArchiveService = Ioc.Default.GetRequiredService<IStorageArchiveService>();
private CurrentInstanceViewModel? InstanceViewModel => ContentPageContext.ShellPage?.InstanceViewModel;

// The first branch will always be the active one.
public const int ACTIVE_BRANCH_INDEX = 0;
Expand Down Expand Up @@ -81,6 +89,30 @@ public string ExtendedStatusInfo
set => SetProperty(ref _ExtendedStatusInfo, value);
}

private bool _IsZipEncodingSelectorVisible;
public bool IsZipEncodingSelectorVisible
{
get => _IsZipEncodingSelectorVisible;
set => SetProperty(ref _IsZipEncodingSelectorVisible, value);
}

public List<EncodingItem> ZipEncodingOptions { get; } = EncodingItem.Defaults.ToList();

private EncodingItem? _SelectedZipEncoding;
public EncodingItem? SelectedZipEncoding
{
get => _SelectedZipEncoding;
set
{
if(value is null)
{
return;
}
if (SetProperty(ref _SelectedZipEncoding, value))
_ = OnZipEncodingChangedAsync(value);
}
}

public bool ShowOpenInIDEButton
{
get
Expand Down Expand Up @@ -112,6 +144,24 @@ public StatusBarViewModel()
break;
}
};

SubscribeToShellPage();
ContentPageContext.PropertyChanged += OnContentPageContextPropertyChanged;
}

private void OnContentPageContextPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(ContentPageContext.ShellPage))
{
UnsubscribeFromInstanceViewModel();
SubscribeToShellPage();
}
}

private void SubscribeToShellPage()
{
SubscribeToInstanceViewModel();
_ = UpdateZipEncodingStateAsync();
}

public void UpdateGitInfo(bool isGitRepository, string? repositoryPath, BranchItem? head)
Expand Down Expand Up @@ -164,5 +214,146 @@ public Task ExecuteDeleteBranch(string? branchName)
{
return GitHelpers.DeleteBranchAsync(_gitRepositoryPath, GitBranchDisplayName, branchName);
}

public async Task UpdateZipEncodingStateAsync()
{
if (ContentPageContext.ShellPage?.SlimContentPage?.StatusBarViewModel != this)
return;

var instanceVM = InstanceViewModel;
if (instanceVM is null)
return;

if (!instanceVM.IsPageTypeZipFolder)
{
IsZipEncodingSelectorVisible = false;
return;
}

var workingDir = ContentPageContext.ShellPage?.ShellViewModel.WorkingDirectory;
if (string.IsNullOrEmpty(workingDir) || !ZipStorageFolder.IsZipPath(workingDir))
return;

if (TryRestoreZipEncodingFromContainerPath(workingDir))
return;

try
{
var isUndetermined = await StorageArchiveService.IsEncodingUndeterminedAsync(workingDir);
instanceVM.IsZipEncodingUndetermined = isUndetermined;

if (!isUndetermined)
{
IsZipEncodingSelectorVisible = false;
return;
}

var detected = await StorageArchiveService.DetectEncodingAsync(workingDir);
if (detected is not null)
{
instanceVM.ZipEncodingName = detected.WebName;
EncodingItem? ZipEncodingItem = ZipEncodingOptions.FirstOrDefault(e =>
e.Encoding?.WebName.Equals(detected.WebName, StringComparison.OrdinalIgnoreCase) == true);
if(ZipEncodingItem == null)
{
ZipEncodingItem = new EncodingItem(detected, detected.EncodingName);
}
ZipEncodingOptions.Add(ZipEncodingItem);
SelectedZipEncoding = ZipEncodingItem;
}
else
{
instanceVM.ZipEncodingName = null;
SelectedZipEncoding = ZipEncodingOptions.FirstOrDefault(e => e.Encoding is null);
}

IsZipEncodingSelectorVisible = true;
}
catch (Exception ex)
{
App.Logger.LogError(ex, "Error checking zip encoding.");
IsZipEncodingSelectorVisible = false;
}
}

private bool TryRestoreZipEncodingFromContainerPath(string workingDir)
{
if (!FileExtensionHelpers.IsBrowsableZipFile(workingDir, out var ext))
return false;

var marker = workingDir.IndexOf(ext, StringComparison.OrdinalIgnoreCase);
if (marker is -1)
return false;

var containerPath = workingDir.Substring(0, marker + ext.Length);
if (!ZipStorageFolder.TryGetEncodingForContainerPath(containerPath, out var encoding))
return false;

EncodingItem? match = encoding is not null
? ZipEncodingOptions.FirstOrDefault(e => e.Encoding == encoding)
: ZipEncodingOptions.FirstOrDefault(e => e.Encoding is null);

if (match is null && encoding is not null)
{
match = new EncodingItem(encoding, encoding.EncodingName);
ZipEncodingOptions.Add(match);
}

if (match is not null)
{
SelectedZipEncoding = match;
IsZipEncodingSelectorVisible = true;
return true;
}

return false;
}

private async Task OnZipEncodingChangedAsync(EncodingItem encodingItem)
{
if (ContentPageContext.ShellPage is null)
return;

if (ContentPageContext.ShellPage?.SlimContentPage?.StatusBarViewModel != this)
return;

var workingDir = ContentPageContext.ShellPage.ShellViewModel.WorkingDirectory;
if (string.IsNullOrEmpty(workingDir))
return;

if (FileExtensionHelpers.IsBrowsableZipFile(workingDir, out var ext))
{
var marker = workingDir.IndexOf(ext, StringComparison.OrdinalIgnoreCase);
if (marker is not -1)
{
var containerPath = workingDir.Substring(0, marker + ext.Length);
ZipStorageFolder.SetEncodingForContainerPath(containerPath, encodingItem.Encoding);
}
}

ContentPageContext.ShellPage.ShellViewModel.RefreshItems(null);
}

internal void SubscribeToInstanceViewModel()
{
var instanceVM = InstanceViewModel;
if (instanceVM is not null)
instanceVM.PropertyChanged += OnInstanceViewModelPropertyChanged;
}

internal void UnsubscribeFromInstanceViewModel()
{
var instanceVM = InstanceViewModel;
if (instanceVM is not null)
instanceVM.PropertyChanged -= OnInstanceViewModelPropertyChanged;
}

private void OnInstanceViewModelPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName is nameof(CurrentInstanceViewModel.IsPageTypeZipFolder))
{
_ = UpdateZipEncodingStateAsync();
}
}
}
}
4 changes: 3 additions & 1 deletion src/Files.App/Services/Storage/StorageArchiveService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,9 @@ public async Task<bool> IsEncodingUndeterminedAsync(string archiveFilePath)
{
using (ZipFile zipFile = new ZipFile(archiveFilePath))
{
return !zipFile.Cast<ZipEntry>().All(entry => entry.IsUnicodeText);
return !zipFile.Cast<ZipEntry>().All(
entry => entry.IsUnicodeText || entry.Name.All(c => char.IsAscii(c))
);
}
}
catch (Exception ex)
Expand Down
50 changes: 49 additions & 1 deletion src/Files.App/UserControls/StatusBar.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>

<!-- Folder and selection info -->
Expand Down Expand Up @@ -193,10 +194,57 @@
Background="{ThemeResource DividerStrokeColorDefaultBrush}" />
</StackPanel>

<!-- ZIP Encoding Selector -->
<Button
x:Name="ZipEncodingSelector"
Grid.Column="2"
Height="24"
Padding="8,0,8,0"
Background="Transparent"
BorderBrush="Transparent"
ToolTipService.ToolTip="{helpers:ResourceString Name=Encoding}"
Visibility="{x:Bind StatusBarViewModel.IsZipEncodingSelectorVisible, Mode=OneWay}">

<!-- Encoding Name -->
<TextBlock Text="{x:Bind StatusBarViewModel.SelectedZipEncoding.Name, Mode=OneWay, FallbackValue='', TargetNullValue=''}" />

<Button.Flyout>
<Flyout x:Name="ZipEncodingFlyout" Opening="ZipEncodingFlyout_Opening">
<Grid
Width="240"
Height="300"
Margin="-16">

<!-- Encodings List -->
<ListView
x:Name="ZipEncodingList"
Padding="4"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
IsItemClickEnabled="True"
ItemClick="ZipEncodingList_ItemClick"
ItemsSource="{x:Bind StatusBarViewModel.ZipEncodingOptions, Mode=OneWay}"
SelectedItem="{x:Bind StatusBarViewModel.SelectedZipEncoding, Mode=TwoWay}"
SelectionMode="Single">

<ListView.ItemTemplate>
<DataTemplate x:DataType="data:EncodingItem">
<TextBlock
VerticalAlignment="Center"
Text="{x:Bind Name}"
TextTrimming="CharacterEllipsis" />
</DataTemplate>
</ListView.ItemTemplate>

</ListView>
</Grid>
</Flyout>
</Button.Flyout>
</Button>

<!-- Use visibility because it causes a crash to use a TwoWay x:Bind on an element that is inside an element with x:Load (#12589, #12599) -->
<Button
x:Name="GitBranch"
Grid.Column="2"
Grid.Column="3"
Height="24"
Margin="4,0,0,0"
Padding="8,0,8,0"
Expand Down
15 changes: 15 additions & 0 deletions src/Files.App/UserControls/StatusBar.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,26 @@ private async void BranchesFlyout_Opening(object _, object e)
StatusBarViewModel.SelectedBranchIndex = StatusBarViewModel.ACTIVE_BRANCH_INDEX;
}

private async void ZipEncodingFlyout_Opening(object _, object e)
{
if (StatusBarViewModel is null)
return;

await StatusBarViewModel.UpdateZipEncodingStateAsync();
if (StatusBarViewModel.SelectedZipEncoding is not null)
ZipEncodingList.SelectedItem = StatusBarViewModel.SelectedZipEncoding;
}

private void BranchesList_ItemClick(object sender, ItemClickEventArgs e)
{
BranchesFlyout.Hide();
}

private void ZipEncodingList_ItemClick(object sender, ItemClickEventArgs e)
{
ZipEncodingFlyout.Hide();
}

private void BranchesFlyout_Closing(object _, object e)
{
if (StatusBarViewModel is null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ async Task<TOut> RetryWithCredentialsAsync<TOut>(Func<Task<TOut>> func, Exceptio
{
var handled = exception is SevenZipOpenFailedException szofex && szofex.Result is OperationResult.WrongPassword ||
exception is ExtractionFailedException efex && efex.Result is OperationResult.WrongPassword ||
exception is FtpAuthenticationException;
exception is FtpAuthenticationException ||
exception is ICSharpCode.SharpZipLib.Zip.ZipException szlzex && szlzex.Message.Contains("encrypted");

if (!handled || PasswordRequestedCallback is null)
throw exception;
Expand Down
Loading
Loading