feature<Launcher>: close tabs by context menu; collect garbage after repository closed

This commit is contained in:
leo 2020-11-30 15:21:45 +08:00
parent a9f138076e
commit 1a46551a77
14 changed files with 145 additions and 103 deletions

View file

@ -90,6 +90,14 @@ namespace SourceGit {
Current.MainWindow.Show();
}
/// <summary>
/// Open repository.
/// </summary>
/// <param name="repo"></param>
public static void Open(Git.Repository repo) {
(Current.MainWindow as UI.Launcher).Open(repo);
}
/// <summary>
/// Deactivated event.
/// </summary>

View file

@ -15,7 +15,6 @@ namespace SourceGit.Git {
public class Repository {
#region HOOKS
public static Action<Repository> OnOpen = null;
[XmlIgnore] public Action<string> OnNavigateCommit = null;
[XmlIgnore] public Action OnWorkingCopyChanged = null;
[XmlIgnore] public Action OnTagChanged = null;
@ -23,6 +22,7 @@ namespace SourceGit.Git {
[XmlIgnore] public Action OnBranchChanged = null;
[XmlIgnore] public Action OnCommitsChanged = null;
[XmlIgnore] public Action OnSubmoduleChanged = null;
[XmlIgnore] public Action OnClosing = null;
#endregion
#region PROPERTIES_SAVED
@ -294,14 +294,14 @@ namespace SourceGit.Git {
featurePrefix = GetConfig("gitflow.prefix.feature");
releasePrefix = GetConfig("gitflow.prefix.release");
hotfixPrefix = GetConfig("gitflow.prefix.hotfix");
OnOpen?.Invoke(this);
}
/// <summary>
/// Close repository.
/// </summary>
public void Close() {
OnClosing?.Invoke();
OnBranchChanged = null;
OnCommitsChanged = null;
OnTagChanged = null;
@ -309,6 +309,7 @@ namespace SourceGit.Git {
OnWorkingCopyChanged = null;
OnNavigateCommit = null;
OnSubmoduleChanged = null;
OnClosing = null;
cachedBranches.Clear();
cachedRemotes.Clear();

View file

@ -4,6 +4,7 @@
<Geometry x:Key="Icon.Submodule">M557.696 545.347L789.873 402.66c23.998-14.999 31.297-46.496 16.398-70.493-14.798-23.798-45.995-31.197-69.993-16.699L506.501 456.555 277.123 315.37c-24.098-14.798-55.595-7.3-70.493 16.799-14.799 24.097-7.3 55.594 16.798 70.493l231.778 142.586V819.12c0 28.297 22.897 51.195 51.195 51.195 28.297 0 51.195-22.898 51.195-51.195V545.347h0.1zM506.5 0l443.356 255.975v511.95L506.501 1023.9 63.144 767.925v-511.95L506.5 0z</Geometry>
<Geometry x:Key="Icon.LFS">M169.984 470.016l0 84.010667 86.016 0 0-84.010667-86.016 0zM86.016 598.016l0-171.989333 852.010667 0 0 171.989333-852.010667 0zM256 297.984l0-84.010667-86.016 0 0 84.010667 86.016 0zM86.016 169.984l852.010667 0 0 171.989333-852.010667 0 0-171.989333zM169.984 726.016l0 84.010667 86.016 0 0-84.010667-86.016 0zM86.016 854.016l0-171.989333 852.010667 0 0 171.989333-852.010667 0z</Geometry>
<Geometry x:Key="Icon.User">M 841.758 299.375 c 0 165.25 -134 299.375 -299.375 299.375 S 243.009 464.75 243.009 299.375 S 377.009 0 542.383 0 c 165.25 0 299.375 134 299.375 299.375 Z m 0 0 M 789.383 612.75 c -69.75 55.125 -156.25 85.625 -247.125 85.625 c -91.875 0 -179.25 -31.25 -249.25 -87.375 C 108.384 678.875 25.8838 915.75 25.8838 1024 h 1027.75 c 0 -107.25 -83.125 -342.5 -264.25 -411.25 Z m 0 0</Geometry>
<Geometry x:Key="Icon.Home">M1024 590.432 512 193.024 0 590.432 0 428.416 512 30.976 1024 428.416ZM896 576 896 960 640 960 640 704 384 704 384 960 128 960 128 576 512 288Z</Geometry>
<Geometry x:Key="Icon.ScrollLeft">M753.613 996.727L269.38 511.505 754.602 27.272z</Geometry>
<Geometry x:Key="Icon.ScrollRight">M270.387 27.273L754.62 512.495 269.398 996.728z</Geometry>

View file

@ -7,8 +7,7 @@
xmlns:local="clr-namespace:SourceGit.UI"
xmlns:converters="clr-namespace:SourceGit.Converters"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Unloaded="Cleanup">
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<RoutedUICommand x:Key="OpenSearchBarCommand" Text="OpenSearchBar"/>
<RoutedUICommand x:Key="HideSearchBarCommand" Text="HideSearchBar"/>

View file

@ -50,7 +50,8 @@ namespace SourceGit.UI {
/// Constructor.
/// </summary>
/// <param name="repo">Opened repository.</param>
public Dashboard(Git.Repository opened) {
public Dashboard(Git.Repository opened) {
opened.OnClosing = Cleanup;
opened.OnWorkingCopyChanged = UpdateLocalChanges;
opened.OnTagChanged = UpdateTags;
opened.OnStashChanged = UpdateStashes;
@ -78,6 +79,22 @@ namespace SourceGit.UI {
UpdateSubmodules();
}
/// <summary>
/// Cleanup
/// </summary>
public void Cleanup() {
repo = null;
localBranchTree.ItemsSource = null;
remoteBranchTree.ItemsSource = null;
tagList.ItemsSource = null;
cachedLocalBranches.Clear();
cachedRemotes.Clear();
histories.Cleanup();
commits.Cleanup();
stashes.Cleanup();
}
#region DATA_UPDATE
private void UpdateHistories() {
Dispatcher.Invoke(() => {
@ -287,14 +304,6 @@ namespace SourceGit.UI {
});
});
}
private void Cleanup(object sender, RoutedEventArgs e) {
localBranchTree.ItemsSource = null;
remoteBranchTree.ItemsSource = null;
tagList.ItemsSource = null;
cachedLocalBranches.Clear();
cachedRemotes.Clear();
}
#endregion
#region TOOLBAR
@ -1021,7 +1030,7 @@ namespace SourceGit.UI {
sub.Name = Path.GetFileName(path);
sub.Parent = repo;
if (!sub.BringUpTab()) sub.Open();
App.Open(sub);
}
#endregion

View file

@ -71,6 +71,9 @@ namespace SourceGit.UI {
/// </summary>
public void Reset() {
mask.Visibility = Visibility.Visible;
lineChanges = null;
foreach (var editor in editors) editorContainer.Children.Remove(editor);
editors.Clear();
}
/// <summary>

View file

@ -10,8 +10,7 @@
xmlns:sourcegit="clr-namespace:SourceGit"
xmlns:converters="clr-namespace:SourceGit.Converters"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Unloaded="Cleanup">
d:DesignHeight="450" d:DesignWidth="800">
<Grid x:Name="layout" Background="{StaticResource Brush.BG1}">
<!-- List Panel (SearchBar + DataGrid) -->
<Grid x:Name="commitListPanel" Background="{StaticResource Brush.BG2}" ClipToBounds="True">

View file

@ -78,6 +78,15 @@ namespace SourceGit.UI {
}
}
/// <summary>
/// Cleanup
/// </summary>
public void Cleanup() {
commitGraph.Children.Clear();
commitList.ItemsSource = null;
cachedCommits.Clear();
}
#region DATA
public void SetCommits(List<Git.Commit> commits) {
cachedCommits = commits;
@ -184,12 +193,6 @@ namespace SourceGit.UI {
SetLoadingEnabled(false);
});
}
private void Cleanup(object sender, RoutedEventArgs e) {
commitGraph.Children.Clear();
commitList.ItemsSource = null;
cachedCommits.Clear();
}
#endregion
#region SEARCH_BAR
@ -516,7 +519,7 @@ namespace SourceGit.UI {
// Reset
var reset = new MenuItem();
reset.Header = $"Reset '{current.Name}' To Here";
reset.Header = $"Reset '{current.Name}' to Here";
reset.Visibility = commit.IsHEAD ? Visibility.Collapsed : Visibility.Visible;
reset.Click += (o, e) => {
Reset.Show(Repo, commit);
@ -526,7 +529,7 @@ namespace SourceGit.UI {
// Rebase or interactive rebase
var rebase = new MenuItem();
rebase.Header = commit.IsMerged ? $"Interactive Rebase '{current.Name}' From Here" : $"Rebase '{current.Name}' To Here";
rebase.Header = commit.IsMerged ? $"Interactive Rebase '{current.Name}' from Here" : $"Rebase '{current.Name}' to Here";
rebase.Visibility = commit.IsHEAD ? Visibility.Collapsed : Visibility.Visible;
rebase.Click += (o, e) => {
if (commit.IsMerged) {
@ -587,7 +590,7 @@ namespace SourceGit.UI {
// Save as patch
var patch = new MenuItem();
patch.Header = "Save As Patch";
patch.Header = "Save as Patch";
patch.Click += (o, e) => {
FolderDailog.Open("Save patch to ...", saveTo => {
Repo.RunCommand($"format-patch {commit.SHA} -1 -o \"{saveTo}\"", null);

View file

@ -110,7 +110,7 @@
Data="M 0,0 L 0,6 6,6 C 6,6 0,6 0,0 Z"
Fill="Transparent"/>
<StackPanel Orientation="Horizontal" Margin="8,0" VerticalAlignment="Center">
<StackPanel Orientation="Horizontal" Margin="12,0" VerticalAlignment="Center">
<Path Grid.Column="0" Width="14" Height="14" x:Name="Icon" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Git}"/>
<ContentPresenter
@ -119,15 +119,8 @@
TextElement.Foreground="{DynamicResource Brush.FG}"
TextElement.FontWeight="Bold"
ContentSource="Header"
Margin="8,0"
Margin="8,0,0,0"
RecognizesAccessKey="True" />
<Button x:Name="Closer" Background="Transparent" Grid.Column="2" Click="CloseRepo" Visibility="Hidden">
<Button.ToolTip>
<ToolTip Content="CLOSE" FontWeight="Normal"/>
</Button.ToolTip>
<Path Width="8" Height="8" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Close}"/>
</Button>
</StackPanel>
</Grid>
<ControlTemplate.Triggers>
@ -139,16 +132,8 @@
<Setter TargetName="CornerRight" Property="Fill" Value="{StaticResource Brush.BG1}"/>
</Trigger>
<Trigger Property="AllowDrop" Value="False">
<Setter TargetName="Icon" Property="Fill" Value="#FFF05133"/>
<Setter TargetName="Closer" Property="Visibility" Value="Collapsed"/>
<Setter TargetName="Icon" Property="Data" Value="{StaticResource Icon.Home}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="AllowDrop" Value="True"/>
<Condition Property="IsMouseOver" Value="True"/>
</MultiTrigger.Conditions>
<Setter TargetName="Closer" Property="Visibility" Value="Visible"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="False"/>
@ -166,6 +151,7 @@
<EventSetter Event="MouseMove" Handler="TabsMouseMove"/>
<EventSetter Event="Drop" Handler="TabsDrop"/>
<EventSetter Event="ContextMenuOpening" Handler="TabsContextMenuOpening"/>
</Style>
</TabControl.ItemContainerStyle>

View file

@ -36,26 +36,9 @@ namespace SourceGit.UI {
/// Constructor
/// </summary>
public Launcher() {
Git.Repository.OnOpen = repo => {
Dispatcher.Invoke(() => {
var page = new Dashboard(repo);
var tab = new Tab() {
Title = repo.Parent == null ? repo.Name : $"{repo.Parent.Name} : {repo.Name}",
Tooltip = repo.Path,
AllowDragDrop = true,
Repo = repo,
Page = page,
};
repo.SetPopupManager(page.popupManager);
Tabs.Add(tab);
openedTabs.SelectedItem = tab;
});
};
Tabs.Add(new Tab() {
Title = "SOURCE GIT",
Tooltip = "Welcome Page",
Title = "HOME",
Tooltip = "Repositories Manager",
AllowDragDrop = false,
Page = new Manager(),
});
@ -64,21 +47,87 @@ namespace SourceGit.UI {
openedTabs.SelectedItem = Tabs[0];
}
/// <summary>
/// Open repository
/// </summary>
/// <param name="repo"></param>
public void Open(Git.Repository repo) {
for (int i = 1; i < Tabs.Count; i++) {
var opened = Tabs[i];
if (opened.Repo.Path == repo.Path) {
openedTabs.SelectedItem = opened;
return;
}
}
repo.Open();
var page = new Dashboard(repo);
var tab = new Tab() {
Title = repo.Parent == null ? repo.Name : $"{repo.Parent.Name} : {repo.Name}",
Tooltip = repo.Path,
AllowDragDrop = true,
Repo = repo,
Page = page,
};
repo.SetPopupManager(page.popupManager);
Tabs.Add(tab);
openedTabs.SelectedItem = tab;
}
#region LAYOUT_CONTENT
/// <summary>
/// Close repository tab.
/// Context menu for tab items.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void CloseRepo(object sender, RoutedEventArgs e) {
var tab = (sender as Button).DataContext as Tab;
if (tab == null || tab.Repo == null) return;
private void TabsContextMenuOpening(object sender, ContextMenuEventArgs ev) {
var tab = (sender as TabItem).DataContext as Tab;
if (tab == null) {
ev.Handled = true;
return;
}
Tabs.Remove(tab);
tab.Page = null;
tab.Repo.RemovePopup();
tab.Repo.Close();
tab.Repo = null;
var repo = tab.Repo;
if (repo == null) {
ev.Handled = true;
return;
}
var close = new MenuItem();
close.Header = "Close";
close.Click += (o, e) => {
Tabs.Remove(tab);
tab.Page = null;
tab.Repo.RemovePopup();
tab.Repo.Close();
tab.Repo = null;
};
var copyPath = new MenuItem();
copyPath.Header = "Copy Path";
copyPath.Click += (o, e) => {
Clipboard.SetText(repo.Path);
e.Handled = true;
};
var refresh = new MenuItem();
refresh.Header = "Refresh";
refresh.Click += (o, e) => {
repo.AssertCommand(null);
e.Handled = true;
};
var menu = new ContextMenu();
menu.Items.Add(close);
menu.Items.Add(new Separator());
menu.Items.Add(copyPath);
menu.Items.Add(refresh);
menu.IsOpen = true;
ev.Handled = true;
}
/// <summary>
@ -189,29 +238,4 @@ namespace SourceGit.UI {
}
#endregion
}
/// <summary>
/// Extension methods for repository.
/// </summary>
public static class RepositoryTabBindings {
/// <summary>
/// Bring up tab of repository if it was opened before.
/// </summary>
/// <param name="repo"></param>
/// <returns></returns>
public static bool BringUpTab(this Git.Repository repo) {
var main = App.Current.MainWindow as Launcher;
for (int i = 1; i < main.Tabs.Count; i++) {
var opened = main.Tabs[i];
if (opened.Repo.Path == repo.Path) {
main.openedTabs.SelectedItem = opened;
return true;
}
}
return false;
}
}
}

View file

@ -429,7 +429,7 @@ namespace SourceGit.UI {
}
var repo = App.Preference.AddRepository(path, "");
if (!repo.BringUpTab()) repo.Open();
App.Open(repo);
}
/// <summary>

View file

@ -7,8 +7,7 @@
xmlns:git="clr-namespace:SourceGit.Git"
xmlns:converters="clr-namespace:SourceGit.Converters"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Unloaded="Cleanup">
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="{StaticResource Brush.BG3}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300"/>

View file

@ -34,9 +34,7 @@ namespace SourceGit.UI {
/// <summary>
/// Cleanup
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Cleanup(object sender, RoutedEventArgs e) {
public void Cleanup() {
stashList.ItemsSource = null;
changeList.ItemsSource = null;
diff.Reset();

View file

@ -112,6 +112,18 @@ namespace SourceGit.UI {
Validation.ClearInvalid(txtCommitMsg.GetBindingExpression(TextBox.TextProperty));
}
/// <summary>
/// Cleanup
/// </summary>
public void Cleanup() {
Repo = null;
unstagedList.ItemsSource = null;
unstagedList.ItemsSource = null;
stageList.ItemsSource = null;
stageTree.ItemsSource = null;
diffViewer.Reset();
}
#region UNSTAGED
private void UnstagedTreeMultiSelectionChanged(object sender, RoutedEventArgs e) {
mergePanel.Visibility = Visibility.Collapsed;