Skip to content

Commit 6fe96d6

Browse files
committed
feature: allow deleting multiple branches at one time (#137)
1 parent 99794e7 commit 6fe96d6

13 files changed

+493
-88
lines changed
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
using System.Collections.Generic;
2+
3+
using Avalonia;
4+
using Avalonia.Controls;
5+
using Avalonia.Data.Converters;
6+
7+
namespace SourceGit.Converters
8+
{
9+
public static class BranchTreeNodeConverters
10+
{
11+
public static readonly CornerRadius DEFAULT = new CornerRadius(4);
12+
13+
public static readonly FuncMultiValueConverter<object, CornerRadius> ToCornerRadius =
14+
new FuncMultiValueConverter<object, CornerRadius>(v =>
15+
{
16+
if (v == null)
17+
return DEFAULT;
18+
19+
var array = new List<object>();
20+
array.AddRange(v);
21+
if (array.Count != 2)
22+
return DEFAULT;
23+
24+
var item = array[1] as TreeViewItem;
25+
if (item == null || !item.IsSelected)
26+
return DEFAULT;
27+
28+
var prev = GetPrevTreeViewItem(item);
29+
var next = GetNextTreeViewItem(item, true);
30+
31+
double top = 4, bottom = 4;
32+
if (prev != null && prev.IsSelected)
33+
top = 0;
34+
if (next != null && next.IsSelected)
35+
bottom = 0;
36+
37+
return new CornerRadius(top, bottom);
38+
});
39+
40+
private static TreeViewItem GetPrevTreeViewItem(TreeViewItem item)
41+
{
42+
if (item.Parent is TreeView tree)
43+
{
44+
var idx = tree.IndexFromContainer(item);
45+
if (idx == 0)
46+
return null;
47+
48+
var prev = tree.ContainerFromIndex(idx - 1) as TreeViewItem;
49+
if (prev != null && prev.IsExpanded && prev.ItemCount > 0)
50+
return prev.ContainerFromIndex(prev.ItemCount - 1) as TreeViewItem;
51+
52+
return prev;
53+
}
54+
else if (item.Parent is TreeViewItem parentItem)
55+
{
56+
var idx = parentItem.IndexFromContainer(item);
57+
if (idx == 0)
58+
return parentItem;
59+
60+
var prev = parentItem.ContainerFromIndex(idx - 1) as TreeViewItem;
61+
if (prev != null && prev.IsExpanded && prev.ItemCount > 0)
62+
return prev.ContainerFromIndex(prev.ItemCount - 1) as TreeViewItem;
63+
64+
return prev;
65+
}
66+
else
67+
{
68+
return null;
69+
}
70+
}
71+
72+
private static TreeViewItem GetNextTreeViewItem(TreeViewItem item, bool intoSelf = false)
73+
{
74+
if (intoSelf && item.IsExpanded && item.ItemCount > 0)
75+
return item.ContainerFromIndex(0) as TreeViewItem;
76+
77+
if (item.Parent is TreeView tree)
78+
{
79+
var idx = tree.IndexFromContainer(item);
80+
if (idx == tree.ItemCount - 1)
81+
return null;
82+
83+
return tree.ContainerFromIndex(idx + 1) as TreeViewItem;
84+
}
85+
else if (item.Parent is TreeViewItem parentItem)
86+
{
87+
var idx = parentItem.IndexFromContainer(item);
88+
if (idx == parentItem.ItemCount - 1)
89+
return GetNextTreeViewItem(parentItem);
90+
91+
return parentItem.ContainerFromIndex(idx + 1) as TreeViewItem;
92+
}
93+
else
94+
{
95+
return null;
96+
}
97+
}
98+
}
99+
}

src/Resources/Locales/en_US.axaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
<x:String x:Key="Text.BranchCM.Checkout" xml:space="preserve">Checkout${0}$</x:String>
3636
<x:String x:Key="Text.BranchCM.CopyName" xml:space="preserve">Copy Branch Name</x:String>
3737
<x:String x:Key="Text.BranchCM.Delete" xml:space="preserve">Delete${0}$</x:String>
38+
<x:String x:Key="Text.BranchCM.DeleteMultiBranches" xml:space="preserve">Delete selected {0} branches</x:String>
3839
<x:String x:Key="Text.BranchCM.DiscardAll" xml:space="preserve">Discard all changes</x:String>
3940
<x:String x:Key="Text.BranchCM.FastForward" xml:space="preserve">Fast-Forward to${0}$</x:String>
4041
<x:String x:Key="Text.BranchCM.Finish" xml:space="preserve">Git Flow - Finish${0}$</x:String>
@@ -128,6 +129,9 @@
128129
<x:String x:Key="Text.DeleteBranch.Branch" xml:space="preserve">Branch :</x:String>
129130
<x:String x:Key="Text.DeleteBranch.IsRemoteTip" xml:space="preserve">You are about to delete a remote branch!!!</x:String>
130131
<x:String x:Key="Text.DeleteBranch.WithTrackingRemote" xml:space="preserve">Also delete remote branch${0}$</x:String>
132+
<x:String x:Key="Text.DeleteMultiBranch" xml:space="preserve">Delete Multiple Branches</x:String>
133+
<x:String x:Key="Text.DeleteMultiBranch.Targets" xml:space="preserve">Targets : </x:String>
134+
<x:String x:Key="Text.DeleteMultiBranch.Tip" xml:space="preserve">You are trying to delete multiple branches at one time. Be sure to double-check before taking action!</x:String>
131135
<x:String x:Key="Text.DeleteRemote" xml:space="preserve">Delete Remote</x:String>
132136
<x:String x:Key="Text.DeleteRemote.Remote" xml:space="preserve">Remote :</x:String>
133137
<x:String x:Key="Text.DeleteRepositoryNode.Target" xml:space="preserve">Target :</x:String>

src/Resources/Locales/zh_CN.axaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
<x:String x:Key="Text.BranchCM.Checkout" xml:space="preserve">检出(checkout)${0}$</x:String>
3636
<x:String x:Key="Text.BranchCM.CopyName" xml:space="preserve">复制分支名</x:String>
3737
<x:String x:Key="Text.BranchCM.Delete" xml:space="preserve">删除${0}$</x:String>
38+
<x:String x:Key="Text.BranchCM.DeleteMultiBranches" xml:space="preserve">删除选中的 {0} 个分支</x:String>
3839
<x:String x:Key="Text.BranchCM.DiscardAll" xml:space="preserve">放弃所有更改</x:String>
3940
<x:String x:Key="Text.BranchCM.FastForward" xml:space="preserve">快进(fast-forward)到${0}$</x:String>
4041
<x:String x:Key="Text.BranchCM.Finish" xml:space="preserve">GIT工作流 - 完成${0}$</x:String>
@@ -128,6 +129,9 @@
128129
<x:String x:Key="Text.DeleteBranch.Branch" xml:space="preserve">分支名 :</x:String>
129130
<x:String x:Key="Text.DeleteBranch.IsRemoteTip" xml:space="preserve">您正在删除远程上的分支,请务必小心!!!</x:String>
130131
<x:String x:Key="Text.DeleteBranch.WithTrackingRemote" xml:space="preserve">同时删除远程分支${0}$</x:String>
132+
<x:String x:Key="Text.DeleteMultiBranch" xml:space="preserve">删除多个分支</x:String>
133+
<x:String x:Key="Text.DeleteMultiBranch.Targets" xml:space="preserve">分支列表 :</x:String>
134+
<x:String x:Key="Text.DeleteMultiBranch.Tip" xml:space="preserve">您正在尝试一次性删除多个分支,请务必仔细检查后再执行操作!</x:String>
131135
<x:String x:Key="Text.DeleteRemote" xml:space="preserve">删除远程确认</x:String>
132136
<x:String x:Key="Text.DeleteRemote.Remote" xml:space="preserve">远程名 :</x:String>
133137
<x:String x:Key="Text.DeleteRepositoryNode.Target" xml:space="preserve">目标 :</x:String>

src/Resources/Styles.axaml

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -486,37 +486,15 @@
486486
</Style>
487487

488488
<Style Selector="ListBox.page_switcher ListBoxItem">
489-
<Setter Property="Template">
490-
<ControlTemplate>
491-
<Grid>
492-
<ContentPresenter Name="PART_ContentPresenter"
493-
Background="{TemplateBinding Background}"
494-
BorderBrush="{TemplateBinding BorderBrush}"
495-
BorderThickness="{TemplateBinding BorderThickness}"
496-
CornerRadius="{TemplateBinding CornerRadius}"
497-
ContentTemplate="{TemplateBinding ContentTemplate}"
498-
Content="{TemplateBinding Content}"
499-
Padding="{TemplateBinding Padding}"
500-
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
501-
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" />
502-
503-
<Border Background="{DynamicResource Brush.FG2}"
504-
Width="4"
505-
HorizontalAlignment="Left" VerticalAlignment="Stretch"
506-
IsHitTestVisible="False"
507-
IsVisible="{TemplateBinding IsSelected}"/>
508-
</Grid>
509-
</ControlTemplate>
510-
</Setter>
511-
</Style>
512-
<Style Selector="ListBox.page_switcher ListBoxItem:selected /template/ ContentPresenter#PART_ContentPresenter">
513-
<Setter Property="Background" Value="{DynamicResource Brush.SwitcherBG}"/>
489+
<Setter Property="Margin" Value="4,0,2,0"/>
490+
<Setter Property="Padding" Value="0"/>
491+
<Setter Property="CornerRadius" Value="4"/>
514492
</Style>
515493
<Style Selector="ListBox.page_switcher ListBoxItem:pointerover /template/ ContentPresenter#PART_ContentPresenter">
516-
<Setter Property="Background" Value="{DynamicResource Brush.SwitcherHover}"/>
494+
<Setter Property="Background" Value="Transparent"/>
517495
</Style>
518-
<Style Selector="ListBox.page_switcher ListBoxItem:selected:pointerover /template/ ContentPresenter#PART_ContentPresenter">
519-
<Setter Property="Background" Value="{DynamicResource Brush.SwitcherBG}"/>
496+
<Style Selector="ListBox.page_switcher ListBoxItem:selected /template/ ContentPresenter#PART_ContentPresenter">
497+
<Setter Property="Background" Value="{DynamicResource Brush.AccentHovered}"/>
520498
</Style>
521499

522500
<Style Selector="ContextMenu">

src/Resources/Themes.axaml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414
<Color x:Key="Color.Decorator">#FF6F6F6F</Color>
1515
<Color x:Key="Color.DecoratorIcon">#FFF8F8F8</Color>
1616
<Color x:Key="Color.Conflict">#FF836C2E</Color>
17-
<Color x:Key="Color.SwitcherHover">#FFDFDFDF</Color>
18-
<Color x:Key="Color.SwitcherBG">#FFCFCFCF</Color>
1917
<Color x:Key="Color.Border0">#FFCFCFCF</Color>
2018
<Color x:Key="Color.Border1">#FF898989</Color>
2119
<Color x:Key="Color.Border2">#FFCFCFCF</Color>
@@ -42,8 +40,6 @@
4240
<Color x:Key="Color.Decorator">#FF505050</Color>
4341
<Color x:Key="Color.DecoratorIcon">#FFF8F8F8</Color>
4442
<Color x:Key="Color.Conflict">#FFFAFAD2</Color>
45-
<Color x:Key="Color.SwitcherHover">#FF323232</Color>
46-
<Color x:Key="Color.SwitcherBG">#FF3F3F3F</Color>
4743
<Color x:Key="Color.Border0">#FF181818</Color>
4844
<Color x:Key="Color.Border1">#FF7C7C7C</Color>
4945
<Color x:Key="Color.Border2">#FF404040</Color>
@@ -70,8 +66,6 @@
7066
<SolidColorBrush x:Key="Brush.Decorator" Color="{DynamicResource Color.Decorator}"/>
7167
<SolidColorBrush x:Key="Brush.DecoratorIcon" Color="{DynamicResource Color.DecoratorIcon}"/>
7268
<SolidColorBrush x:Key="Brush.Conflict" Color="{DynamicResource Color.Conflict}"/>
73-
<SolidColorBrush x:Key="Brush.SwitcherHover" Color="{DynamicResource Color.SwitcherHover}"/>
74-
<SolidColorBrush x:Key="Brush.SwitcherBG" Color="{DynamicResource Color.SwitcherBG}"/>
7569
<SolidColorBrush x:Key="Brush.Border0" Color="{DynamicResource Color.Border0}"/>
7670
<SolidColorBrush x:Key="Brush.Border1" Color="{DynamicResource Color.Border1}"/>
7771
<SolidColorBrush x:Key="Brush.Border2" Color="{DynamicResource Color.Border2}"/>
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using System.Collections.Generic;
2+
using System.Threading.Tasks;
3+
4+
namespace SourceGit.ViewModels
5+
{
6+
public class DeleteMultipleBranches : Popup
7+
{
8+
public List<Models.Branch> Targets
9+
{
10+
get;
11+
}
12+
13+
public DeleteMultipleBranches(Repository repo, List<Models.Branch> branches, bool isLocal)
14+
{
15+
_repo = repo;
16+
_isLocal = isLocal;
17+
Targets = branches;
18+
View = new Views.DeleteMultipleBranches() { DataContext = this };
19+
}
20+
21+
public override Task<bool> Sure()
22+
{
23+
_repo.SetWatcherEnabled(false);
24+
ProgressDescription = "Deleting multiple branches...";
25+
26+
return Task.Run(() =>
27+
{
28+
if (_isLocal)
29+
{
30+
foreach (var target in Targets)
31+
{
32+
SetProgressDescription($"Deleting local branch : {target.Name}");
33+
Commands.Branch.DeleteLocal(_repo.FullPath, target.Name);
34+
}
35+
}
36+
else
37+
{
38+
foreach (var target in Targets)
39+
{
40+
SetProgressDescription($"Deleting remote branch : {target.Remote}/{target.Name}");
41+
Commands.Branch.DeleteRemote(_repo.FullPath, target.Remote, target.Name);
42+
}
43+
}
44+
45+
CallUIThread(() => _repo.SetWatcherEnabled(true));
46+
return true;
47+
});
48+
}
49+
50+
private Repository _repo = null;
51+
private bool _isLocal = false;
52+
}
53+
}

src/ViewModels/Repository.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,12 @@ public void CheckoutLocalBranch(string branch)
722722
PopupHost.ShowAndStartPopup(new Checkout(this, branch));
723723
}
724724

725+
public void DeleteMultipleBranches(List<Models.Branch> branches, bool isLocal)
726+
{
727+
if (PopupHost.CanCreatePopup())
728+
PopupHost.ShowPopup(new DeleteMultipleBranches(this, branches, isLocal));
729+
}
730+
725731
public void CreateNewTag()
726732
{
727733
var current = Branches.Find(x => x.IsCurrent);

src/Views/ContextMenuExtension.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ public static class ContextMenuExtension
88
{
99
public static void OpenContextMenu(this Control control, ContextMenu menu)
1010
{
11+
if (menu == null) return;
12+
1113
menu.PlacementTarget = control;
1214
menu.Closing += OnContextMenuClosing; // Clear context menu because it is dynamic.
1315

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<UserControl xmlns="https://github.com/avaloniaui"
2+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
3+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
4+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
5+
xmlns:m="using:SourceGit.Models"
6+
xmlns:vm="using:SourceGit.ViewModels"
7+
xmlns:v="using:SourceGit.Views"
8+
xmlns:c="using:SourceGit.Converters"
9+
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
10+
x:Class="SourceGit.Views.DeleteMultipleBranches"
11+
x:DataType="vm:DeleteMultipleBranches">
12+
<StackPanel Orientation="Vertical" Margin="8,0">
13+
<TextBlock FontSize="18"
14+
Classes="bold"
15+
Text="{DynamicResource Text.DeleteMultiBranch}" />
16+
17+
<Grid Margin="0,16,8,0" RowDefinitions="Auto,Auto" ColumnDefinitions="120,*">
18+
<TextBlock Grid.Row="0" Grid.Column="0"
19+
HorizontalAlignment="Right" VerticalAlignment="Top"
20+
Text="{DynamicResource Text.DeleteMultiBranch.Targets}" />
21+
<Border Grid.Row="0" Grid.Column="1"
22+
Background="{DynamicResource Brush.Contents}"
23+
BorderThickness="1"
24+
BorderBrush="{DynamicResource Brush.Border1}"
25+
CornerRadius="4"
26+
Padding="4">
27+
<DataGrid MaxHeight="200"
28+
Background="Transparent"
29+
BorderThickness="0"
30+
ItemsSource="{Binding Targets}"
31+
SelectionMode="Single"
32+
CanUserReorderColumns="False"
33+
CanUserResizeColumns="False"
34+
CanUserSortColumns="False"
35+
IsReadOnly="True"
36+
HeadersVisibility="None"
37+
Focusable="False"
38+
RowHeight="26"
39+
HorizontalScrollBarVisibility="Auto"
40+
VerticalScrollBarVisibility="Auto">
41+
<DataGrid.Styles>
42+
<Style Selector="DataGridRow">
43+
<Setter Property="CornerRadius" Value="4" />
44+
</Style>
45+
46+
<Style Selector="DataGridRow /template/ Border#RowBorder">
47+
<Setter Property="ClipToBounds" Value="True" />
48+
</Style>
49+
50+
<Style Selector="DataGridRow:pointerover /template/ Rectangle#BackgroundRectangle">
51+
<Setter Property="Fill" Value="{DynamicResource Brush.AccentHovered}" />
52+
</Style>
53+
54+
<Style Selector="DataGridRow:selected /template/ Rectangle#BackgroundRectangle">
55+
<Setter Property="Fill" Value="{DynamicResource Brush.Accent}" />
56+
</Style>
57+
</DataGrid.Styles>
58+
59+
<DataGrid.Columns>
60+
<DataGridTemplateColumn Header="ICON">
61+
<DataGridTemplateColumn.CellTemplate>
62+
<DataTemplate>
63+
<Path Width="10" Height="10" Margin="4,0,8,0" Data="{StaticResource Icons.Branch}" />
64+
</DataTemplate>
65+
</DataGridTemplateColumn.CellTemplate>
66+
</DataGridTemplateColumn>
67+
68+
<DataGridTemplateColumn Width="*" Header="NAME">
69+
<DataGridTemplateColumn.CellTemplate>
70+
<DataTemplate>
71+
<TextBlock Text="{Binding Converter={x:Static c:BranchConverters.ToName}}" ClipToBounds="True"
72+
Classes="monospace" />
73+
</DataTemplate>
74+
</DataGridTemplateColumn.CellTemplate>
75+
</DataGridTemplateColumn>
76+
</DataGrid.Columns>
77+
</DataGrid>
78+
</Border>
79+
80+
<TextBlock Grid.Row="1" Grid.Column="1"
81+
Margin="0,8,0,0"
82+
Text="{DynamicResource Text.DeleteMultiBranch.Tip}"
83+
TextWrapping="Wrap"
84+
Foreground="{DynamicResource Brush.FG2}"
85+
FontStyle="Italic" />
86+
</Grid>
87+
</StackPanel>
88+
</UserControl>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using Avalonia.Controls;
2+
3+
namespace SourceGit.Views
4+
{
5+
public partial class DeleteMultipleBranches : UserControl
6+
{
7+
public DeleteMultipleBranches()
8+
{
9+
InitializeComponent();
10+
}
11+
}
12+
}
13+

0 commit comments

Comments
 (0)