Skip to content

Fix: Fixed "Size All Columns to Fit" not working properly #11437

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Feb 28, 2023
Merged
2 changes: 1 addition & 1 deletion src/Files.App/Views/LayoutModes/DetailsLayoutBrowser.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,7 @@
<Grid
x:Name="ItemTagGrid"
Width="{Binding ColumnsViewModel.TagColumn.LengthIncludingGridSplitter.Value, ElementName=PageRoot, Mode=OneWay}"
Padding="0,0,12,0"
Padding="12,0,12,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Visibility="{Binding ColumnsViewModel.TagColumn.Visibility, ElementName=PageRoot, Mode=OneWay}">
Expand Down
103 changes: 73 additions & 30 deletions src/Files.App/Views/LayoutModes/DetailsLayoutBrowser.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ private void ToggleMenuFlyoutItem_Click(object sender, RoutedEventArgs e)

private void GridSplitter_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
var columnToResize = (Grid.GetColumn(sender as CommunityToolkit.WinUI.UI.Controls.GridSplitter) - 1) / 2;
var columnToResize = Grid.GetColumn(sender as CommunityToolkit.WinUI.UI.Controls.GridSplitter) / 2;
ResizeColumnToFit(columnToResize);
e.Handled = true;
}
Expand All @@ -582,7 +582,7 @@ private void ResizeColumnToFit(int columnToResize)
{
1 => 40, // Check all items columns
2 => FileList.Items.Cast<ListedItem>().Select(x => x.Name?.Length ?? 0).Max(), // file name column
3 => FileList.Items.Cast<ListedItem>().Select(x => x.FileTagsUI?.FirstOrDefault()?.Name?.Length ?? 0).Max(), // file tag column
3 => FileList.Items.Cast<ListedItem>().Select(x => x.FileTagsUI?.Sum(x => x?.Name?.Length ?? 0) ?? 0).Max(), // file tag column
4 => FileList.Items.Cast<ListedItem>().Select(x => (x as RecycleBinItem)?.ItemOriginalPath?.Length ?? 0).Max(), // original path column
5 => FileList.Items.Cast<ListedItem>().Select(x => (x as RecycleBinItem)?.ItemDateDeleted?.Length ?? 0).Max(), // date deleted column
6 => FileList.Items.Cast<ListedItem>().Select(x => x.ItemDateModified?.Length ?? 0).Max(), // date modified column
Expand All @@ -597,7 +597,8 @@ private void ResizeColumnToFit(int columnToResize)
if (maxItemLength == 0)
return;

var columnSizeToFit = columnToResize == 10 ? maxItemLength : MeasureTextColumnEstimate(columnToResize, 5, maxItemLength);
var columnSizeToFit = MeasureColumnEstimate(columnToResize, 5, maxItemLength);

if (columnSizeToFit > 1)
{
var column = columnToResize switch
Expand All @@ -613,7 +614,7 @@ private void ResizeColumnToFit(int columnToResize)
_ => ColumnsViewModel.StatusColumn
};

if (columnToResize == 1) // file name column
if (columnToResize == 2) // file name column
columnSizeToFit += 20;

var minFitLength = Math.Max(columnSizeToFit, column.NormalMinLength);
Expand All @@ -625,48 +626,90 @@ private void ResizeColumnToFit(int columnToResize)
FolderSettings.ColumnsViewModel = ColumnsViewModel;
}

private double MeasureTextColumnEstimate(int columnIndex, int measureItemsCount, int maxItemLength)
private double MeasureColumnEstimate(int columnIndex, int measureItemsCount, int maxItemLength)
{
if (columnIndex == 10)
return maxItemLength;

if (columnIndex == 3)
return MeasureTagColumnEstimate(columnIndex);

return MeasureTextColumnEstimate(columnIndex, measureItemsCount, maxItemLength);
}

private double MeasureTagColumnEstimate(int columnIndex)
{
var tbs = DependencyObjectHelpers.FindChildren<TextBlock>(FileList.ItemsPanelRoot).Where(tb =>
var grids = DependencyObjectHelpers
.FindChildren<Grid>(FileList.ItemsPanelRoot)
.Where(grid => IsCorrectColumn(grid, columnIndex));

// Get the list of stack panels with the most letters
var stackPanels = grids
.Select(DependencyObjectHelpers.FindChildren<StackPanel>)
.OrderByDescending(sps => sps.Select(sp => DependencyObjectHelpers.FindChildren<TextBlock>(sp).Select(tb => tb.Text.Length).Sum()).Sum())
.First()
.ToArray();

var mesuredSize = stackPanels.Select(x =>
{
int columnIndexFromName = tb.Name switch
{
"ItemName" => 1,
"ItemTag" => 2,
"ItemOriginalPath" => 3,
"ItemDateDeleted" => 4,
"ItemDateModified" => 5,
"ItemDateCreated" => 6,
"ItemType" => 7,
"ItemSize" => 8,
"ItemStatus" => 9,
_ => -1,
};
x.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));

if (columnIndexFromName == -1)
return false;
return x.DesiredSize.Width;
}).Sum();

return columnIndexFromName == columnIndex;
});
if (stackPanels.Length >= 2)
mesuredSize += 4 * (stackPanels.Length - 1); // The spacing between the tags

return mesuredSize;
}

private double MeasureTextColumnEstimate(int columnIndex, int measureItemsCount, int maxItemLength)
{
var tbs = DependencyObjectHelpers
.FindChildren<TextBlock>(FileList.ItemsPanelRoot)
.Where(tb => IsCorrectColumn(tb, columnIndex));

// heuristic: usually, text with more letters are wider than shorter text with wider letters
// with this, we can calculate avg width using longest text(s) to avoid overshooting the width
var widthPerLetter = tbs.OrderByDescending(x => x.Text.Length).Where(tb => !string.IsNullOrEmpty(tb.Text)).Take(measureItemsCount).Select(tb =>
{
var sampleTb = new TextBlock { Text = tb.Text, FontSize = tb.FontSize, FontFamily = tb.FontFamily };
sampleTb.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
var widthPerLetter = tbs
.OrderByDescending(x => x.Text.Length)
.Where(tb => !string.IsNullOrEmpty(tb.Text))
.Take(measureItemsCount)
.Select(tb =>
{
var sampleTb = new TextBlock { Text = tb.Text, FontSize = tb.FontSize, FontFamily = tb.FontFamily };
sampleTb.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));

return sampleTb.DesiredSize.Width / Math.Max(1, tb.Text.Length);
});
return sampleTb.DesiredSize.Width / Math.Max(1, tb.Text.Length);
});

if (!widthPerLetter.Any())
return 0;

// take weighted avg between mean and max since width is an estimate
// Take weighted avg between mean and max since width is an estimate
var weightedAvg = (widthPerLetter.Average() + widthPerLetter.Max()) / 2;
return weightedAvg * maxItemLength;
}

private bool IsCorrectColumn(FrameworkElement element, int columnIndex)
{
int columnIndexFromName = element.Name switch
{
"ItemName" => 2,
"ItemTagGrid" => 3,
"ItemOriginalPath" => 4,
"ItemDateDeleted" => 5,
"ItemDateModified" => 6,
"ItemDateCreated" => 7,
"ItemType" => 8,
"ItemSize" => 9,
"ItemStatus" => 10,
_ => -1,
};

return columnIndexFromName != -1 && columnIndexFromName == columnIndex;
}

private void FileList_Loaded(object sender, RoutedEventArgs e)
{
ContentScroller = FileList.FindDescendant<ScrollViewer>(x => x.Name == "ScrollViewer");
Expand Down