Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/Models/RepositoryUIStates.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ public bool IsCommitTimeColumnVisibleInHistory
set;
} = true;

// Source-column indexes in display order, including hidden columns.
public List<int> HistoryColumnOrder { get; set; } = [];

public double AuthorColumnWidth
{
get;
Expand Down
6 changes: 6 additions & 0 deletions src/ViewModels/Histories.cs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,12 @@ public GridLength BottomArea
}
}

public List<int> ColumnOrder
{
get => _repo.UIStates.HistoryColumnOrder;
set => _repo.UIStates.HistoryColumnOrder = value;
}

public double AuthorColumnWidth
{
get => _repo.UIStates.AuthorColumnWidth;
Expand Down
31 changes: 18 additions & 13 deletions src/Views/Histories.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,15 @@
<ColumnDefinition Width="{Binding RightArea, Mode=TwoWay}" MinWidth="100"/>
</v:HistoriesLayout.ColumnDefinitions>

<Grid Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3">
<Grid Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" ClipToBounds="True">
<v:HistoriesCommitList x:Name="CommitListContainer"
Classes="static_scrollbar"
ScrollViewer.AllowAutoHide="False"
Background="Transparent"
ItemsSource="{Binding Commits, Mode=OneWay}"
TotalCommits="{Binding Commits.Count, Mode=OneWay}"
SelectedCommits="{Binding SelectedCommits, Mode=TwoWay}"
ColumnDisplayIndexChanged="OnColumnDisplayIndexChanged"
ColumnHeaderHeight="24"
RowHeight="26"
LayoutUpdated="OnCommitListLayoutUpdated"
Expand Down Expand Up @@ -81,9 +82,6 @@
</ControlTemplate>
</Setter>

<Style Selector="^:nth-child(5)">
<Setter Property="AreSeparatorsVisible" Value="False"/>
</Style>
</Style>

<Style Selector="DataGridCell">
Expand All @@ -105,9 +103,7 @@
<DataGrid.Columns>
<DataGridTemplateColumn Width="*" IsReadOnly="True">
<DataGridTemplateColumn.Header>
<Border Padding="48,0">
<TextBlock Classes="table_header" Text="{DynamicResource Text.Histories.Header.GraphAndSubject}" HorizontalAlignment="Center"/>
</Border>
<TextBlock Classes="table_header" Text="{DynamicResource Text.Histories.Header.GraphAndSubject}" HorizontalAlignment="Center"/>
</DataGridTemplateColumn.Header>

<DataGridTemplateColumn.CellTemplate>
Expand Down Expand Up @@ -242,12 +238,21 @@
</DataGrid.Columns>
</v:HistoriesCommitList>

<Border Background="Transparent"
Height="24"
HorizontalAlignment="Stretch" VerticalAlignment="Top"
PointerMoved="OnCommitListHeaderPointerMoved"
PointerPressed="OnCommitListHeaderPointerPressed"
PointerReleased="OnCommitListHeaderPointerReleased">
<Border x:Name="AuthorColumnResizeHandle"
Width="4" Height="24"
Background="Transparent"
Cursor="SizeWestEast"
HorizontalAlignment="Left" VerticalAlignment="Top"
IsVisible="{Binding IsAuthorColumnVisible}"
PointerPressed="OnAuthorResizePressed"
PointerMoved="OnAuthorResizeMoved"
PointerReleased="OnAuthorResizeReleased"
PointerCaptureLost="OnAuthorResizeCaptureLost"/>

<Border x:Name="HistoryHeaderTools"
Background="Transparent"
Width="72" Height="24"
HorizontalAlignment="Left" VerticalAlignment="Top">
<StackPanel Orientation="Horizontal"
Height="18"
Margin="4,0,0,0"
Expand Down
158 changes: 122 additions & 36 deletions src/Views/Histories.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public List<Models.Commit> SelectedCommits
public HistoriesCommitList()
{
SelectionMode = DataGridSelectionMode.Extended;
CanUserReorderColumns = false;
CanUserReorderColumns = true;
CanUserResizeColumns = false;
CanUserSortColumns = false;
AutoGenerateColumns = false;
Expand Down Expand Up @@ -372,6 +372,7 @@ public bool IsDetailsPanelExpanded
public Histories()
{
InitializeComponent();
CommitListContainer.Columns[1].PropertyChanged += OnAuthorColumnPropertyChanged;
}

public async Task GotoParent()
Expand Down Expand Up @@ -465,60 +466,104 @@ protected override void OnDataContextChanged(EventArgs e)
base.OnDataContextChanged(e);

if (DataContext is ViewModels.Histories vm)
{
CommitListContainer.Columns[1].Width = new(vm.AuthorColumnWidth, DataGridLengthUnitType.Pixel);
RestoreColumnOrder(vm);
}
}

private void OnCommitListHeaderPointerMoved(object sender, PointerEventArgs e)
private void RestoreColumnOrder(ViewModels.Histories vm)
{
if (sender is not Border border)
return;

if (DataContext is not ViewModels.Histories { IsAuthorColumnVisible: true } vm)
return;
var columns = CommitListContainer.Columns;
var order = new List<int>();
var seen = new HashSet<int>();
foreach (var index in vm.ColumnOrder ?? [])
{
if (index >= 0 && index < columns.Count && seen.Add(index))
order.Add(index);
}
for (var i = 0; i < columns.Count; i++)
{
if (seen.Add(i))
order.Add(i);
}

var pos = e.GetPosition(border);
if (_resizingAuthorColumn)
_restoringColumnOrder = true;
try
{
var posX = CommitListContainer.Columns[0].ActualWidth;
var maxW = posX + CommitListContainer.Columns[1].ActualWidth - 100;
var delta = posX - pos.X;
var w = Math.Max(Math.Min(vm.AuthorColumnWidth + delta, maxW), 80);
CommitListContainer.Columns[1].Width = new(w, DataGridLengthUnitType.Pixel);
vm.AuthorColumnWidth = w;
for (var i = 0; i < order.Count; i++)
columns[order[i]].DisplayIndex = i;
}
else
finally
{
var dis = CommitListContainer.Columns[0].ActualWidth - 4 - pos.X;
if (dis < 4 && dis > -4)
{
if (border.Cursor != _resizingCursor)
border.Cursor = _resizingCursor;
}
else if (border.Cursor != Cursor.Default)
{
border.Cursor = Cursor.Default;
}
_restoringColumnOrder = false;
}
}

private void OnCommitListHeaderPointerPressed(object sender, PointerPressedEventArgs e)
private void OnColumnDisplayIndexChanged(object sender, DataGridColumnEventArgs e)
{
if (sender is not Border border)
if (_restoringColumnOrder || _pendingColumnOrderSave || DataContext is not ViewModels.Histories)
return;

var pos = e.GetPosition(border);
var dis = CommitListContainer.Columns[0].ActualWidth - 4 - pos.X;
if (dis > 4 || dis < -4)
return;
// DisplayIndex changes arrive while the grid adjusts the remaining columns.
// Save the complete permutation after those updates have finished.
_pendingColumnOrderSave = true;
Avalonia.Threading.Dispatcher.UIThread.Post(() =>
{
_pendingColumnOrderSave = false;
if (DataContext is not ViewModels.Histories vm)
return;

var columns = CommitListContainer.Columns;
var order = new int[columns.Count];
for (var i = 0; i < columns.Count; i++)
order[columns[i].DisplayIndex] = i;
vm.ColumnOrder = [.. order];
});
}

private void OnAuthorColumnPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == DataGridColumn.WidthProperty &&
sender is DataGridColumn column && column.Width.UnitType == DataGridLengthUnitType.Pixel &&
DataContext is ViewModels.Histories vm)
vm.AuthorColumnWidth = column.Width.Value;
}

if (e.GetCurrentPoint(border).Properties.IsLeftButtonPressed)
private void OnAuthorResizePressed(object sender, PointerPressedEventArgs e)
{
if (sender is Border border && DataContext is ViewModels.Histories vm && e.GetCurrentPoint(border).Properties.IsLeftButtonPressed)
{
_authorResizeStartX = e.GetPosition(CommitListContainer).X;
_authorResizeStartWidth = vm.AuthorColumnWidth;
_resizingAuthorColumn = true;
e.Pointer.Capture(border);
e.Handled = true;
}
}

private void OnAuthorResizeMoved(object sender, PointerEventArgs e)
{
if (_resizingAuthorColumn && DataContext is ViewModels.Histories vm)
{
var width = Math.Max(80, _authorResizeStartWidth + _authorResizeStartX - e.GetPosition(CommitListContainer).X);
CommitListContainer.Columns[1].Width = new(width, DataGridLengthUnitType.Pixel);
vm.AuthorColumnWidth = width;
e.Handled = true;
}
}

private void OnCommitListHeaderPointerReleased(object sender, PointerReleasedEventArgs e)
private void OnAuthorResizeReleased(object sender, PointerReleasedEventArgs e)
{
if (_resizingAuthorColumn)
{
_resizingAuthorColumn = false;
e.Pointer.Capture(null);
e.Handled = true;
}
}

private void OnAuthorResizeCaptureLost(object sender, PointerCaptureLostEventArgs e)
{
_resizingAuthorColumn = false;
}
Expand Down Expand Up @@ -778,7 +823,45 @@ private void OnCommitListLayoutUpdated(object _1, EventArgs _2)

IsScrollToTopVisible = startY >= rowHeight;

var clipWidth = dataGrid.Columns[0].ActualWidth - 4;
var graphColumn = dataGrid.Columns[0];
double graphOffset = 0;
foreach (var column in dataGrid.Columns)
{
if (column.IsVisible && column.DisplayIndex < graphColumn.DisplayIndex)
graphOffset += column.ActualWidth;
}
if (Math.Abs(CommitGraph.Margin.Left - graphOffset) > 0.01)
CommitGraph.Margin = new Thickness(graphOffset, 24, 0, 0);

// Keep the tools at the left edge and reserve space in the first visible header.
DataGridColumn firstColumn = null;
foreach (var column in dataGrid.Columns)
{
if (column.IsVisible && (firstColumn == null || column.DisplayIndex < firstColumn.DisplayIndex))
firstColumn = column;
}
foreach (var column in dataGrid.Columns)
{
if (column.Header is Control header)
{
var margin = column == firstColumn ? new Thickness(HistoryHeaderTools.Width, 0, 0, 0) : default;
if (header.Margin != margin)
header.Margin = margin;
}
}

var authorColumn = dataGrid.Columns[1];
double authorOffset = 0;
foreach (var column in dataGrid.Columns)
{
if (column.IsVisible && column.DisplayIndex < authorColumn.DisplayIndex)
authorOffset += column.ActualWidth;
}
var resizeOffset = Math.Max(0, authorOffset - 2);
if (Math.Abs(AuthorColumnResizeHandle.Margin.Left - resizeOffset) > 0.01)
AuthorColumnResizeHandle.Margin = new Thickness(resizeOffset, 0, 0, 0);

var clipWidth = Math.Max(0, graphColumn.ActualWidth - 4);
var lastLayout = CommitGraph.Layout;
if (lastLayout == null ||
Math.Abs(lastLayout.StartY - startY) > 0.01 ||
Expand Down Expand Up @@ -1970,6 +2053,9 @@ private async Task InteractiveRebaseWithPrefillActionAsync(ViewModels.Repository
private bool _isScrollToTopVisible = false;
private bool _isDetailsPanelExpanded = true;
private bool _resizingAuthorColumn = false;
private Cursor _resizingCursor = new(StandardCursorType.SizeWestEast);
private double _authorResizeStartX = 0;
private double _authorResizeStartWidth = 0;
private bool _restoringColumnOrder = false;
private bool _pendingColumnOrderSave = false;
}
}