Skip to content

Commit 6677362

Browse files
committed
feature: add focused history layout
1 parent bd97d06 commit 6677362

21 files changed

Lines changed: 601 additions & 47 deletions

‎src/Models/HistoryLayout.cs‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace SourceGit.Models
2+
{
3+
public enum HistoryLayout
4+
{
5+
Stacked,
6+
SideBySide,
7+
Focused,
8+
}
9+
}

‎src/Models/RepositoryUIStates.cs‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ public double AuthorColumnWidth
4545
set;
4646
} = 120;
4747

48+
public double FocusedHistoryDetailsWidth
49+
{
50+
get;
51+
set;
52+
} = 340;
53+
4854
public bool EnableTopoOrderInHistory
4955
{
5056
get;

‎src/Resources/Locales/en_US.axaml‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,8 @@
515515
<x:String x:Key="Text.GitLFS.TrackByExtension" xml:space="preserve">Track all *{0} files</x:String>
516516
<x:String x:Key="Text.GotoRevisionSelector" xml:space="preserve">Select Commit</x:String>
517517
<x:String x:Key="Text.Histories.AdvancedOptions" xml:space="preserve">Advanced Options</x:String>
518+
<x:String x:Key="Text.Histories.BackToGraph" xml:space="preserve">← Graph</x:String>
519+
<x:String x:Key="Text.Histories.CopyCommitSHA" xml:space="preserve">Copy full commit SHA</x:String>
518520
<x:String x:Key="Text.Histories.Header.Author" xml:space="preserve">AUTHOR</x:String>
519521
<x:String x:Key="Text.Histories.Header.AuthorTime" xml:space="preserve">AUTHOR TIME</x:String>
520522
<x:String x:Key="Text.Histories.Header.CommitTime" xml:space="preserve">COMMIT TIME</x:String>
@@ -527,6 +529,7 @@
527529
<x:String x:Key="Text.Histories.HighlightsInGraph.SelectedCommitsOnly" xml:space="preserve">Selected Commits Only</x:String>
528530
<x:String x:Key="Text.Histories.HighlightsInGraph.SelectedCommitsOnlyFirstParent" xml:space="preserve">Selected Commits (First-parent Only)</x:String>
529531
<x:String x:Key="Text.Histories.Selected" xml:space="preserve">SELECTED {0} COMMITS</x:String>
532+
<x:String x:Key="Text.Histories.SelectionDetails" xml:space="preserve">DETAILS</x:String>
530533
<x:String x:Key="Text.Histories.ShowColumns" xml:space="preserve">SHOW COLUMNS</x:String>
531534
<x:String x:Key="Text.Histories.Tips" xml:space="preserve">Hold 'Ctrl' or 'Shift' to select multiple commits.</x:String>
532535
<x:String x:Key="Text.Histories.Tips.MacOS" xml:space="preserve">Hold ⌘ or ⇧ to select multiple commits.</x:String>
@@ -822,6 +825,7 @@
822825
<x:String x:Key="Text.Repository.FilterCommits.Include" xml:space="preserve">Filter in commit graph</x:String>
823826
<x:String x:Key="Text.Repository.FilterCommits.Summary" xml:space="preserve">filter(s) enabled</x:String>
824827
<x:String x:Key="Text.Repository.HistoriesLayout" xml:space="preserve">LAYOUT</x:String>
828+
<x:String x:Key="Text.Repository.HistoriesLayout.Focused" xml:space="preserve">Focused</x:String>
825829
<x:String x:Key="Text.Repository.HistoriesLayout.Horizontal" xml:space="preserve">Horizontal</x:String>
826830
<x:String x:Key="Text.Repository.HistoriesLayout.Vertical" xml:space="preserve">Vertical</x:String>
827831
<x:String x:Key="Text.Repository.HistoriesOrder" xml:space="preserve">COMMITS ORDER</x:String>

‎src/ViewModels/CommitDetail.cs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -539,9 +539,9 @@ private void Refresh()
539539
}, token);
540540
}
541541

542-
private void UpdateDetails()
542+
public void UpdateDetails()
543543
{
544-
if (ActiveTabIndex == 1 && _changeSelection is { Count: 1, HasFolder: false })
544+
if ((ActiveTabIndex == 1 || Preferences.Instance.IsFocusedHistoryLayout) && _changeSelection is { Count: 1, HasFolder: false })
545545
DiffContext = new DiffContext(_repo.FullPath, new Models.DiffOption(_commit, _changeSelection.Changes[0]), _diffContext);
546546
else
547547
DiffContext = null;

‎src/ViewModels/Histories.cs‎

Lines changed: 102 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.ComponentModel;
34
using System.IO;
45
using System.Threading.Tasks;
56

@@ -149,10 +150,92 @@ public object DetailContext
149150
set
150151
{
151152
if (SetProperty(ref _detailContext, value))
153+
{
154+
ShowGraph();
155+
ObserveDetailContext(value);
152156
OnPropertyChanged(nameof(IsOpenAsStandaloneVisible));
157+
}
158+
}
159+
}
160+
161+
public DiffContext FocusedDiff
162+
{
163+
get => _focusedDiff;
164+
private set
165+
{
166+
if (SetProperty(ref _focusedDiff, value))
167+
OnPropertyChanged(nameof(IsFocusedDiffVisible));
153168
}
154169
}
155170

171+
public string FocusedDiffPath
172+
{
173+
get => _focusedDiffPath;
174+
private set => SetProperty(ref _focusedDiffPath, value);
175+
}
176+
177+
public bool IsFocusedDiffVisible => Preferences.Instance.IsFocusedHistoryLayout && FocusedDiff != null;
178+
179+
public void OpenFocusedDiff()
180+
{
181+
if (!Preferences.Instance.IsFocusedHistoryLayout)
182+
return;
183+
184+
ChangeSelection selection;
185+
DiffContext diff;
186+
if (DetailContext is CommitDetail detail)
187+
{
188+
selection = detail.ChangeSelection;
189+
if (detail.DiffContext == null)
190+
detail.UpdateDetails();
191+
diff = detail.DiffContext;
192+
}
193+
else if (DetailContext is RevisionCompare compare)
194+
{
195+
selection = compare.ChangeSelection;
196+
diff = compare.DiffContext;
197+
}
198+
else
199+
{
200+
return;
201+
}
202+
203+
if (selection is { Count: 1, HasFolder: false } && diff != null)
204+
{
205+
FocusedDiffPath = selection.Changes[0].Path;
206+
FocusedDiff = diff;
207+
}
208+
}
209+
210+
public void ShowGraph()
211+
{
212+
FocusedDiff = null;
213+
FocusedDiffPath = string.Empty;
214+
}
215+
216+
public void UpdateLayout()
217+
{
218+
ShowGraph();
219+
OnPropertyChanged(nameof(RightArea));
220+
OnPropertyChanged(nameof(BottomArea));
221+
}
222+
223+
private void ObserveDetailContext(object context)
224+
{
225+
if (_observedDetailContext != null)
226+
_observedDetailContext.PropertyChanged -= OnDetailContextPropertyChanged;
227+
_observedDetailContext = context as INotifyPropertyChanged;
228+
if (_observedDetailContext != null)
229+
_observedDetailContext.PropertyChanged += OnDetailContextPropertyChanged;
230+
}
231+
232+
private void OnDetailContextPropertyChanged(object sender, PropertyChangedEventArgs e)
233+
{
234+
if (e.PropertyName is nameof(CommitDetail.Commit) or nameof(RevisionCompare.IsLoading) or
235+
nameof(RevisionCompare.StartPoint) or nameof(RevisionCompare.EndPoint))
236+
ShowGraph();
237+
}
238+
156239
public Models.Bisect Bisect
157240
{
158241
get => _bisect;
@@ -184,8 +267,20 @@ public GridLength LeftArea
184267

185268
public GridLength RightArea
186269
{
187-
get => _rightArea;
188-
set => SetProperty(ref _rightArea, value);
270+
get => Preferences.Instance.IsFocusedHistoryLayout ? new GridLength(_repo.UIStates.FocusedHistoryDetailsWidth) : _rightArea;
271+
set
272+
{
273+
if (Preferences.Instance.IsFocusedHistoryLayout)
274+
{
275+
if (value.IsAbsolute && value.Value >= 260)
276+
{
277+
_repo.UIStates.FocusedHistoryDetailsWidth = value.Value;
278+
OnPropertyChanged();
279+
}
280+
}
281+
else
282+
SetProperty(ref _rightArea, value);
283+
}
189284
}
190285

191286
public GridLength TopArea
@@ -199,7 +294,7 @@ public GridLength BottomArea
199294
get => _isCollapseDetails ? new GridLength(28, GridUnitType.Pixel) : _bottomArea;
200295
set
201296
{
202-
if (!Preferences.Instance.UseTwoColumnsLayoutInHistories && !_isCollapseDetails)
297+
if (!Preferences.Instance.IsHorizontalHistoryLayout && !_isCollapseDetails)
203298
SetProperty(ref _bottomArea, value);
204299
}
205300
}
@@ -220,7 +315,7 @@ public bool IsCollapseDetails
220315
get => _isCollapseDetails;
221316
set
222317
{
223-
if (!Preferences.Instance.UseTwoColumnsLayoutInHistories && SetProperty(ref _isCollapseDetails, value))
318+
if (!Preferences.Instance.IsHorizontalHistoryLayout && SetProperty(ref _isCollapseDetails, value))
224319
{
225320
OnPropertyChanged(nameof(TopArea));
226321
OnPropertyChanged(nameof(BottomArea));
@@ -588,6 +683,9 @@ private void GenerateGraph(List<Models.Commit> commits)
588683
private Models.Bisect _bisect = null;
589684
private object _detailContext = Models.Null.Instance;
590685
private bool _ignoreSelectionChange = false;
686+
private INotifyPropertyChanged _observedDetailContext = null;
687+
private DiffContext _focusedDiff = null;
688+
private string _focusedDiffPath = string.Empty;
591689

592690
private bool _isSearchingCommits = false;
593691
private SearchCommitContext _searchCommitContext = null;

‎src/ViewModels/Preferences.cs‎

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,40 @@ public bool UseCompactBranchNamesInGraph
213213

214214
public bool UseTwoColumnsLayoutInHistories
215215
{
216-
get => _useTwoColumnsLayoutInHistories;
217-
set => SetProperty(ref _useTwoColumnsLayoutInHistories, value);
216+
get => IsHorizontalHistoryLayout;
217+
set
218+
{
219+
// Keep reading older preference files. The explicit layout takes precedence,
220+
// regardless of the order of properties in the JSON document.
221+
if (SetProperty(ref _useTwoColumnsLayoutInHistories, value) && !_historyLayout.HasValue)
222+
NotifyHistoryLayoutChanged();
223+
}
224+
}
225+
226+
public Models.HistoryLayout HistoryLayout
227+
{
228+
get => _historyLayout ?? (_useTwoColumnsLayoutInHistories ? Models.HistoryLayout.SideBySide : Models.HistoryLayout.Stacked);
229+
set
230+
{
231+
if (Enum.IsDefined(value) && _historyLayout != value)
232+
{
233+
_historyLayout = value;
234+
NotifyHistoryLayoutChanged();
235+
}
236+
}
237+
}
238+
239+
[JsonIgnore]
240+
public bool IsHorizontalHistoryLayout => HistoryLayout != Models.HistoryLayout.Stacked;
241+
242+
[JsonIgnore]
243+
public bool IsFocusedHistoryLayout => HistoryLayout == Models.HistoryLayout.Focused;
244+
245+
private void NotifyHistoryLayoutChanged()
246+
{
247+
OnPropertyChanged(nameof(HistoryLayout));
248+
OnPropertyChanged(nameof(IsHorizontalHistoryLayout));
249+
OnPropertyChanged(nameof(IsFocusedHistoryLayout));
218250
}
219251

220252
public bool DisplayTimeAsPeriodInHistories
@@ -831,6 +863,7 @@ private bool RemoveInvalidRepositoriesRecursive(List<RepositoryNode> collection)
831863

832864
private bool _showTagsInGraph = true;
833865
private bool _useTwoColumnsLayoutInHistories = false;
866+
private Models.HistoryLayout? _historyLayout = null;
834867
private bool _displayTimeAsPeriodInHistories = false;
835868
private bool _useSideBySideDiff = false;
836869
private bool _ignoreWhitespaceChangesInDiff = false;

‎src/ViewModels/RevisionCompare.cs‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,8 @@ _endPoint is Models.Commit e &&
121121
hashes.Contains(e.SHA))
122122
return;
123123

124-
_startPoint = l;
125-
_endPoint = r;
124+
StartPoint = l;
125+
EndPoint = r;
126126
Refresh();
127127
}
128128

@@ -343,6 +343,9 @@ private void RefreshVisible()
343343

344344
private void Refresh()
345345
{
346+
OnPropertyChanged(nameof(LeftSideDesc));
347+
OnPropertyChanged(nameof(RightSideDesc));
348+
346349
Task.Run(async () =>
347350
{
348351
_changes = await new Commands.CompareRevisions(_repo.FullPath, GetSHA(_startPoint), GetSHA(_endPoint))

‎src/Views/ChangeCollectionView.axaml.cs‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,49 @@ public event EventHandler<RoutedEventArgs> ChangeDoubleTapped
166166
public ChangeCollectionView()
167167
{
168168
InitializeComponent();
169+
AddHandler(PointerReleasedEvent, OnChangePointerReleased, RoutingStrategies.Bubble, true);
170+
AddHandler(KeyDownEvent, OnChangeKeyDown, RoutingStrategies.Bubble, true);
171+
}
172+
173+
public static readonly RoutedEvent<RoutedEventArgs> ChangeActivatedEvent =
174+
RoutedEvent.Register<ChangeCollectionView, RoutedEventArgs>(nameof(ChangeActivated), RoutingStrategies.Bubble);
175+
176+
public event EventHandler<RoutedEventArgs> ChangeActivated
177+
{
178+
add { AddHandler(ChangeActivatedEvent, value); }
179+
remove { RemoveHandler(ChangeActivatedEvent, value); }
180+
}
181+
182+
private void OnChangePointerReleased(object sender, PointerReleasedEventArgs e)
183+
{
184+
if (e.InitialPressMouseButton != MouseButton.Left || e.KeyModifiers != KeyModifiers.None ||
185+
Selection is not { Count: 1, HasFolder: false })
186+
return;
187+
188+
for (var source = e.Source as Control; source != null && source != this; source = source.GetVisualParent() as Control)
189+
{
190+
if (source is not ListBoxItem item)
191+
continue;
192+
193+
var change = item.DataContext switch
194+
{
195+
Models.Change c => c,
196+
ViewModels.ChangeTreeNode { IsFolder: false } node => node.Change,
197+
_ => null,
198+
};
199+
if (change == Selection.Changes[0])
200+
RaiseEvent(new RoutedEventArgs(ChangeActivatedEvent));
201+
return;
202+
}
203+
}
204+
205+
private void OnChangeKeyDown(object sender, KeyEventArgs e)
206+
{
207+
if (e.KeyModifiers != KeyModifiers.None || Selection is not { Count: 1, HasFolder: false })
208+
return;
209+
210+
if (e.Key is Key.Enter or Key.Space or Key.Up or Key.Down or Key.Home or Key.End or Key.PageUp or Key.PageDown)
211+
RaiseEvent(new RoutedEventArgs(ChangeActivatedEvent));
169212
}
170213

171214
public void ToggleNodeIsExpanded(ViewModels.ChangeTreeNode node)

‎src/Views/ChangeListLayout.cs‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using Avalonia;
2+
using Avalonia.Controls;
3+
4+
namespace SourceGit.Views
5+
{
6+
public class ChangeListLayout : Grid
7+
{
8+
public static readonly StyledProperty<bool> IsFileListOnlyProperty =
9+
AvaloniaProperty.Register<ChangeListLayout, bool>(nameof(IsFileListOnly));
10+
11+
public static readonly StyledProperty<GridLength> ListWidthProperty =
12+
AvaloniaProperty.Register<ChangeListLayout, GridLength>(nameof(ListWidth), new GridLength(256));
13+
14+
public bool IsFileListOnly
15+
{
16+
get => GetValue(IsFileListOnlyProperty);
17+
set => SetValue(IsFileListOnlyProperty, value);
18+
}
19+
20+
public GridLength ListWidth
21+
{
22+
get => GetValue(ListWidthProperty);
23+
set => SetValue(ListWidthProperty, value);
24+
}
25+
26+
protected override System.Type StyleKeyOverride => typeof(Grid);
27+
28+
public ChangeListLayout()
29+
{
30+
ColumnDefinitions = new ColumnDefinitions("256,4,*");
31+
ColumnDefinitions[0].PropertyChanged += (_, e) =>
32+
{
33+
if (!_updating && !IsFileListOnly && e.Property == ColumnDefinition.WidthProperty)
34+
SetCurrentValue(ListWidthProperty, ColumnDefinitions[0].Width);
35+
};
36+
RefreshLayout();
37+
}
38+
39+
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
40+
{
41+
base.OnPropertyChanged(change);
42+
if (change.Property == IsFileListOnlyProperty || change.Property == ListWidthProperty)
43+
RefreshLayout();
44+
}
45+
46+
private void RefreshLayout()
47+
{
48+
_updating = true;
49+
ColumnDefinitions[0].MinWidth = IsFileListOnly ? 0 : 200;
50+
ColumnDefinitions[0].Width = IsFileListOnly ? GridLength.Star : ListWidth;
51+
ColumnDefinitions[1].Width = new GridLength(IsFileListOnly ? 0 : 4);
52+
ColumnDefinitions[2].MinWidth = IsFileListOnly ? 0 : 100;
53+
ColumnDefinitions[2].Width = IsFileListOnly ? new GridLength(0) : GridLength.Star;
54+
_updating = false;
55+
}
56+
57+
private bool _updating = false;
58+
}
59+
}

0 commit comments

Comments
 (0)