mirror of
https://github.com/sourcegit-scm/sourcegit.git
synced 2024-12-24 20:57:19 -08:00
feature: auto focus the next
change after stage/unstage selected changes (#464)
This commit is contained in:
parent
dcddc5a2f2
commit
ea3a6a4755
4 changed files with 122 additions and 22 deletions
|
@ -325,23 +325,24 @@ namespace SourceGit.ViewModels
|
||||||
PopupHost.ShowPopup(new StashChanges(_repo, _cached, true));
|
PopupHost.ShowPopup(new StashChanges(_repo, _cached, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StageSelected()
|
public void StageSelected(Models.Change next)
|
||||||
{
|
{
|
||||||
StageChanges(_selectedUnstaged);
|
StageChanges(_selectedUnstaged, next);
|
||||||
SelectedUnstaged = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StageAll()
|
public void StageAll()
|
||||||
{
|
{
|
||||||
StageChanges(_unstaged);
|
StageChanges(_unstaged, null);
|
||||||
SelectedUnstaged = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void StageChanges(List<Models.Change> changes)
|
public async void StageChanges(List<Models.Change> changes, Models.Change next)
|
||||||
{
|
{
|
||||||
if (_unstaged.Count == 0 || changes.Count == 0)
|
if (_unstaged.Count == 0 || changes.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Use `_selectedUnstaged` instead of `SelectedUnstaged` to avoid UI refresh.
|
||||||
|
_selectedUnstaged = next != null ? [next] : [];
|
||||||
|
|
||||||
IsStaging = true;
|
IsStaging = true;
|
||||||
_repo.SetWatcherEnabled(false);
|
_repo.SetWatcherEnabled(false);
|
||||||
if (changes.Count == _unstaged.Count)
|
if (changes.Count == _unstaged.Count)
|
||||||
|
@ -362,23 +363,24 @@ namespace SourceGit.ViewModels
|
||||||
IsStaging = false;
|
IsStaging = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UnstageSelected()
|
public void UnstageSelected(Models.Change next)
|
||||||
{
|
{
|
||||||
UnstageChanges(_selectedStaged);
|
UnstageChanges(_selectedStaged, next);
|
||||||
SelectedStaged = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UnstageAll()
|
public void UnstageAll()
|
||||||
{
|
{
|
||||||
UnstageChanges(_staged);
|
UnstageChanges(_staged, null);
|
||||||
SelectedStaged = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void UnstageChanges(List<Models.Change> changes)
|
public async void UnstageChanges(List<Models.Change> changes, Models.Change next)
|
||||||
{
|
{
|
||||||
if (_staged.Count == 0 || changes.Count == 0)
|
if (_staged.Count == 0 || changes.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Use `_selectedStaged` instead of `SelectedStaged` to avoid UI refresh.
|
||||||
|
_selectedStaged = next != null ? [next] : [];
|
||||||
|
|
||||||
IsUnstaging = true;
|
IsUnstaging = true;
|
||||||
_repo.SetWatcherEnabled(false);
|
_repo.SetWatcherEnabled(false);
|
||||||
if (_useAmend)
|
if (_useAmend)
|
||||||
|
@ -499,7 +501,7 @@ namespace SourceGit.ViewModels
|
||||||
stage.Icon = App.CreateMenuIcon("Icons.File.Add");
|
stage.Icon = App.CreateMenuIcon("Icons.File.Add");
|
||||||
stage.Click += (_, e) =>
|
stage.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
StageChanges(_selectedUnstaged);
|
StageChanges(_selectedUnstaged, null);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -823,7 +825,7 @@ namespace SourceGit.ViewModels
|
||||||
stage.Icon = App.CreateMenuIcon("Icons.File.Add");
|
stage.Icon = App.CreateMenuIcon("Icons.File.Add");
|
||||||
stage.Click += (_, e) =>
|
stage.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
StageChanges(_selectedUnstaged);
|
StageChanges(_selectedUnstaged, null);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -917,7 +919,7 @@ namespace SourceGit.ViewModels
|
||||||
unstage.Icon = App.CreateMenuIcon("Icons.File.Remove");
|
unstage.Icon = App.CreateMenuIcon("Icons.File.Remove");
|
||||||
unstage.Click += (_, e) =>
|
unstage.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
UnstageChanges(_selectedStaged);
|
UnstageChanges(_selectedStaged, null);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1086,7 +1088,7 @@ namespace SourceGit.ViewModels
|
||||||
unstage.Icon = App.CreateMenuIcon("Icons.File.Remove");
|
unstage.Icon = App.CreateMenuIcon("Icons.File.Remove");
|
||||||
unstage.Click += (_, e) =>
|
unstage.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
UnstageChanges(_selectedStaged);
|
UnstageChanges(_selectedStaged, null);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -96,6 +96,7 @@ namespace SourceGit.Views
|
||||||
|
|
||||||
public ChangeCollectionView()
|
public ChangeCollectionView()
|
||||||
{
|
{
|
||||||
|
Focusable = true;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,6 +133,69 @@ namespace SourceGit.Views
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Models.Change GetNextChangeWithoutSelection()
|
||||||
|
{
|
||||||
|
var selected = SelectedChanges;
|
||||||
|
var changes = Changes;
|
||||||
|
if (selected == null || selected.Count == 0)
|
||||||
|
return changes.Count > 0 ? changes[0] : null;
|
||||||
|
if (selected.Count == changes.Count)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var set = new HashSet<string>();
|
||||||
|
foreach (var c in selected)
|
||||||
|
set.Add(c.Path);
|
||||||
|
|
||||||
|
if (Content is ViewModels.ChangeCollectionAsTree tree)
|
||||||
|
{
|
||||||
|
var lastUnselected = -1;
|
||||||
|
for (int i = tree.Rows.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var row = tree.Rows[i];
|
||||||
|
if (!row.IsFolder)
|
||||||
|
{
|
||||||
|
if (set.Contains(row.FullPath))
|
||||||
|
{
|
||||||
|
if (lastUnselected == -1)
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lastUnselected = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastUnselected != -1)
|
||||||
|
return tree.Rows[lastUnselected].Change;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var lastUnselected = -1;
|
||||||
|
for (int i = changes.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (set.Contains(changes[i].Path))
|
||||||
|
{
|
||||||
|
if (lastUnselected == -1)
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lastUnselected = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastUnselected != -1)
|
||||||
|
return changes[lastUnselected];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||||
{
|
{
|
||||||
base.OnPropertyChanged(change);
|
base.OnPropertyChanged(change);
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
Classes="icon_button"
|
Classes="icon_button"
|
||||||
Width="26" Height="14"
|
Width="26" Height="14"
|
||||||
Padding="0"
|
Padding="0"
|
||||||
Command="{Binding StageSelected}">
|
Click="OnStageSelectedButtonClicked">
|
||||||
<ToolTip.Tip>
|
<ToolTip.Tip>
|
||||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||||
<TextBlock Text="{DynamicResource Text.WorkingCopy.Unstaged.Stage}" VerticalAlignment="Center"/>
|
<TextBlock Text="{DynamicResource Text.WorkingCopy.Unstaged.Stage}" VerticalAlignment="Center"/>
|
||||||
|
@ -64,6 +64,7 @@
|
||||||
|
|
||||||
<!-- Unstaged Changes -->
|
<!-- Unstaged Changes -->
|
||||||
<v:ChangeCollectionView Grid.Row="1"
|
<v:ChangeCollectionView Grid.Row="1"
|
||||||
|
x:Name="UnstagedChangesView"
|
||||||
IsUnstagedChange="True"
|
IsUnstagedChange="True"
|
||||||
SelectionMode="Multiple"
|
SelectionMode="Multiple"
|
||||||
Background="{DynamicResource Brush.Contents}"
|
Background="{DynamicResource Brush.Contents}"
|
||||||
|
@ -81,7 +82,7 @@
|
||||||
<TextBlock Grid.Column="1" Text="{DynamicResource Text.WorkingCopy.Staged}" Foreground="{DynamicResource Brush.FG2}" FontWeight="Bold" Margin="8,0,0,0"/>
|
<TextBlock Grid.Column="1" Text="{DynamicResource Text.WorkingCopy.Staged}" Foreground="{DynamicResource Brush.FG2}" FontWeight="Bold" Margin="8,0,0,0"/>
|
||||||
<TextBlock Grid.Column="2" FontWeight="Bold" Foreground="{DynamicResource Brush.FG2}" Text="{Binding Staged, Converter={x:Static c:ListConverters.ToCount}}"/>
|
<TextBlock Grid.Column="2" FontWeight="Bold" Foreground="{DynamicResource Brush.FG2}" Text="{Binding Staged, Converter={x:Static c:ListConverters.ToCount}}"/>
|
||||||
<v:LoadingIcon Grid.Column="3" Width="14" Height="14" Margin="8,0,0,0" IsVisible="{Binding IsUnstaging}"/>
|
<v:LoadingIcon Grid.Column="3" Width="14" Height="14" Margin="8,0,0,0" IsVisible="{Binding IsUnstaging}"/>
|
||||||
<Button Grid.Column="5" Classes="icon_button" Width="26" Height="14" Padding="0" Command="{Binding UnstageSelected}">
|
<Button Grid.Column="5" Classes="icon_button" Width="26" Height="14" Padding="0" Click="OnUnstageSelectedButtonClicked">
|
||||||
<ToolTip.Tip>
|
<ToolTip.Tip>
|
||||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||||
<TextBlock Text="{DynamicResource Text.WorkingCopy.Staged.Unstage}" VerticalAlignment="Center"/>
|
<TextBlock Text="{DynamicResource Text.WorkingCopy.Staged.Unstage}" VerticalAlignment="Center"/>
|
||||||
|
@ -98,6 +99,7 @@
|
||||||
|
|
||||||
<!-- Staged Changes -->
|
<!-- Staged Changes -->
|
||||||
<v:ChangeCollectionView Grid.Row="3"
|
<v:ChangeCollectionView Grid.Row="3"
|
||||||
|
x:Name="StagedChangesView"
|
||||||
SelectionMode="Multiple"
|
SelectionMode="Multiple"
|
||||||
Background="{DynamicResource Brush.Contents}"
|
Background="{DynamicResource Brush.Contents}"
|
||||||
ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=StagedChangeViewMode}"
|
ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=StagedChangeViewMode}"
|
||||||
|
|
|
@ -46,7 +46,9 @@ namespace SourceGit.Views
|
||||||
{
|
{
|
||||||
if (DataContext is ViewModels.WorkingCopy vm)
|
if (DataContext is ViewModels.WorkingCopy vm)
|
||||||
{
|
{
|
||||||
vm.StageSelected();
|
var next = UnstagedChangesView.GetNextChangeWithoutSelection();
|
||||||
|
vm.StageSelected(next);
|
||||||
|
UnstagedChangesView.Focus();
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +57,9 @@ namespace SourceGit.Views
|
||||||
{
|
{
|
||||||
if (DataContext is ViewModels.WorkingCopy vm)
|
if (DataContext is ViewModels.WorkingCopy vm)
|
||||||
{
|
{
|
||||||
vm.UnstageSelected();
|
var next = StagedChangesView.GetNextChangeWithoutSelection();
|
||||||
|
vm.UnstageSelected(next);
|
||||||
|
StagedChangesView.Focus();
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +70,9 @@ namespace SourceGit.Views
|
||||||
{
|
{
|
||||||
if (e.Key is Key.Space or Key.Enter)
|
if (e.Key is Key.Space or Key.Enter)
|
||||||
{
|
{
|
||||||
vm.StageSelected();
|
var next = UnstagedChangesView.GetNextChangeWithoutSelection();
|
||||||
|
vm.StageSelected(next);
|
||||||
|
UnstagedChangesView.Focus();
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -84,11 +90,37 @@ namespace SourceGit.Views
|
||||||
{
|
{
|
||||||
if (DataContext is ViewModels.WorkingCopy vm && e.Key is Key.Space or Key.Enter)
|
if (DataContext is ViewModels.WorkingCopy vm && e.Key is Key.Space or Key.Enter)
|
||||||
{
|
{
|
||||||
vm.UnstageSelected();
|
var next = StagedChangesView.GetNextChangeWithoutSelection();
|
||||||
|
vm.UnstageSelected(next);
|
||||||
|
StagedChangesView.Focus();
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnStageSelectedButtonClicked(object _, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (DataContext is ViewModels.WorkingCopy vm)
|
||||||
|
{
|
||||||
|
var next = UnstagedChangesView.GetNextChangeWithoutSelection();
|
||||||
|
vm.StageSelected(next);
|
||||||
|
UnstagedChangesView.Focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUnstageSelectedButtonClicked(object _, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (DataContext is ViewModels.WorkingCopy vm)
|
||||||
|
{
|
||||||
|
var next = StagedChangesView.GetNextChangeWithoutSelection();
|
||||||
|
vm.UnstageSelected(next);
|
||||||
|
StagedChangesView.Focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
private void OnOpenAIAssist(object _, RoutedEventArgs e)
|
private void OnOpenAIAssist(object _, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (!Models.OpenAI.IsValid)
|
if (!Models.OpenAI.IsValid)
|
||||||
|
|
Loading…
Reference in a new issue