Skip to content

Commit ca7b674

Browse files
committed
feature: add merge command palette
Signed-off-by: leo <longshuang@msn.cn>
1 parent 81b2fbb commit ca7b674

File tree

7 files changed

+283
-14
lines changed

7 files changed

+283
-14
lines changed

src/App.axaml.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public static void LogException(Exception ex)
106106
#endregion
107107

108108
#region Utility Functions
109-
public static object CreateViewForViewModel(object data)
109+
public static Control CreateViewForViewModel(object data)
110110
{
111111
var dataTypeName = data.GetType().FullName;
112112
if (string.IsNullOrEmpty(dataTypeName) || !dataTypeName.Contains(".ViewModels.", StringComparison.Ordinal))
@@ -115,7 +115,7 @@ public static object CreateViewForViewModel(object data)
115115
var viewTypeName = dataTypeName.Replace(".ViewModels.", ".Views.");
116116
var viewType = Type.GetType(viewTypeName);
117117
if (viewType != null)
118-
return Activator.CreateInstance(viewType);
118+
return Activator.CreateInstance(viewType) as Control;
119119

120120
return null;
121121
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using CommunityToolkit.Mvvm.ComponentModel;
4+
5+
namespace SourceGit.ViewModels
6+
{
7+
public class MergeCommandPalette : ObservableObject, IDisposable
8+
{
9+
public List<Models.Branch> Branches
10+
{
11+
get => _branches;
12+
private set => SetProperty(ref _branches, value);
13+
}
14+
15+
public string Filter
16+
{
17+
get => _filter;
18+
set
19+
{
20+
if (SetProperty(ref _filter, value))
21+
UpdateBranches();
22+
}
23+
}
24+
25+
public Models.Branch SelectedBranch
26+
{
27+
get => _selectedBranch;
28+
set => SetProperty(ref _selectedBranch, value);
29+
}
30+
31+
public MergeCommandPalette(Launcher launcher, Repository repo)
32+
{
33+
_launcher = launcher;
34+
_repo = repo;
35+
UpdateBranches();
36+
}
37+
38+
public void Dispose()
39+
{
40+
_launcher = null;
41+
_repo = null;
42+
_branches.Clear();
43+
_filter = null;
44+
_selectedBranch = null;
45+
}
46+
47+
public void ClearFilter()
48+
{
49+
Filter = string.Empty;
50+
}
51+
52+
public void Launch()
53+
{
54+
if (_repo.CanCreatePopup() && _selectedBranch != null)
55+
_repo.ShowPopup(new Merge(_repo, _selectedBranch, _repo.CurrentBranch.Name, false));
56+
57+
_launcher?.CancelCommandPalette();
58+
}
59+
60+
private void UpdateBranches()
61+
{
62+
var current = _repo.CurrentBranch;
63+
if (current == null)
64+
return;
65+
66+
var branches = new List<Models.Branch>();
67+
foreach (var b in _repo.Branches)
68+
{
69+
if (b == current)
70+
continue;
71+
72+
if (string.IsNullOrEmpty(_filter) || b.FriendlyName.Contains(_filter, StringComparison.OrdinalIgnoreCase))
73+
branches.Add(b);
74+
}
75+
76+
branches.Sort((l, r) =>
77+
{
78+
if (l.IsLocal == r.IsLocal)
79+
return l.Name.CompareTo(r.Name);
80+
81+
return l.IsLocal ? -1 : 1;
82+
});
83+
84+
Branches = branches;
85+
}
86+
87+
private Launcher _launcher = null;
88+
private Repository _repo = null;
89+
private List<Models.Branch> _branches = new List<Models.Branch>();
90+
private string _filter = string.Empty;
91+
private Models.Branch _selectedBranch = null;
92+
}
93+
}

src/ViewModels/RepositoryCommandPalette.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ public RepositoryCommandPalette(Launcher launcher, Repository repo)
4747
_launcher.OpenCommandPalette(sub);
4848
}));
4949

50+
_cmds.Add(new("Merge", App.Text("Merge") + "...", false, () =>
51+
{
52+
var sub = new MergeCommandPalette(_launcher, _repo);
53+
_launcher.OpenCommandPalette(sub);
54+
}));
55+
5056
_visibleCmds = _cmds;
5157
}
5258

src/Views/Launcher.axaml

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -132,18 +132,7 @@
132132
<Border Background="{DynamicResource Brush.Popup}" CornerRadius="8">
133133
<ContentControl Margin="16,10,16,12" Content="{Binding CommandPalette}">
134134
<ContentControl.DataTemplates>
135-
<DataTemplate DataType="vm:LauncherPagesCommandPalette">
136-
<v:LauncherPagesCommandPalette/>
137-
</DataTemplate>
138-
<DataTemplate DataType="vm:RepositoryCommandPalette">
139-
<v:RepositoryCommandPalette/>
140-
</DataTemplate>
141-
<DataTemplate DataType="vm:FileHistoryCommandPalette">
142-
<v:FileHistoryCommandPalette/>
143-
</DataTemplate>
144-
<DataTemplate DataType="vm:BlameCommandPalette">
145-
<v:BlameCommandPalette/>
146-
</DataTemplate>
135+
<v:CommandPaletteDataTemplates/>
147136
</ContentControl.DataTemplates>
148137
</ContentControl>
149138
</Border>

src/Views/Launcher.axaml.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
using Avalonia;
44
using Avalonia.Controls;
5+
using Avalonia.Controls.Templates;
56
using Avalonia.Input;
67
using Avalonia.Interactivity;
78
using Avalonia.Media;
@@ -10,6 +11,12 @@
1011

1112
namespace SourceGit.Views
1213
{
14+
public class CommandPaletteDataTemplates : IDataTemplate
15+
{
16+
public Control Build(object param) => App.CreateViewForViewModel(param);
17+
public bool Match(object data) => true;
18+
}
19+
1320
public partial class Launcher : ChromelessWindow
1421
{
1522
public static readonly StyledProperty<GridLength> CaptionHeightProperty =
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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.MergeCommandPalette"
11+
x:DataType="vm:MergeCommandPalette">
12+
<Grid RowDefinitions="Auto,Auto">
13+
<TextBox Grid.Row="0"
14+
x:Name="FilterTextBox"
15+
Height="24"
16+
Margin="4,8,4,0"
17+
BorderThickness="1"
18+
CornerRadius="12"
19+
Text="{Binding Filter, Mode=TwoWay}"
20+
BorderBrush="{DynamicResource Brush.Border2}"
21+
VerticalContentAlignment="Center"
22+
v:AutoFocusBehaviour.IsEnabled="True">
23+
<TextBox.InnerLeftContent>
24+
<StackPanel Orientation="Horizontal">
25+
<Path Width="14" Height="14"
26+
Margin="6,0,0,0"
27+
Fill="{DynamicResource Brush.FG2}"
28+
Data="{StaticResource Icons.Search}"/>
29+
<Border BorderThickness="0"
30+
Background="{DynamicResource Brush.Badge}"
31+
Height="18"
32+
CornerRadius="4"
33+
Margin="4,0" Padding="4,0">
34+
<TextBlock Text="{DynamicResource Text.Merge}"
35+
Foreground="Black"
36+
FontWeight="Bold"/>
37+
</Border>
38+
</StackPanel>
39+
</TextBox.InnerLeftContent>
40+
41+
<TextBox.InnerRightContent>
42+
<Button Classes="icon_button"
43+
Width="16"
44+
Margin="0,0,6,0"
45+
Command="{Binding ClearFilter}"
46+
IsVisible="{Binding Filter, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
47+
HorizontalAlignment="Right">
48+
<Path Width="14" Height="14"
49+
Margin="0,1,0,0"
50+
Fill="{DynamicResource Brush.FG1}"
51+
Data="{StaticResource Icons.Clear}"/>
52+
</Button>
53+
</TextBox.InnerRightContent>
54+
</TextBox>
55+
56+
<ListBox Grid.Row="1"
57+
x:Name="BranchListBox"
58+
MaxHeight="250"
59+
Margin="4,8,4,0"
60+
BorderThickness="0"
61+
SelectionMode="Single"
62+
Background="Transparent"
63+
Focusable="True"
64+
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
65+
ScrollViewer.VerticalScrollBarVisibility="Auto"
66+
ItemsSource="{Binding Branches, Mode=OneWay}"
67+
SelectedItem="{Binding SelectedBranch, Mode=TwoWay}"
68+
IsVisible="{Binding Branches, Converter={x:Static c:ListConverters.IsNotNullOrEmpty}}">
69+
<ListBox.Styles>
70+
<Style Selector="ListBoxItem">
71+
<Setter Property="Padding" Value="8,0"/>
72+
<Setter Property="MinHeight" Value="26"/>
73+
<Setter Property="CornerRadius" Value="4"/>
74+
</Style>
75+
76+
<Style Selector="ListBox">
77+
<Setter Property="FocusAdorner">
78+
<FocusAdornerTemplate>
79+
<Grid/>
80+
</FocusAdornerTemplate>
81+
</Setter>
82+
</Style>
83+
</ListBox.Styles>
84+
85+
<ListBox.ItemsPanel>
86+
<ItemsPanelTemplate>
87+
<VirtualizingStackPanel Orientation="Vertical"/>
88+
</ItemsPanelTemplate>
89+
</ListBox.ItemsPanel>
90+
91+
<ListBox.ItemTemplate>
92+
<DataTemplate DataType="m:Branch">
93+
<Grid ColumnDefinitions="Auto,*" Background="Transparent" Tapped="OnItemTapped">
94+
<Path Grid.Column="0"
95+
Width="12" Height="12"
96+
Data="{StaticResource Icons.Branch}"
97+
IsHitTestVisible="False"/>
98+
<TextBlock Grid.Column="1"
99+
Margin="4,0,0,0"
100+
VerticalAlignment="Center"
101+
IsHitTestVisible="False"
102+
Text="{Binding FriendlyName, Mode=OneWay}"/>
103+
</Grid>
104+
</DataTemplate>
105+
</ListBox.ItemTemplate>
106+
</ListBox>
107+
</Grid>
108+
</UserControl>
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using Avalonia.Controls;
2+
using Avalonia.Input;
3+
4+
namespace SourceGit.Views
5+
{
6+
public partial class MergeCommandPalette : UserControl
7+
{
8+
public MergeCommandPalette()
9+
{
10+
InitializeComponent();
11+
}
12+
13+
protected override void OnKeyDown(KeyEventArgs e)
14+
{
15+
base.OnKeyDown(e);
16+
17+
if (DataContext is not ViewModels.MergeCommandPalette vm)
18+
return;
19+
20+
if (e.Key == Key.Enter)
21+
{
22+
vm.Launch();
23+
e.Handled = true;
24+
}
25+
else if (e.Key == Key.Up)
26+
{
27+
if (BranchListBox.IsKeyboardFocusWithin)
28+
{
29+
FilterTextBox.Focus(NavigationMethod.Directional);
30+
e.Handled = true;
31+
return;
32+
}
33+
}
34+
else if (e.Key == Key.Down || e.Key == Key.Tab)
35+
{
36+
if (FilterTextBox.IsKeyboardFocusWithin)
37+
{
38+
if (vm.Branches.Count > 0)
39+
{
40+
BranchListBox.Focus(NavigationMethod.Directional);
41+
vm.SelectedBranch = vm.Branches[0];
42+
}
43+
44+
e.Handled = true;
45+
return;
46+
}
47+
48+
if (BranchListBox.IsKeyboardFocusWithin && e.Key == Key.Tab)
49+
{
50+
FilterTextBox.Focus(NavigationMethod.Directional);
51+
e.Handled = true;
52+
return;
53+
}
54+
}
55+
}
56+
57+
private void OnItemTapped(object sender, TappedEventArgs e)
58+
{
59+
if (DataContext is ViewModels.MergeCommandPalette vm)
60+
{
61+
vm.Launch();
62+
e.Handled = true;
63+
}
64+
}
65+
}
66+
}

0 commit comments

Comments
 (0)