Skip to content

Commit aaf9862

Browse files
committed
refactor: allows multiple-selection in interactive rebase window (#1775)
Signed-off-by: leo <[email protected]>
1 parent 83ad776 commit aaf9862

File tree

3 files changed

+130
-67
lines changed

3 files changed

+130
-67
lines changed

src/ViewModels/InteractiveRebase.cs

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -107,28 +107,25 @@ public AvaloniaList<InteractiveRebaseItem> Items
107107
get;
108108
} = [];
109109

110-
public InteractiveRebaseItem SelectedItem
110+
public InteractiveRebaseItem PreSelected
111111
{
112-
get => _selectedItem;
113-
set
114-
{
115-
if (SetProperty(ref _selectedItem, value))
116-
DetailContext.Commit = value?.Commit;
117-
}
112+
get => _preSelected;
113+
private set => SetProperty(ref _preSelected, value);
118114
}
119115

120-
public CommitDetail DetailContext
116+
public object Detail
121117
{
122-
get;
118+
get => _detail;
119+
private set => SetProperty(ref _detail, value);
123120
}
124121

125122
public InteractiveRebase(Repository repo, Models.Commit on, InteractiveRebasePrefill prefill = null)
126123
{
127124
_repo = repo;
125+
_commitDetail = new CommitDetail(repo, false);
128126
Current = repo.CurrentBranch;
129127
On = on;
130128
IsLoading = true;
131-
DetailContext = new CommitDetail(repo, false);
132129

133130
Task.Run(async () =>
134131
{
@@ -157,12 +154,29 @@ public InteractiveRebase(Repository repo, Models.Commit on, InteractiveRebasePre
157154
Dispatcher.UIThread.Post(() =>
158155
{
159156
Items.AddRange(list);
160-
SelectedItem = selected;
157+
PreSelected = selected;
161158
IsLoading = false;
162159
});
163160
});
164161
}
165162

163+
public void SelectCommits(List<InteractiveRebaseItem> items)
164+
{
165+
if (items.Count == 0)
166+
{
167+
Detail = null;
168+
}
169+
else if (items.Count == 1)
170+
{
171+
_commitDetail.Commit = items[0].Commit;
172+
Detail = _commitDetail;
173+
}
174+
else
175+
{
176+
Detail = new Models.Count(items.Count);
177+
}
178+
}
179+
166180
public void MoveItemUp(InteractiveRebaseItem item)
167181
{
168182
var idx = Items.IndexOf(item);
@@ -171,7 +185,7 @@ public void MoveItemUp(InteractiveRebaseItem item)
171185
var prev = Items[idx - 1];
172186
Items.RemoveAt(idx - 1);
173187
Items.Insert(idx, prev);
174-
SelectedItem = item;
188+
PreSelected = item;
175189
UpdateItems();
176190
}
177191
}
@@ -184,20 +198,27 @@ public void MoveItemDown(InteractiveRebaseItem item)
184198
var next = Items[idx + 1];
185199
Items.RemoveAt(idx + 1);
186200
Items.Insert(idx, next);
187-
SelectedItem = item;
201+
PreSelected = item;
188202
UpdateItems();
189203
}
190204
}
191205

192-
public void ChangeAction(InteractiveRebaseItem item, Models.InteractiveRebaseAction action)
206+
public void ChangeAction(List<InteractiveRebaseItem> selected, Models.InteractiveRebaseAction action)
193207
{
194-
if (!item.CanSquashOrFixup)
208+
if (action == Models.InteractiveRebaseAction.Squash || action == Models.InteractiveRebaseAction.Fixup)
209+
{
210+
foreach (var item in selected)
211+
{
212+
if (item.CanSquashOrFixup)
213+
item.Action = action;
214+
}
215+
}
216+
else
195217
{
196-
if (action == Models.InteractiveRebaseAction.Squash || action == Models.InteractiveRebaseAction.Fixup)
197-
return;
218+
foreach (var item in selected)
219+
item.Action = action;
198220
}
199221

200-
item.Action = action;
201222
UpdateItems();
202223
}
203224

@@ -257,6 +278,8 @@ private void UpdateItems()
257278

258279
private Repository _repo = null;
259280
private bool _isLoading = false;
260-
private InteractiveRebaseItem _selectedItem = null;
281+
private InteractiveRebaseItem _preSelected = null;
282+
private object _detail = null;
283+
private CommitDetail _commitDetail = null;
261284
}
262285
}

src/Views/InteractiveRebase.axaml

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
33
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
44
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
5+
xmlns:m="using:SourceGit.Models"
56
xmlns:vm="using:SourceGit.ViewModels"
67
xmlns:c="using:SourceGit.Converters"
78
xmlns:v="using:SourceGit.Views"
@@ -55,8 +56,8 @@
5556
Focusable="True"
5657
Background="{DynamicResource Brush.Contents}"
5758
ItemsSource="{Binding Items}"
58-
SelectionMode="Single"
59-
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
59+
SelectionMode="Multiple"
60+
SelectedItem="{Binding PreSelected, Mode=OneWay}"
6061
SelectionChanged="OnRowsSelectionChanged"
6162
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
6263
ScrollViewer.VerticalScrollBarVisibility="Auto"
@@ -176,21 +177,38 @@
176177
BorderThickness="0,1,0,0"
177178
BorderBrush="{DynamicResource Brush.Border2}"/>
178179

179-
<Grid Grid.Row="2">
180-
<Path Width="128" Height="128"
181-
Data="{StaticResource Icons.Detail}"
182-
HorizontalAlignment="Center"
183-
Fill="{DynamicResource Brush.FG2}"
184-
IsVisible="{Binding SelectedItem, Converter={x:Static ObjectConverters.IsNull}}"/>
185-
186-
<ContentControl Content="{Binding DetailContext}" IsVisible="{Binding SelectedItem, Converter={x:Static ObjectConverters.IsNotNull}}">
187-
<ContentControl.DataTemplates>
188-
<DataTemplate DataType="vm:CommitDetail">
189-
<v:CommitDetail/>
190-
</DataTemplate>
191-
</ContentControl.DataTemplates>
192-
</ContentControl>
193-
</Grid>
180+
<ContentControl Grid.Row="2">
181+
<ContentControl.Content>
182+
<Binding Path="Detail">
183+
<Binding.TargetNullValue>
184+
<Path Width="128" Height="128"
185+
Data="{StaticResource Icons.Detail}"
186+
HorizontalAlignment="Center" VerticalAlignment="Center"
187+
Fill="{DynamicResource Brush.FG2}"/>
188+
</Binding.TargetNullValue>
189+
</Binding>
190+
</ContentControl.Content>
191+
192+
<ContentControl.DataTemplates>
193+
<DataTemplate DataType="m:Count">
194+
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
195+
<Path Width="128" Height="128"
196+
Data="{StaticResource Icons.Detail}"
197+
HorizontalAlignment="Center"
198+
Fill="{DynamicResource Brush.FG2}"/>
199+
200+
<TextBlock HorizontalAlignment="Center"
201+
Margin="0,16"
202+
FontSize="24" FontWeight="Bold"
203+
Foreground="{DynamicResource Brush.FG2}"
204+
Text="{Binding Value, Converter={x:Static c:StringConverters.FormatByResourceKey}, ConverterParameter='Histories.Selected'}"/>
205+
</StackPanel>
206+
</DataTemplate>
207+
<DataTemplate DataType="vm:CommitDetail">
208+
<v:CommitDetail/>
209+
</DataTemplate>
210+
</ContentControl.DataTemplates>
211+
</ContentControl>
194212
</Grid>
195213
</Border>
196214

src/Views/InteractiveRebase.axaml.cs

Lines changed: 53 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23

34
using Avalonia;
45
using Avalonia.Controls;
@@ -14,60 +15,67 @@ public class InteractiveRebaseListBox : ListBox
1415
{
1516
protected override Type StyleKeyOverride => typeof(ListBox);
1617

17-
/// <summary>
18-
/// Prevent ListBox handle the arrow keys.
19-
/// </summary>
20-
/// <param name="e"></param>
2118
protected override void OnKeyDown(KeyEventArgs e)
2219
{
23-
if (DataContext is not ViewModels.InteractiveRebase vm)
20+
if (DataContext is not ViewModels.InteractiveRebase vm || SelectedItems == null)
2421
return;
2522

26-
var item = vm.SelectedItem;
27-
if (item == null)
23+
var items = new List<ViewModels.InteractiveRebaseItem>();
24+
foreach (var item in SelectedItems)
25+
{
26+
if (item is ViewModels.InteractiveRebaseItem rebaseItem)
27+
items.Add(rebaseItem);
28+
}
29+
30+
if (items.Count == 0)
2831
{
2932
base.OnKeyDown(e);
3033
return;
3134
}
3235

3336
if (e.Key == Key.P)
3437
{
35-
vm.ChangeAction(item, Models.InteractiveRebaseAction.Pick);
38+
vm.ChangeAction(items, Models.InteractiveRebaseAction.Pick);
3639
MoveSelection(NavigationDirection.Next);
3740
e.Handled = true;
3841
}
3942
else if (e.Key == Key.E)
4043
{
41-
vm.ChangeAction(item, Models.InteractiveRebaseAction.Edit);
44+
vm.ChangeAction(items, Models.InteractiveRebaseAction.Edit);
4245
MoveSelection(NavigationDirection.Next);
4346
e.Handled = true;
4447
}
4548
else if (e.Key == Key.R)
4649
{
47-
vm.ChangeAction(item, Models.InteractiveRebaseAction.Reword);
48-
MoveSelection(NavigationDirection.Next);
50+
vm.ChangeAction(items, Models.InteractiveRebaseAction.Reword);
51+
if (items.Count == 1)
52+
this.FindAncestorOfType<InteractiveRebase>()?.OpenCommitMessageEditor(items[0]);
53+
else
54+
MoveSelection(NavigationDirection.Next);
55+
4956
e.Handled = true;
5057
}
5158
else if (e.Key == Key.S)
5259
{
53-
vm.ChangeAction(item, Models.InteractiveRebaseAction.Squash);
60+
vm.ChangeAction(items, Models.InteractiveRebaseAction.Squash);
5461
MoveSelection(NavigationDirection.Next);
5562
e.Handled = true;
5663
}
5764
else if (e.Key == Key.F)
5865
{
59-
vm.ChangeAction(item, Models.InteractiveRebaseAction.Fixup);
66+
vm.ChangeAction(items, Models.InteractiveRebaseAction.Fixup);
6067
MoveSelection(NavigationDirection.Next);
6168
e.Handled = true;
6269
}
6370
else if (e.Key == Key.D)
6471
{
65-
vm.ChangeAction(item, Models.InteractiveRebaseAction.Drop);
72+
vm.ChangeAction(items, Models.InteractiveRebaseAction.Drop);
6673
MoveSelection(NavigationDirection.Next);
6774
e.Handled = true;
6875
}
69-
else if (e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control))
76+
else if (e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control) && items.Count == 1)
7077
{
78+
var item = items[0];
7179
if (e.Key == Key.Up)
7280
{
7381
vm.MoveItemUp(item);
@@ -93,6 +101,13 @@ public InteractiveRebase()
93101
InitializeComponent();
94102
}
95103

104+
public void OpenCommitMessageEditor(ViewModels.InteractiveRebaseItem item)
105+
{
106+
var dialog = new CommitMessageEditor();
107+
dialog.AsBuiltin(item.FullMessage, msg => item.FullMessage = msg);
108+
dialog.ShowDialog(this);
109+
}
110+
96111
protected override void OnLoaded(RoutedEventArgs e)
97112
{
98113
base.OnLoaded(e);
@@ -108,19 +123,25 @@ private void CloseWindow(object _1, RoutedEventArgs _2)
108123

109124
private void OnRowsSelectionChanged(object sender, SelectionChangedEventArgs e)
110125
{
111-
if (!_firstSelectionChangedHandled &&
112-
sender is InteractiveRebaseListBox list &&
113-
list.SelectedItem is ViewModels.InteractiveRebaseItem item)
114-
{
126+
if (DataContext is not ViewModels.InteractiveRebase vm || sender is not InteractiveRebaseListBox listBox)
127+
return;
128+
129+
var isFirstTimeHere = !_firstSelectionChangedHandled;
130+
if (isFirstTimeHere)
115131
_firstSelectionChangedHandled = true;
116132

117-
if (item.Action == Models.InteractiveRebaseAction.Reword)
118-
{
119-
var dialog = new CommitMessageEditor();
120-
dialog.AsBuiltin(item.FullMessage, msg => item.FullMessage = msg);
121-
dialog.ShowDialog(this);
122-
}
133+
var selected = listBox.SelectedItems ?? new List<object>();
134+
var items = new List<ViewModels.InteractiveRebaseItem>();
135+
foreach (var item in selected)
136+
{
137+
if (item is ViewModels.InteractiveRebaseItem rebaseItem)
138+
items.Add(rebaseItem);
123139
}
140+
141+
vm.SelectCommits(items);
142+
143+
if (items.Count == 1 && isFirstTimeHere && items[0].Action == Models.InteractiveRebaseAction.Reword)
144+
OpenCommitMessageEditor(items[0]);
124145
}
125146

126147
private void OnSetupRowHeaderDragDrop(object sender, RoutedEventArgs e)
@@ -201,11 +222,7 @@ private void OnButtonActionClicked(object sender, RoutedEventArgs e)
201222
private void OnOpenCommitMessageEditor(object sender, RoutedEventArgs e)
202223
{
203224
if (sender is Button { DataContext: ViewModels.InteractiveRebaseItem item })
204-
{
205-
var dialog = new CommitMessageEditor();
206-
dialog.AsBuiltin(item.FullMessage, msg => item.FullMessage = msg);
207-
dialog.ShowDialog(this);
208-
}
225+
OpenCommitMessageEditor(item);
209226

210227
e.Handled = true;
211228
}
@@ -256,7 +273,12 @@ private void CreateActionMenuItem(MenuFlyout flyout, ViewModels.InteractiveRebas
256273
menuItem.Click += (_, e) =>
257274
{
258275
if (DataContext is ViewModels.InteractiveRebase vm)
259-
vm.ChangeAction(item, action);
276+
{
277+
vm.ChangeAction([item], action);
278+
279+
if (action == Models.InteractiveRebaseAction.Reword)
280+
OpenCommitMessageEditor(item);
281+
}
260282

261283
e.Handled = true;
262284
};

0 commit comments

Comments
 (0)