diff --git a/SourceGit/App.xaml.cs b/SourceGit/App.xaml.cs index 2ba3756d..9f5e8de3 100644 --- a/SourceGit/App.xaml.cs +++ b/SourceGit/App.xaml.cs @@ -36,6 +36,14 @@ namespace SourceGit { set; } + /// + /// Get main window. + /// + public static UI.Launcher Launcher { + get; + private set; + } + /// /// Raise error message. /// @@ -92,8 +100,8 @@ namespace SourceGit { } // Show main window - var launcher = new UI.Launcher(); - launcher.Show(); + Launcher = new UI.Launcher(); + Launcher.Show(); } /// diff --git a/SourceGit/Git/Repository.cs b/SourceGit/Git/Repository.cs index 087ed5f4..9efc74e3 100644 --- a/SourceGit/Git/Repository.cs +++ b/SourceGit/Git/Repository.cs @@ -16,7 +16,6 @@ namespace SourceGit.Git { #region HOOKS public static Action OnOpen = null; - public static Action OnClose = null; [XmlIgnore] public Action OnNavigateCommit = null; [XmlIgnore] public Action OnWorkingCopyChanged = null; [XmlIgnore] public Action OnTagChanged = null; @@ -355,7 +354,7 @@ namespace SourceGit.Git { releasePrefix = null; hotfixPrefix = null; - OnClose?.Invoke(); + GC.Collect(); } #endregion diff --git a/SourceGit/UI/AddSubmodule.xaml.cs b/SourceGit/UI/AddSubmodule.xaml.cs index 6bd46603..cbb210ab 100644 --- a/SourceGit/UI/AddSubmodule.xaml.cs +++ b/SourceGit/UI/AddSubmodule.xaml.cs @@ -34,7 +34,8 @@ namespace SourceGit.UI { /// /// public static void Show(Git.Repository repo) { - PopupManager.Show(new AddSubmodule(repo)); + var popup = App.Launcher.GetPopupManager(repo); + popup?.Show(new AddSubmodule(repo)); } #region EVENTS @@ -57,14 +58,17 @@ namespace SourceGit.UI { if (Validation.GetHasError(txtPath)) return; var recursive = chkRecursive.IsChecked == true; + var popup = App.Launcher.GetPopupManager(repo); - PopupManager.Lock(); - await Task.Run(() => repo.AddSubmodule(RepoURL, LocalPath, recursive, PopupManager.UpdateStatus)); - PopupManager.Close(true); + popup?.Lock(); + await Task.Run(() => repo.AddSubmodule(RepoURL, LocalPath, recursive, msg => { + popup?.UpdateStatus(msg); + })); + popup?.Close(true); } private void Cancel(object sender, RoutedEventArgs e) { - PopupManager.Close(); + App.Launcher.GetPopupManager(repo)?.Close(); } #endregion } diff --git a/SourceGit/UI/Apply.xaml.cs b/SourceGit/UI/Apply.xaml.cs index e1452801..e180a07c 100644 --- a/SourceGit/UI/Apply.xaml.cs +++ b/SourceGit/UI/Apply.xaml.cs @@ -52,7 +52,8 @@ namespace SourceGit.UI { /// /// public static void Show(Git.Repository opened) { - PopupManager.Show(new Apply(opened)); + var popup = App.Launcher.GetPopupManager(opened); + popup?.Show(new Apply(opened)); } /// @@ -82,13 +83,14 @@ namespace SourceGit.UI { txtPatchFile.GetBindingExpression(TextBox.TextProperty).UpdateSource(); if (Validation.GetHasError(txtPatchFile)) return; - PopupManager.Lock(); + var popup = App.Launcher.GetPopupManager(repo); + popup?.Lock(); var mode = combWhitespaceOptions.SelectedItem as WhitespaceOption; var ignoreSpaceChanges = chkIgnoreWhitespace.IsChecked == true; await Task.Run(() => repo.Apply(PatchFile, ignoreSpaceChanges, mode.Arg)); - PopupManager.Close(true); + popup?.Close(true); } /// @@ -97,7 +99,7 @@ namespace SourceGit.UI { /// /// private void Cancel(object sender, RoutedEventArgs e) { - PopupManager.Close(); + App.Launcher.GetPopupManager(repo)?.Close(); } } } diff --git a/SourceGit/UI/Blame.xaml.cs b/SourceGit/UI/Blame.xaml.cs index abf44055..3bf3fbeb 100644 --- a/SourceGit/UI/Blame.xaml.cs +++ b/SourceGit/UI/Blame.xaml.cs @@ -36,7 +36,7 @@ namespace SourceGit.UI { double minWidth = content.ActualWidth; // Move to center. - var parent = App.Current.MainWindow; + var parent = App.Launcher; Left = parent.Left + (parent.Width - Width) * 0.5; Top = parent.Top + (parent.Height - Height) * 0.5; diff --git a/SourceGit/UI/CherryPick.xaml.cs b/SourceGit/UI/CherryPick.xaml.cs index 0c77b147..f8665a1d 100644 --- a/SourceGit/UI/CherryPick.xaml.cs +++ b/SourceGit/UI/CherryPick.xaml.cs @@ -1,4 +1,4 @@ -锘縰sing System.Windows; +using System.Windows; using System.Windows.Controls; namespace SourceGit.UI { @@ -29,7 +29,8 @@ namespace SourceGit.UI { /// /// public static void Show(Git.Repository repo, Git.Commit commit) { - PopupManager.Show(new CherryPick(repo, commit)); + var popup = App.Launcher.GetPopupManager(repo); + popup?.Show(new CherryPick(repo, commit)); } /// @@ -39,7 +40,9 @@ namespace SourceGit.UI { /// private void Start(object sender, RoutedEventArgs e) { repo.CherryPick(commitSHA, chkCommitChanges.IsChecked != true); - PopupManager.Close(); + + var popup = App.Launcher.GetPopupManager(repo); + popup?.Close(); } /// @@ -48,7 +51,8 @@ namespace SourceGit.UI { /// /// private void Cancel(object sender, RoutedEventArgs e) { - PopupManager.Close(); + var popup = App.Launcher.GetPopupManager(repo); + popup?.Close(); } } } diff --git a/SourceGit/UI/Clone.xaml.cs b/SourceGit/UI/Clone.xaml.cs index 658a5c1b..e05b1edf 100644 --- a/SourceGit/UI/Clone.xaml.cs +++ b/SourceGit/UI/Clone.xaml.cs @@ -42,7 +42,8 @@ namespace SourceGit.UI { /// Show clone dialog. /// public static void Show() { - PopupManager.Show(new Clone()); + var popup = App.Launcher.GetPopupManager(null); + popup?.Show(new Clone()); } /// @@ -91,16 +92,17 @@ namespace SourceGit.UI { rName = RemoteName; } - PopupManager.Lock(); + var popup = App.Launcher.GetPopupManager(null); + popup.Lock(); var repo = await Task.Run(() => { - return Git.Repository.Clone(RemoteUri, ParentFolder, rName, repoName, PopupManager.UpdateStatus); + return Git.Repository.Clone(RemoteUri, ParentFolder, rName, repoName, popup.UpdateStatus); }); if (repo == null) { - PopupManager.Unlock(); + popup.Unlock(); } else { - PopupManager.Close(true); + popup.Close(true); repo.Open(); } } @@ -111,7 +113,7 @@ namespace SourceGit.UI { /// /// private void Cancel(object sender, RoutedEventArgs e) { - PopupManager.Close(); + App.Launcher.GetPopupManager(null).Close(); } } } diff --git a/SourceGit/UI/Configure.xaml.cs b/SourceGit/UI/Configure.xaml.cs index 2a459be6..f416297d 100644 --- a/SourceGit/UI/Configure.xaml.cs +++ b/SourceGit/UI/Configure.xaml.cs @@ -43,7 +43,8 @@ namespace SourceGit.UI { /// /// public static void Show(Git.Repository repo) { - PopupManager.Show(new Configure(repo)); + var popup = App.Launcher.GetPopupManager(repo); + popup?.Show(new Configure(repo)); } #region EVENTS @@ -63,7 +64,7 @@ namespace SourceGit.UI { } private void Close(object sender, RoutedEventArgs e) { - PopupManager.Close(); + App.Launcher.GetPopupManager(repo)?.Close(); } #endregion } diff --git a/SourceGit/UI/CreateBranch.xaml.cs b/SourceGit/UI/CreateBranch.xaml.cs index 4edd8d41..12fdc908 100644 --- a/SourceGit/UI/CreateBranch.xaml.cs +++ b/SourceGit/UI/CreateBranch.xaml.cs @@ -58,7 +58,9 @@ namespace SourceGit.UI { dialog.basedOnDesc.Content = branch.Name; if (!branch.IsLocal) dialog.txtName.Text = branch.Name.Substring(branch.Remote.Length + 1); - PopupManager.Show(dialog); + + var popup = App.Launcher.GetPopupManager(repo); + popup?.Show(dialog); } /// @@ -71,7 +73,9 @@ namespace SourceGit.UI { dialog.based = tag.Name; dialog.basedOnType.Data = dialog.FindResource("Icon.Tag") as Geometry; dialog.basedOnDesc.Content = tag.Name; - PopupManager.Show(dialog); + + var popup = App.Launcher.GetPopupManager(repo); + popup?.Show(dialog); } /// @@ -84,7 +88,9 @@ namespace SourceGit.UI { dialog.based = commit.SHA; dialog.basedOnType.Data = dialog.FindResource("Icon.Commit") as Geometry; dialog.basedOnDesc.Content = $"{commit.ShortSHA} {commit.Subject}"; - PopupManager.Show(dialog); + + var popup = App.Launcher.GetPopupManager(repo); + popup?.Show(dialog); } /// @@ -96,7 +102,8 @@ namespace SourceGit.UI { txtName.GetBindingExpression(TextBox.TextProperty).UpdateSource(); if (Validation.GetHasError(txtName)) return; - PopupManager.Lock(); + var popup = App.Launcher.GetPopupManager(repo); + popup?.Lock(); bool checkout = chkCheckout.IsChecked == true; await Task.Run(() => { @@ -119,7 +126,7 @@ namespace SourceGit.UI { } }); - PopupManager.Close(true); + popup?.Close(true); } /// @@ -128,7 +135,7 @@ namespace SourceGit.UI { /// /// private void Cancel(object sender, RoutedEventArgs e) { - PopupManager.Close(); + App.Launcher.GetPopupManager(repo)?.Close(); } } } diff --git a/SourceGit/UI/CreateTag.xaml.cs b/SourceGit/UI/CreateTag.xaml.cs index 2c87039f..17e1d940 100644 --- a/SourceGit/UI/CreateTag.xaml.cs +++ b/SourceGit/UI/CreateTag.xaml.cs @@ -1,4 +1,4 @@ -锘縰sing System.Linq; +using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Media; @@ -51,7 +51,9 @@ namespace SourceGit.UI { dialog.based = branch.Head; dialog.basedOnType.Data = dialog.FindResource("Icon.Branch") as Geometry; dialog.basedOnDesc.Content = branch.Name; - PopupManager.Show(dialog); + + var popup = App.Launcher.GetPopupManager(repo); + popup?.Show(dialog); } /// @@ -64,7 +66,9 @@ namespace SourceGit.UI { dialog.based = commit.SHA; dialog.basedOnType.Data = dialog.FindResource("Icon.Commit") as Geometry; dialog.basedOnDesc.Content = $"{commit.ShortSHA} {commit.Subject}"; - PopupManager.Show(dialog); + + var popup = App.Launcher.GetPopupManager(repo); + popup?.Show(dialog); } /// @@ -77,7 +81,9 @@ namespace SourceGit.UI { if (Validation.GetHasError(tagName)) return; Git.Tag.Add(repo, TagName, based, tagMessage.Text); - PopupManager.Close(); + + var popup = App.Launcher.GetPopupManager(repo); + popup?.Close(); } /// @@ -86,7 +92,7 @@ namespace SourceGit.UI { /// /// private void Cancel(object sender, RoutedEventArgs e) { - PopupManager.Close(); + App.Launcher.GetPopupManager(repo)?.Close(); } } } diff --git a/SourceGit/UI/Dashboard.xaml b/SourceGit/UI/Dashboard.xaml index 478e14fb..d48a68bb 100644 --- a/SourceGit/UI/Dashboard.xaml +++ b/SourceGit/UI/Dashboard.xaml @@ -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"> @@ -34,41 +33,19 @@ - + - + - - - - - - - - - - + - + + + + + + + + + + + + + + + + + @@ -105,11 +201,32 @@ - - + + + + + + + + + + + + + + + + - + diff --git a/SourceGit/UI/Launcher.xaml.cs b/SourceGit/UI/Launcher.xaml.cs index bf8f0e11..24d78e47 100644 --- a/SourceGit/UI/Launcher.xaml.cs +++ b/SourceGit/UI/Launcher.xaml.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.ObjectModel; using System.Windows; using System.Windows.Controls; @@ -11,6 +10,16 @@ namespace SourceGit.UI { /// public partial class Launcher : Window { + /// + /// Tab data. + /// + public class Tab { + public string Title { get; set; } + public bool IsActive { get; set; } + public Git.Repository Repo { get; set; } + public object Page { get; set; } + } + /// /// Alert data. /// @@ -24,37 +33,82 @@ namespace SourceGit.UI { /// public ObservableCollection Alerts { get; set; } = new ObservableCollection(); + /// + /// Opened tabs. + /// + public ObservableCollection Tabs { get; set; } = new ObservableCollection(); + /// /// Constructor /// public Launcher() { - Git.Repository.OnOpen = ShowDashboard; - Git.Repository.OnClose = ShowManager; + App.OnError = msg => { + ShowAlert(new Alert() { Title = "ERROR", Message = msg }); + }; - App.OnError = msg => ShowAlert(new Alert() { Title = "ERROR", Message = msg }); + Git.Repository.OnOpen = repo => { + Dispatcher.Invoke(() => { + foreach (var item in openedTabs.Items) { + var opened = item as Tab; + if (opened != null && opened.Repo != null && repo.Path == opened.Repo.Path) { + openedTabs.SelectedItem = opened; + return; + } + } + + var tab = new Tab() { + Title = repo.Parent == null ? repo.Name : $"{repo.Parent.Name} : {repo.Name}", + Repo = repo, + Page = new Dashboard(repo), + }; + + Tabs.Add(tab); + openedTabs.SelectedItem = tab; + }); + }; + + Tabs.Add(new Tab() { + Title = "Repositories", + Page = new Manager(), + }); InitializeComponent(); - ShowManager(); + openedTabs.SelectedItem = Tabs[0]; + } + + /// + /// Get popup manager from given active page. + /// + /// + /// + public PopupManager GetPopupManager(Git.Repository repo) { + if (repo == null) return (Tabs[0].Page as Manager).popupManager; + + foreach (var tab in Tabs) { + if (tab.Repo != null && tab.Repo.Path == repo.Path) { + return (tab.Page as Dashboard).popupManager; + } + } + + return null; } #region LAYOUT_CONTENT /// - /// Show manager. + /// Close repository tab. /// - private void ShowManager() { - Dispatcher.Invoke(() => { - body.Content = new Manager(); - }); - } + /// + /// + private void CloseRepo(object sender, RoutedEventArgs e) { + var tab = (sender as Button).DataContext as Tab; + if (tab == null || tab.Repo == null) return; - /// - /// Show dashboard. - /// - /// - private void ShowDashboard(Git.Repository repo) { - Dispatcher.Invoke(() => { - body.Content = new Dashboard(repo); - }); + var popup = (tab.Page as Dashboard).popupManager; + if (popup.IsLocked()) popup.Close(true); + Tabs.Remove(tab); + + tab.Page = null; + tab.Repo.Close(); } /// @@ -63,7 +117,9 @@ namespace SourceGit.UI { /// /// private void ShowPreference(object sender, RoutedEventArgs e) { - Preference.Show(); + var dialog = new Preference(); + dialog.Owner = this; + dialog.ShowDialog(); } /// diff --git a/SourceGit/UI/Manager.xaml b/SourceGit/UI/Manager.xaml index 8949dec7..520bd6bb 100644 --- a/SourceGit/UI/Manager.xaml +++ b/SourceGit/UI/Manager.xaml @@ -8,37 +8,17 @@ xmlns:git="clr-namespace:SourceGit.Git" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> - - - - - - - - - - - - - - - - - - - - + - + - + - + @@ -148,7 +128,7 @@ - + @@ -161,7 +141,7 @@ - + @@ -176,7 +156,7 @@ - + - + @@ -217,8 +197,8 @@ - - - + + + diff --git a/SourceGit/UI/Manager.xaml.cs b/SourceGit/UI/Manager.xaml.cs index 65fff061..c8bcbf4b 100644 --- a/SourceGit/UI/Manager.xaml.cs +++ b/SourceGit/UI/Manager.xaml.cs @@ -33,11 +33,6 @@ namespace SourceGit.UI { /// Constructor. /// public Manager() { - Loaded += async (o, e) => { - await Task.Delay(500); - GC.Collect(); - }; - InitializeComponent(); UpdateRecentOpened(); UpdateTree(); diff --git a/SourceGit/UI/Merge.xaml.cs b/SourceGit/UI/Merge.xaml.cs index 1eb132b3..422e4a28 100644 --- a/SourceGit/UI/Merge.xaml.cs +++ b/SourceGit/UI/Merge.xaml.cs @@ -53,7 +53,8 @@ namespace SourceGit.UI { /// /// public static void Show(Git.Repository opened, string source, string dest) { - PopupManager.Show(new Merge(opened, source, dest)); + var popup = App.Launcher.GetPopupManager(opened); + popup?.Show(new Merge(opened, source, dest)); } /// @@ -64,13 +65,14 @@ namespace SourceGit.UI { /// public static void StartDirectly(Git.Repository opened, string source, string dest) { var merge = new Merge(opened, source, dest); - PopupManager.Show(merge); - PopupManager.Lock(); + var popup = App.Launcher.GetPopupManager(opened); + popup?.Show(merge); + popup?.Lock(); Task.Run(() => { opened.Merge(source, ""); merge.Dispatcher.Invoke(() => { - PopupManager.Close(true); + popup?.Close(true); }); }); } @@ -81,13 +83,14 @@ namespace SourceGit.UI { /// /// private async void Start(object sender, RoutedEventArgs e) { - PopupManager.Lock(); + var popup = App.Launcher.GetPopupManager(repo); + popup?.Lock(); var branch = sourceBranch.Content as string; var opt = combOptions.SelectedItem as Option; await Task.Run(() => repo.Merge(branch, opt.Arg)); - PopupManager.Close(true); + popup?.Close(true); } /// @@ -96,7 +99,7 @@ namespace SourceGit.UI { /// /// private void Cancel(object sender, RoutedEventArgs e) { - PopupManager.Close(); + App.Launcher.GetPopupManager(repo)?.Close(); } } } diff --git a/SourceGit/UI/PopupManager.xaml.cs b/SourceGit/UI/PopupManager.xaml.cs index 7a856756..948bc8bf 100644 --- a/SourceGit/UI/PopupManager.xaml.cs +++ b/SourceGit/UI/PopupManager.xaml.cs @@ -10,14 +10,12 @@ namespace SourceGit.UI { /// Common popup manager. /// public partial class PopupManager : UserControl { - private static PopupManager instance = null; - private static bool locked = false; + private bool locked = false; /// /// Constructor. /// public PopupManager() { - instance = this; InitializeComponent(); } @@ -25,8 +23,8 @@ namespace SourceGit.UI { /// Show content as popup. /// /// - public static void Show(UIElement elem) { - if (instance == null || locked) return; + public void Show(UIElement elem) { + if (locked) return; var gone = new Thickness(0, -(double)elem.GetValue(HeightProperty) - 16, 0, 0); @@ -35,53 +33,49 @@ namespace SourceGit.UI { anim.From = gone; anim.To = new Thickness(0); - instance.statusMsg.Content = ""; - instance.popupContent.Child = elem; - instance.popupContent.Margin = gone; - instance.Visibility = Visibility.Visible; - instance.popupContent.BeginAnimation(MarginProperty, anim); + statusMsg.Content = ""; + popupContent.Child = elem; + popupContent.Margin = gone; + Visibility = Visibility.Visible; + popupContent.BeginAnimation(MarginProperty, anim); } /// /// Is current locked. /// /// - public static bool IsLocked() { + public bool IsLocked() { return locked; } /// /// Lock /// - public static void Lock() { - if (instance == null) return; + public void Lock() { locked = true; + status.Visibility = Visibility.Visible; - instance.status.Visibility = Visibility.Visible; DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1)); anim.RepeatBehavior = RepeatBehavior.Forever; - instance.statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim); + statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim); } /// /// Unlock /// - public static void Unlock() { - if (instance == null) return; + public void Unlock() { locked = false; - instance.statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null); - instance.status.Visibility = Visibility.Collapsed; + statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null); + status.Visibility = Visibility.Collapsed; } /// /// Update status description /// /// - public static void UpdateStatus(string desc) { - if (instance == null) return; - - instance.Dispatcher.Invoke(() => { - instance.statusMsg.Content = desc; + public void UpdateStatus(string desc) { + Dispatcher.Invoke(() => { + statusMsg.Content = desc; }); } @@ -89,23 +83,23 @@ namespace SourceGit.UI { /// Close current popup. /// /// - public static void Close(bool unlockFirst = false) { - if (instance == null) return; - if (instance.popupContent.Child == null) return; + public void Close(bool unlockFirst = false) { + if (popupContent.Child == null) return; if (locked && !unlockFirst) return; locked = false; ThicknessAnimation anim = new ThicknessAnimation(); anim.Duration = TimeSpan.FromMilliseconds(150); anim.From = new Thickness(0); - anim.To = new Thickness(0, -(double)instance.popupContent.Child.GetValue(HeightProperty) - 16, 0, 0); + anim.To = new Thickness(0, -(double)popupContent.Child.GetValue(HeightProperty) - 16, 0, 0); anim.Completed += (obj, ev) => { - instance.Visibility = Visibility.Collapsed; - instance.popupContent.Child = null; + Visibility = Visibility.Collapsed; + popupContent.Child = null; }; - instance.popupContent.BeginAnimation(MarginProperty, anim); - instance.statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null); - instance.status.Visibility = Visibility.Collapsed; + + popupContent.BeginAnimation(MarginProperty, anim); + statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null); + status.Visibility = Visibility.Collapsed; } /// diff --git a/SourceGit/UI/Preference.xaml b/SourceGit/UI/Preference.xaml index 2ced4374..52ab8e5e 100644 --- a/SourceGit/UI/Preference.xaml +++ b/SourceGit/UI/Preference.xaml @@ -1,4 +1,4 @@ - - - - - - - - - - - - - - - - - - - - - - + Height="520" Width="500" + Title="Preference" + WindowStartupLocation="CenterOwner" ResizeMode="NoResize"> - - - - - - -