mirror of
https://github.com/sourcegit-scm/sourcegit
synced 2025-05-24 05:35:00 +00:00
Upgrade project style
This commit is contained in:
parent
afd22ca85d
commit
baa6c1445a
136 changed files with 29 additions and 770 deletions
77
SourceGit/UI/About.xaml
Normal file
77
SourceGit/UI/About.xaml
Normal file
|
@ -0,0 +1,77 @@
|
|||
<Window x:Class="SourceGit.UI.About"
|
||||
x:Name="me"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
Height="280" Width="400"
|
||||
WindowStartupLocation="CenterOwner" ResizeMode="NoResize">
|
||||
|
||||
<!-- Enable WindowChrome Feature -->
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome UseAeroCaptionButtons="False" CornerRadius="0" CaptionHeight="32" GlassFrameThickness="0 0 0 1" ResizeBorderThickness="8" ResizeGripDirection="None" NonClientFrameEdges="None" />
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
<!-- Window Layout -->
|
||||
<Border Background="{StaticResource Brush.BG1}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Titlebar -->
|
||||
<Grid Grid.Row="0" Background="{StaticResource Brush.BG4}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Title -->
|
||||
<Label Content="ABOUT" FontWeight="Light"/>
|
||||
|
||||
<!-- Close Button -->
|
||||
<Button Click="Quit" Width="32" Grid.Column="2" WindowChrome.IsHitTestVisibleInChrome="True">
|
||||
<Button.Style>
|
||||
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource Style.Button.HighlightHover}">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="Red"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
|
||||
<Path Width="10" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Close}"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="90"/>
|
||||
<RowDefinition Height="40"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="24"/>
|
||||
<RowDefinition Height="24"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,6,0,0">
|
||||
<Path Width="64" Height="64" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Git}" Fill="#FFF05133"/>
|
||||
</StackPanel>
|
||||
|
||||
<Label Grid.Row="1" Content="SourceGit - OPEN SOURCE GIT CLIENT" HorizontalContentAlignment="Center" VerticalContentAlignment="Bottom" FontSize="18" FontWeight="Bold"/>
|
||||
<Label Grid.Row="2" Content="{Binding ElementName=me, Path=Version}" HorizontalContentAlignment="Center" FontSize="11"/>
|
||||
|
||||
<Label Grid.Row="3" HorizontalContentAlignment="Center" FontSize="11">
|
||||
<Hyperlink RequestNavigate="OpenSource" NavigateUri="https://gitee.com/sourcegit/SourceGit.git">
|
||||
<Run Text="https://gitee.com/sourcegit/SourceGit.git"/>
|
||||
</Hyperlink>
|
||||
</Label>
|
||||
|
||||
<Label Grid.Row="4" Content="Copyright © sourcegit 2020. All rights reserved." HorizontalContentAlignment="Center" FontSize="11"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Window>
|
46
SourceGit/UI/About.xaml.cs
Normal file
46
SourceGit/UI/About.xaml.cs
Normal file
|
@ -0,0 +1,46 @@
|
|||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using System.Windows.Navigation;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// About dialog
|
||||
/// </summary>
|
||||
public partial class About : Window {
|
||||
|
||||
/// <summary>
|
||||
/// Current app version
|
||||
/// </summary>
|
||||
public string Version {
|
||||
get {
|
||||
return "VERSION : " + FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public About() {
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open source code link
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void OpenSource(object sender, RequestNavigateEventArgs e) {
|
||||
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close this dialog
|
||||
/// </summary>
|
||||
private void Quit(object sender, RoutedEventArgs e) {
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
85
SourceGit/UI/Apply.xaml
Normal file
85
SourceGit/UI/Apply.xaml
Normal file
|
@ -0,0 +1,85 @@
|
|||
<UserControl x:Class="SourceGit.UI.Apply"
|
||||
x:Name="me"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:helpers="clr-namespace:SourceGit.Helpers"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="192" d:DesignWidth="500" Height="160" Width="500">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.ColumnSpan="2" FontWeight="DemiBold" FontSize="18" Content="Apply Patch"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right" Content="Patch File :"/>
|
||||
<Grid Grid.Row="2" Grid.Column="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="28"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox
|
||||
Grid.Column="0"
|
||||
x:Name="txtPatchFile"
|
||||
Height="24"
|
||||
helpers:TextBoxHelper.Placeholder="Select .patch file to apply">
|
||||
<TextBox.Text>
|
||||
<Binding Path="PatchFile" ElementName="me" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay">
|
||||
<Binding.ValidationRules>
|
||||
<helpers:PatchFileRequiredRule/>
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
|
||||
<Button Grid.Column="1" Width="24" Height="24" Click="FindPatchFile" Padding="0" BorderThickness="1" Style="{StaticResource Style.Button.Bordered}">
|
||||
<Path Width="14" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Folder}"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="0" HorizontalAlignment="Right" Content="Whitespace :"/>
|
||||
<ComboBox x:Name="combWhitespaceOptions" Grid.Row="3" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Height="20">
|
||||
<Label Content="{Binding Name}" Padding="4,0"/>
|
||||
<Label Content="{Binding Desc}" Foreground="{StaticResource Brush.FG2}" FontSize="11" Padding="4,0"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<Grid Grid.Row="5" Grid.ColumnSpan="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Column="1" Click="Start" Content="SURE" Style="{StaticResource Style.Button.AccentBordered}"/>
|
||||
<Button Grid.Column="3" Click="Cancel" Content="CANCEL" Style="{StaticResource Style.Button.Bordered}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="0" Grid.RowSpan="6" Grid.ColumnSpan="2" x:Name="status" Visibility="Collapsed" Background="{StaticResource Brush.BG1}" Opacity=".9">
|
||||
<Path x:Name="statusIcon" Width="48" Height="48" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Loading}" RenderTransformOrigin=".5,.5">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
112
SourceGit/UI/Apply.xaml.cs
Normal file
112
SourceGit/UI/Apply.xaml.cs
Normal file
|
@ -0,0 +1,112 @@
|
|||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Apply patch dialog
|
||||
/// </summary>
|
||||
public partial class Apply : UserControl {
|
||||
private Git.Repository repo = null;
|
||||
|
||||
/// <summary>
|
||||
/// Whitespace option.
|
||||
/// </summary>
|
||||
public class WhitespaceOption {
|
||||
public string Name { get; set; }
|
||||
public string Desc { get; set; }
|
||||
public string Arg { get; set; }
|
||||
|
||||
public WhitespaceOption(string n, string d, string a) {
|
||||
Name = n;
|
||||
Desc = d;
|
||||
Arg = a;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Path of file to be patched.
|
||||
/// </summary>
|
||||
public string PatchFile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public Apply(Git.Repository opened) {
|
||||
repo = opened;
|
||||
InitializeComponent();
|
||||
|
||||
combWhitespaceOptions.ItemsSource = new WhitespaceOption[] {
|
||||
new WhitespaceOption("No Warn", "Turns off the trailing whitespace warning", "nowarn"),
|
||||
new WhitespaceOption("Warn", "Outputs warnings for a few such errors, but applies", "warn"),
|
||||
new WhitespaceOption("Error", "Raise errors and refuses to apply the patch", "error"),
|
||||
new WhitespaceOption("Error All", "Similar to 'error', but shows more", "error-all"),
|
||||
};
|
||||
combWhitespaceOptions.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show this dialog.
|
||||
/// </summary>
|
||||
/// <param name="opened"></param>
|
||||
public static void Show(Git.Repository opened) {
|
||||
PopupManager.Show(new Apply(opened));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open file browser dialog for select a file to patch.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void FindPatchFile(object sender, RoutedEventArgs e) {
|
||||
var dialog = new OpenFileDialog();
|
||||
dialog.Filter = "Patch File|*.patch";
|
||||
dialog.Title = "Select Patch File";
|
||||
dialog.InitialDirectory = repo.Path;
|
||||
dialog.CheckFileExists = true;
|
||||
|
||||
if (dialog.ShowDialog() == true) {
|
||||
PatchFile = dialog.FileName;
|
||||
txtPatchFile.Text = dialog.FileName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start apply selected path.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void Start(object sender, RoutedEventArgs e) {
|
||||
txtPatchFile.GetBindingExpression(TextBox.TextProperty).UpdateSource();
|
||||
if (Validation.GetHasError(txtPatchFile)) return;
|
||||
|
||||
PopupManager.Lock();
|
||||
|
||||
status.Visibility = Visibility.Visible;
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
|
||||
var mode = combWhitespaceOptions.SelectedItem as WhitespaceOption;
|
||||
await Task.Run(() => repo.Apply(PatchFile, mode.Arg));
|
||||
|
||||
status.Visibility = Visibility.Collapsed;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
PopupManager.Close(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel options.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Cancel(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Close();
|
||||
}
|
||||
}
|
||||
}
|
201
SourceGit/UI/Blame.xaml
Normal file
201
SourceGit/UI/Blame.xaml
Normal file
|
@ -0,0 +1,201 @@
|
|||
<Window x:Class="SourceGit.UI.Blame"
|
||||
x:Name="me"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
Height="600" Width="800">
|
||||
|
||||
<!-- Enable WindowChrome -->
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome UseAeroCaptionButtons="False" CornerRadius="0" CaptionHeight="32"/>
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
<!-- Window Content -->
|
||||
<Border Background="{StaticResource Brush.BG1}">
|
||||
<!-- Fix Maximize BUG -->
|
||||
<Border.Style>
|
||||
<Style TargetType="{x:Type Border}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding WindowState, ElementName=me}" Value="Maximized">
|
||||
<Setter Property="Margin" Value="6"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding WindowState, ElementName=me}" Value="Normal">
|
||||
<Setter Property="Margin" Value="0"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Border.Style>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="24"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Title bar -->
|
||||
<Grid Grid.Row="0" Background="{StaticResource Brush.BG4}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Logo & TITLE -->
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal">
|
||||
<Path
|
||||
Width="20" Height="20" Margin="6,-1,2,0"
|
||||
Style="{StaticResource Style.Icon}"
|
||||
Data="{StaticResource Icon.Git}"
|
||||
Fill="#FFF05133"
|
||||
WindowChrome.IsHitTestVisibleInChrome="True"
|
||||
MouseLeftButtonDown="LogoMouseButtonDown"/>
|
||||
<Label Content="SOURCE GIT - BLAME" FontWeight="Light"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Options -->
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" WindowChrome.IsHitTestVisibleInChrome="True">
|
||||
<Button Click="Minimize" Width="32" Style="{StaticResource Style.Button.HighlightHover}">
|
||||
<Path Style="{StaticResource Style.WindowControlIcon}" Data="{StaticResource Icon.Minimize}"/>
|
||||
</Button>
|
||||
<Button Click="MaximizeOrRestore" Width="32" Style="{StaticResource Style.Button.HighlightHover}">
|
||||
<Path>
|
||||
<Path.Style>
|
||||
<Style TargetType="{x:Type Path}" BasedOn="{StaticResource Style.WindowControlIcon}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding WindowState, ElementName=me}" Value="Maximized">
|
||||
<Setter Property="Data" Value="{StaticResource Icon.Restore}"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding WindowState, ElementName=me}" Value="Normal">
|
||||
<Setter Property="Data" Value="{StaticResource Icon.Maximize}"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Path.Style>
|
||||
</Path>
|
||||
</Button>
|
||||
<Button Click="Quit" Width="32">
|
||||
<Button.Style>
|
||||
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource Style.Button.HighlightHover}">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="Red"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
|
||||
<Path Width="10" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Close}"/>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Blame file -->
|
||||
<Border Grid.Row="1" Padding="2,0">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Column="0" x:Name="blameFile" HorizontalAlignment="Left" FontSize="11" Foreground="{StaticResource Brush.FG2}" FontFamily="Consolas"/>
|
||||
<Label Grid.Column="1" HorizontalAlignment="Right" Foreground="{StaticResource Brush.FG2}" FontSize="11" Content="Use right mouse button to view commit information."/>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- Content -->
|
||||
<Border Grid.Row="2" BorderThickness="1" BorderBrush="{StaticResource Brush.Border2}" ClipToBounds="True">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="1"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox
|
||||
x:Name="lineNumber"
|
||||
Grid.Column="0"
|
||||
AcceptsReturn="True"
|
||||
AcceptsTab="True"
|
||||
BorderThickness="0"
|
||||
Background="Transparent"
|
||||
IsReadOnly="True"
|
||||
Margin="4,0,4,0"
|
||||
FontSize="13"
|
||||
HorizontalContentAlignment="Right"
|
||||
VerticalAlignment="Stretch"
|
||||
FontFamily="Consolas"/>
|
||||
|
||||
<Rectangle Grid.Column="1" Width="1" Fill="{StaticResource Brush.Border2}"/>
|
||||
|
||||
<RichTextBox
|
||||
x:Name="content"
|
||||
Grid.Column="2"
|
||||
AcceptsReturn="True"
|
||||
AcceptsTab="True"
|
||||
IsReadOnly="True"
|
||||
BorderThickness="0"
|
||||
Background="Transparent"
|
||||
Foreground="{StaticResource Brush.FG}"
|
||||
Height="Auto"
|
||||
FontSize="13"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
RenderOptions.ClearTypeHint="Enabled"
|
||||
ScrollViewer.ScrollChanged="SyncScrollChanged"
|
||||
PreviewMouseWheel="MouseWheelOnContent"
|
||||
SizeChanged="ContentSizeChanged"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
FontFamily="Consolas">
|
||||
<FlowDocument PageWidth="0"/>
|
||||
</RichTextBox>
|
||||
|
||||
<!-- Loading tip -->
|
||||
<Path x:Name="loading" Grid.ColumnSpan="5" Data="{StaticResource Icon.Loading}" RenderTransformOrigin=".5,.5">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
</Path.RenderTransform>
|
||||
|
||||
<Path.Style>
|
||||
<Style BasedOn="{StaticResource Style.Icon}" TargetType="{x:Type Path}">
|
||||
<Setter Property="Width" Value="48"/>
|
||||
<Setter Property="Height" Value="48"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Center"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="Fill" Value="{StaticResource Brush.FG2}"/>
|
||||
</Style>
|
||||
</Path.Style>
|
||||
</Path>
|
||||
|
||||
<!-- Popup to show commit info -->
|
||||
<Popup x:Name="popup" Grid.ColumnSpan="5" Placement="MousePoint" IsOpen="False" StaysOpen="False" Focusable="True">
|
||||
<Border BorderBrush="{StaticResource Brush.Accent1}" BorderThickness="1" Background="{StaticResource Brush.BG1}">
|
||||
<Grid Margin="4">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="24"/>
|
||||
<RowDefinition Height="24"/>
|
||||
<RowDefinition Height="24"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.Column="0" Content="COMMIT SHA" Foreground="{StaticResource Brush.FG2}"/>
|
||||
<Label Grid.Row="0" Grid.Column="1" x:Name="commitID"/>
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="AUTHOR" Foreground="{StaticResource Brush.FG2}"/>
|
||||
<Label Grid.Row="1" Grid.Column="1" x:Name="authorName"/>
|
||||
<Label Grid.Row="2" Grid.Column="0" Content="MODIFY TIME" Foreground="{StaticResource Brush.FG2}"/>
|
||||
<Label Grid.Row="2" Grid.Column="1" x:Name="authorTime"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Popup>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Window>
|
211
SourceGit/UI/Blame.xaml.cs
Normal file
211
SourceGit/UI/Blame.xaml.cs
Normal file
|
@ -0,0 +1,211 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Viewer to show git-blame
|
||||
/// </summary>
|
||||
public partial class Blame : Window {
|
||||
|
||||
/// <summary>
|
||||
/// Background color for blocks.
|
||||
/// </summary>
|
||||
public static Brush[] BG = new Brush[] {
|
||||
Brushes.Transparent,
|
||||
new SolidColorBrush(Color.FromArgb(128, 0, 0, 0))
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="repo"></param>
|
||||
/// <param name="file"></param>
|
||||
/// <param name="revision"></param>
|
||||
public Blame(Git.Repository repo, string file, string revision) {
|
||||
InitializeComponent();
|
||||
|
||||
double minWidth = content.ActualWidth;
|
||||
|
||||
// Move to center.
|
||||
var parent = App.Current.MainWindow;
|
||||
Left = parent.Left + (parent.Width - Width) * 0.5;
|
||||
Top = parent.Top + (parent.Height - Height) * 0.5;
|
||||
|
||||
// Show loading.
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
loading.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
loading.Visibility = Visibility.Visible;
|
||||
|
||||
// Layout content
|
||||
blameFile.Content = $"{file}@{revision.Substring(0, 8)}";
|
||||
Task.Run(() => {
|
||||
var blame = repo.BlameFile(file, revision);
|
||||
|
||||
Dispatcher.Invoke(() => {
|
||||
content.Document.Blocks.Clear();
|
||||
|
||||
if (blame.IsBinary) {
|
||||
lineNumber.Text = "0";
|
||||
|
||||
Paragraph p = new Paragraph(new Run("BINARY FILE BLAME NOT SUPPORTED!!!"));
|
||||
p.Margin = new Thickness(0);
|
||||
p.Padding = new Thickness(0);
|
||||
p.LineHeight = 1;
|
||||
p.Background = Brushes.Transparent;
|
||||
p.Foreground = FindResource("Brush.FG") as SolidColorBrush;
|
||||
p.FontStyle = FontStyles.Normal;
|
||||
|
||||
content.Document.Blocks.Add(p);
|
||||
} else {
|
||||
List<string> numbers = new List<string>();
|
||||
for (int i = 0; i < blame.LineCount; i++) numbers.Add(i.ToString());
|
||||
lineNumber.Text = string.Join("\n", numbers);
|
||||
numbers.Clear();
|
||||
|
||||
for (int i = 0; i < blame.Blocks.Count; i++) {
|
||||
var frag = blame.Blocks[i];
|
||||
var idx = i;
|
||||
|
||||
Paragraph p = new Paragraph(new Run(frag.Content));
|
||||
p.DataContext = frag;
|
||||
p.Margin = new Thickness(0);
|
||||
p.Padding = new Thickness(0);
|
||||
p.LineHeight = 1;
|
||||
p.Background = BG[i % 2];
|
||||
p.Foreground = FindResource("Brush.FG") as SolidColorBrush;
|
||||
p.FontStyle = FontStyles.Normal;
|
||||
p.MouseRightButtonDown += (sender, ev) => {
|
||||
Hyperlink link = new Hyperlink(new Run(frag.CommitSHA));
|
||||
link.ToolTip = "CLICK TO GO";
|
||||
link.Click += (o, e) => {
|
||||
repo.OnNavigateCommit?.Invoke(frag.CommitSHA);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
foreach (var block in content.Document.Blocks) {
|
||||
var paragraph = block as Paragraph;
|
||||
if ((paragraph.DataContext as Git.Blame.Block).CommitSHA == frag.CommitSHA) {
|
||||
paragraph.Background = Brushes.Green;
|
||||
} else {
|
||||
paragraph.Background = BG[i % 2];
|
||||
}
|
||||
}
|
||||
|
||||
commitID.Content = link;
|
||||
authorName.Content = frag.Author;
|
||||
authorTime.Content = frag.Time;
|
||||
popup.IsOpen = true;
|
||||
};
|
||||
|
||||
var formatter = new FormattedText(
|
||||
frag.Content,
|
||||
CultureInfo.CurrentUICulture,
|
||||
FlowDirection.LeftToRight,
|
||||
new Typeface(content.FontFamily, p.FontStyle, p.FontWeight, p.FontStretch),
|
||||
content.FontSize,
|
||||
Brushes.Black,
|
||||
new NumberSubstitution(),
|
||||
TextFormattingMode.Ideal);
|
||||
if (minWidth < formatter.Width) {
|
||||
content.Document.PageWidth = formatter.Width + 16;
|
||||
minWidth = formatter.Width;
|
||||
}
|
||||
|
||||
content.Document.Blocks.Add(p);
|
||||
}
|
||||
}
|
||||
|
||||
// Hide loading.
|
||||
loading.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
loading.Visibility = Visibility.Collapsed;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Click logo
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void LogoMouseButtonDown(object sender, MouseButtonEventArgs e) {
|
||||
var element = e.OriginalSource as FrameworkElement;
|
||||
if (element == null) return;
|
||||
|
||||
var pos = PointToScreen(new Point(0, 33));
|
||||
SystemCommands.ShowSystemMenu(this, pos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Minimize
|
||||
/// </summary>
|
||||
private void Minimize(object sender, RoutedEventArgs e) {
|
||||
SystemCommands.MinimizeWindow(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximize/Restore
|
||||
/// </summary>
|
||||
private void MaximizeOrRestore(object sender, RoutedEventArgs e) {
|
||||
if (WindowState == WindowState.Normal) {
|
||||
SystemCommands.MaximizeWindow(this);
|
||||
} else {
|
||||
SystemCommands.RestoreWindow(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quit
|
||||
/// </summary>
|
||||
private void Quit(object sender, RoutedEventArgs e) {
|
||||
Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sync scroll
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void SyncScrollChanged(object sender, ScrollChangedEventArgs e) {
|
||||
if (e.VerticalChange != 0) {
|
||||
var margin = new Thickness(4, -e.VerticalOffset, 4, 0);
|
||||
lineNumber.Margin = margin;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mouse wheel
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void MouseWheelOnContent(object sender, MouseWheelEventArgs e) {
|
||||
if (e.Delta > 0) {
|
||||
content.LineUp();
|
||||
} else {
|
||||
content.LineDown();
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Content size changed.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void ContentSizeChanged(object sender, SizeChangedEventArgs e) {
|
||||
if (content.Document.PageWidth < content.ActualWidth) {
|
||||
content.Document.PageWidth = content.ActualWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
45
SourceGit/UI/CherryPick.xaml
Normal file
45
SourceGit/UI/CherryPick.xaml
Normal file
|
@ -0,0 +1,45 @@
|
|||
<UserControl x:Class="SourceGit.UI.CherryPick"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="160" d:DesignWidth="500" Height="160" Width="500">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.ColumnSpan="2" FontWeight="DemiBold" FontSize="18" Content="Cherry Pick"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right" Content="Commit :"/>
|
||||
<StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal">
|
||||
<Path Width="12" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Commit}" Margin="4,0"/>
|
||||
<Label x:Name="desc"/>
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox Grid.Row="3" Grid.Column="1" x:Name="chkCommitChanges" IsChecked="True" Content="Commit the changes"/>
|
||||
|
||||
<Grid Grid.Row="5" Grid.ColumnSpan="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Column="1" Click="Start" Content="SURE" Style="{StaticResource Style.Button.AccentBordered}"/>
|
||||
<Button Grid.Column="3" Click="Cancel" Content="CANCEL" Style="{StaticResource Style.Button.Bordered}"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
54
SourceGit/UI/CherryPick.xaml.cs
Normal file
54
SourceGit/UI/CherryPick.xaml.cs
Normal file
|
@ -0,0 +1,54 @@
|
|||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Cherry pick commit dialog.
|
||||
/// </summary>
|
||||
public partial class CherryPick : UserControl {
|
||||
private Git.Repository repo = null;
|
||||
private string commitSHA = null;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="opened"></param>
|
||||
/// <param name="commit"></param>
|
||||
public CherryPick(Git.Repository opened, Git.Commit commit) {
|
||||
InitializeComponent();
|
||||
|
||||
repo = opened;
|
||||
commitSHA = commit.SHA;
|
||||
desc.Content = $"{commit.ShortSHA} {commit.Subject}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display this dialog.
|
||||
/// </summary>
|
||||
/// <param name="repo"></param>
|
||||
/// <param name="commit"></param>
|
||||
public static void Show(Git.Repository repo, Git.Commit commit) {
|
||||
PopupManager.Show(new CherryPick(repo, commit));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start pick.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Start(object sender, RoutedEventArgs e) {
|
||||
repo.CherryPick(commitSHA, chkCommitChanges.IsChecked != true);
|
||||
PopupManager.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Cancel(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Close();
|
||||
}
|
||||
}
|
||||
}
|
98
SourceGit/UI/Clone.xaml
Normal file
98
SourceGit/UI/Clone.xaml
Normal file
|
@ -0,0 +1,98 @@
|
|||
<UserControl x:Class="SourceGit.UI.Clone"
|
||||
x:Name="me"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:helpers="clr-namespace:SourceGit.Helpers"
|
||||
mc:Ignorable="d"
|
||||
Width="500" Height="192">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.ColumnSpan="2" FontWeight="DemiBold" FontSize="18" Content="Clone Remote Repository"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right" Content="Repository URL :"/>
|
||||
<TextBox x:Name="txtUrl" Grid.Row="2" Grid.Column="1"
|
||||
Height="24"
|
||||
helpers:TextBoxHelper.Placeholder="Git Repository URL">
|
||||
<TextBox.Text>
|
||||
<Binding Path="RemoteUri" ElementName="me" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay">
|
||||
<Binding.ValidationRules>
|
||||
<helpers:RemoteUriRule/>
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="0" HorizontalAlignment="Right" Content="Parent Folder :"/>
|
||||
<Grid Grid.Row="3" Grid.Column="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="28"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox Grid.Column="0"
|
||||
x:Name="txtParentFolder"
|
||||
Height="24"
|
||||
helpers:TextBoxHelper.Placeholder="Folder to contain this repository">
|
||||
<TextBox.Text>
|
||||
<Binding Path="ParentFolder" ElementName="me" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay">
|
||||
<Binding.ValidationRules>
|
||||
<helpers:CloneFolderRule/>
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
<Button Grid.Column="1" Width="24" Height="24" Padding="0" BorderThickness="1" Click="SelectParentFolder" Style="{StaticResource Style.Button.Bordered}">
|
||||
<Path Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Folder}"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<Label Grid.Row="4" Grid.Column="0" HorizontalAlignment="Right" Content="Local Name :"/>
|
||||
<TextBox Grid.Row="4" Grid.Column="1"
|
||||
VerticalContentAlignment="Center"
|
||||
Height="24"
|
||||
helpers:TextBoxHelper.Placeholder="Repository name. Optional."
|
||||
Text="{Binding LocalName, ElementName=me, Mode=TwoWay}">
|
||||
</TextBox>
|
||||
|
||||
<Grid Grid.Row="6" Grid.ColumnSpan="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Column="1" Click="Start" Content="SURE" Style="{StaticResource Style.Button.AccentBordered}"/>
|
||||
<Button Grid.Column="3" Click="Cancel" Content="CANCEL" Style="{StaticResource Style.Button.Bordered}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="0" Grid.RowSpan="7" Grid.ColumnSpan="2" x:Name="status" Visibility="Collapsed" Background="{StaticResource Brush.BG1}" Opacity=".9">
|
||||
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Path x:Name="statusIcon" Width="48" Height="48" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Loading}" RenderTransformOrigin=".5,.5">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
|
||||
<Label x:Name="statusMsg" Margin="0,8,0,0"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
115
SourceGit/UI/Clone.xaml.cs
Normal file
115
SourceGit/UI/Clone.xaml.cs
Normal file
|
@ -0,0 +1,115 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Clone dialog.
|
||||
/// </summary>
|
||||
public partial class Clone : UserControl {
|
||||
|
||||
/// <summary>
|
||||
/// Remote repository
|
||||
/// </summary>
|
||||
public string RemoteUri { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Parent folder.
|
||||
/// </summary>
|
||||
public string ParentFolder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Local name.
|
||||
/// </summary>
|
||||
public string LocalName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public Clone() {
|
||||
ParentFolder = App.Preference.GitDefaultCloneDir;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show clone dialog.
|
||||
/// </summary>
|
||||
public static void Show() {
|
||||
PopupManager.Show(new Clone());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Select parent folder.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void SelectParentFolder(object sender, RoutedEventArgs e) {
|
||||
var dialog = new System.Windows.Forms.FolderBrowserDialog();
|
||||
dialog.Description = "Git Repository URL";
|
||||
dialog.RootFolder = Environment.SpecialFolder.MyComputer;
|
||||
dialog.ShowNewFolderButton = true;
|
||||
|
||||
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
|
||||
txtParentFolder.Text = dialog.SelectedPath;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start clone
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Start(object sender, RoutedEventArgs e) {
|
||||
txtUrl.GetBindingExpression(TextBox.TextProperty).UpdateSource();
|
||||
if (Validation.GetHasError(txtUrl)) return;
|
||||
|
||||
txtParentFolder.GetBindingExpression(TextBox.TextProperty).UpdateSource();
|
||||
if (Validation.GetHasError(txtParentFolder)) return;
|
||||
|
||||
string repoName;
|
||||
if (string.IsNullOrWhiteSpace(LocalName)) {
|
||||
var from = RemoteUri.LastIndexOfAny(new char[] { '\\', '/' });
|
||||
if (from <= 0) return;
|
||||
|
||||
var name = RemoteUri.Substring(from + 1);
|
||||
repoName = name.Replace(".git", "");
|
||||
} else {
|
||||
repoName = LocalName;
|
||||
}
|
||||
|
||||
PopupManager.Lock();
|
||||
|
||||
status.Visibility = Visibility.Visible;
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
|
||||
Task.Run(() => {
|
||||
var repo = Git.Repository.Clone(RemoteUri, ParentFolder, repoName, msg => Dispatcher.Invoke(() => statusMsg.Content = msg));
|
||||
if (repo == null) {
|
||||
PopupManager.Unlock();
|
||||
Dispatcher.Invoke(() => {
|
||||
status.Visibility = Visibility.Collapsed;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
});
|
||||
} else {
|
||||
Dispatcher.Invoke(() => PopupManager.Close(true));
|
||||
repo.Open();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Cancel(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Close();
|
||||
}
|
||||
}
|
||||
}
|
408
SourceGit/UI/CommitViewer.xaml
Normal file
408
SourceGit/UI/CommitViewer.xaml
Normal file
|
@ -0,0 +1,408 @@
|
|||
<UserControl x:Class="SourceGit.UI.CommitViewer"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:source="clr-namespace:SourceGit"
|
||||
xmlns:local="clr-namespace:SourceGit.UI"
|
||||
xmlns:git="clr-namespace:SourceGit.Git"
|
||||
xmlns:converters="clr-namespace:SourceGit.Converters"
|
||||
xmlns:helpers="clr-namespace:SourceGit.Helpers"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
Unloaded="Cleanup">
|
||||
<TabControl>
|
||||
<TabItem Header="INFORMATION">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition x:Name="committerRow" Height="Auto"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="96"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="96"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- SHA -->
|
||||
<Label Grid.Row="0" Grid.Column="0" Content="SHA" HorizontalAlignment="Right" Opacity=".5"/>
|
||||
<TextBox Grid.Row="0" Grid.Column="1"
|
||||
x:Name="SHA"
|
||||
IsReadOnly="True"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
Margin="11,0,0,0"/>
|
||||
|
||||
<!-- Refs -->
|
||||
<Label x:Name="lblRefs" Grid.Row="0" Grid.Column="2" Content="REFS" HorizontalAlignment="Right" Opacity=".5"/>
|
||||
<ItemsControl Grid.Row="0" Grid.Column="3" x:Name="refs" Margin="8,0,0,0">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<VirtualizingStackPanel Orientation="Horizontal" VerticalAlignment="Center"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border x:Name="BG" Height="16" Margin="2">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="18"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Border Grid.Column="0" Background="{StaticResource Brush.BG5}">
|
||||
<Path x:Name="Icon" Width="8" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Branch}"/>
|
||||
</Border>
|
||||
|
||||
<Label x:Name="Name" Grid.Column="1" Content="{Binding Name}" FontSize="11" Padding="4,0" Foreground="Black"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<DataTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding Type}" Value="{x:Static git:DecoratorType.Tag}">
|
||||
<Setter TargetName="BG" Property="Background" Value="#FF02C302"/>
|
||||
<Setter TargetName="Icon" Property="Data" Value="{StaticResource Icon.Tag}"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding Type}" Value="{x:Static git:DecoratorType.LocalBranchHead}">
|
||||
<Setter TargetName="BG" Property="Background" Value="#FFFFB835"/>
|
||||
<Setter TargetName="Icon" Property="Data" Value="{StaticResource Icon.Branch}"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding Type}" Value="{x:Static git:DecoratorType.RemoteBranchHead}">
|
||||
<Setter TargetName="BG" Property="Background" Value="#FFFFB835"/>
|
||||
<Setter TargetName="Icon" Property="Data" Value="{StaticResource Icon.Remote}"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding Type}" Value="{x:Static git:DecoratorType.CurrentBranchHead}">
|
||||
<Setter TargetName="BG" Property="Background" Value="#FFFFB835"/>
|
||||
<Setter TargetName="Icon" Property="Data" Value="{StaticResource Icon.Check}"/>
|
||||
<Setter TargetName="Icon" Property="Fill" Value="Orange"/>
|
||||
<Setter TargetName="Name" Property="FontWeight" Value="Bold"/>
|
||||
<Setter TargetName="Name" Property="Foreground" Value="{StaticResource Brush.FG}"/>
|
||||
</DataTrigger>
|
||||
</DataTemplate.Triggers>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<!-- PARENTS -->
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="PARENTS" HorizontalAlignment="Right" Opacity=".5"/>
|
||||
<ItemsControl Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3" x:Name="parents" Margin="8,0,0,0">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<VirtualizingStackPanel Orientation="Horizontal" VerticalAlignment="Center"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Label Margin="0,0,8,0">
|
||||
<Hyperlink
|
||||
RequestNavigate="NavigateParent"
|
||||
NavigateUri="{Binding .}"
|
||||
ToolTip="NAVIGATE TO COMMIT">
|
||||
<Run Text="{Binding .}"/>
|
||||
</Hyperlink>
|
||||
</Label>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<!-- AUTHOR -->
|
||||
<Label Grid.Row="2" Grid.Column="0" Content="AUTHOR" HorizontalAlignment="Right" Opacity=".5"/>
|
||||
<TextBox Grid.Row="2" Grid.Column="1" x:Name="author"
|
||||
IsReadOnly="True"
|
||||
Background="Transparent"
|
||||
AcceptsReturn="True"
|
||||
BorderThickness="0"
|
||||
Margin="11,0,0,0"/>
|
||||
|
||||
<!-- AUTHOR TIME -->
|
||||
<Label Grid.Row="2" Grid.Column="2" Content="AUTHOR TIME" HorizontalAlignment="Right" Opacity=".5"/>
|
||||
<TextBox Grid.Row="2" Grid.Column="3" Grid.ColumnSpan="3" x:Name="authorTime"
|
||||
IsReadOnly="True"
|
||||
Background="Transparent"
|
||||
AcceptsReturn="True"
|
||||
BorderThickness="0"
|
||||
Margin="8,0,0,0"/>
|
||||
|
||||
<!-- COMMITTER -->
|
||||
<Label Grid.Row="3" Grid.Column="0" Content="COMMITTER" HorizontalAlignment="Right" Opacity=".5"/>
|
||||
<TextBox Grid.Row="3" Grid.Column="1" x:Name="committer"
|
||||
IsReadOnly="True"
|
||||
Background="Transparent"
|
||||
AcceptsReturn="True"
|
||||
BorderThickness="0"
|
||||
Margin="11,0,0,0"/>
|
||||
|
||||
<!-- COMMIT TIME -->
|
||||
<Label Grid.Row="3" Grid.Column="2" Content="COMMIT TIME" HorizontalAlignment="Right" Opacity=".5"/>
|
||||
<TextBox Grid.Row="3" Grid.Column="3" Grid.ColumnSpan="3" x:Name="committerTime"
|
||||
IsReadOnly="True"
|
||||
Background="Transparent"
|
||||
AcceptsReturn="True"
|
||||
BorderThickness="0"
|
||||
Margin="8,0,0,0"/>
|
||||
|
||||
<Rectangle Grid.Row="4" Grid.ColumnSpan="4" Height="1" Margin="8,0" Fill="{StaticResource Brush.Border2}"/>
|
||||
|
||||
<!-- SUBJECT -->
|
||||
<Label Grid.Row="5" Grid.Column="0" Content="SUBJECT" HorizontalAlignment="Right" Opacity=".5"/>
|
||||
<TextBox Grid.Row="5" Grid.Column="1" Grid.ColumnSpan="3"
|
||||
x:Name="subject"
|
||||
IsReadOnly="True"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
Margin="8,0,16,0"/>
|
||||
|
||||
<!-- MESSAGE -->
|
||||
<Label Grid.Row="6" Grid.Column="0" Content="DESCRIPTION" HorizontalAlignment="Right" VerticalAlignment="Top" Opacity=".5"/>
|
||||
<TextBox Grid.Row="6" Grid.Column="1" Grid.ColumnSpan="3"
|
||||
x:Name="message"
|
||||
IsReadOnly="True"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="11"
|
||||
Margin="11,8,0,0"/>
|
||||
|
||||
<Rectangle Grid.Row="7" Grid.ColumnSpan="4" Height="1" Margin="8,0" Fill="{StaticResource Brush.Border2}"/>
|
||||
|
||||
<!-- CHANGELIST -->
|
||||
<Label Grid.Row="8" Grid.Column="0" Content="CHANGED" HorizontalAlignment="Right" VerticalAlignment="Top" Opacity=".5"/>
|
||||
<DataGrid
|
||||
Grid.Row="8"
|
||||
Grid.Column="1"
|
||||
Grid.ColumnSpan="3"
|
||||
x:Name="changeList1"
|
||||
RowHeight="20"
|
||||
Margin="11,2,0,2">
|
||||
<DataGrid.Resources>
|
||||
<converters:FileStatusToColor x:Key="StatusColorConverter"/>
|
||||
<converters:FileStatusToIcon x:Key="StatusIconConverter"/>
|
||||
|
||||
<Style x:Key="Style.DataGridText.VerticalCenter" TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
</DataGrid.Resources>
|
||||
|
||||
<DataGrid.Columns>
|
||||
<DataGridTemplateColumn Width="22">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<Border Width="14" Height="14" x:Name="status" Background="{Binding ., Converter={StaticResource StatusColorConverter}}" CornerRadius="2" Margin="2,0,4,0">
|
||||
<TextBlock Text="{Binding ., Converter={StaticResource StatusIconConverter}}" Foreground="{StaticResource Brush.FG}" TextAlignment="Center" VerticalAlignment="Center" FontSize="8"/>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTextColumn Width="*" Binding="{Binding Path}" Foreground="{StaticResource Brush.FG}" FontFamily="Consolas" ElementStyle="{StaticResource Style.DataGridText.VerticalCenter}"/>
|
||||
</DataGrid.Columns>
|
||||
|
||||
<DataGrid.RowStyle>
|
||||
<Style TargetType="{x:Type DataGridRow}" BasedOn="{StaticResource Style.DataGridRow}">
|
||||
<EventSetter Event="ContextMenuOpening" Handler="ChangeListContextMenuOpening"/>
|
||||
</Style>
|
||||
</DataGrid.RowStyle>
|
||||
</DataGrid>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
|
||||
<!-- CHANGES -->
|
||||
<TabItem Header="CHANGES">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="200" MinWidth="200"/>
|
||||
<ColumnDefinition Width="1"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid Grid.Column="0" Margin="2,0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="24"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.Resources>
|
||||
<converters:BoolToCollapsed x:Key="BoolToCollapsed"/>
|
||||
<converters:InverseBoolToCollapsed x:Key="InverseBoolToCollapsed"/>
|
||||
</Grid.Resources>
|
||||
|
||||
<Grid Grid.Row="0" Margin="0,0,0,4">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="24"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="24"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Border Grid.Column="0" Grid.ColumnSpan="2" BorderThickness="1" BorderBrush="{StaticResource Brush.Border2}" Background="{StaticResource Brush.BG3}"/>
|
||||
<Path Grid.Column="0" Width="14" Height="14" Fill="{StaticResource Brush.FG2}" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Search}"/>
|
||||
<TextBox Grid.Column="1" x:Name="txtChangeFilter" BorderThickness="0" helpers:TextBoxHelper.Placeholder="Search File ..." TextChanged="SearchChangeFileTextChanged"/>
|
||||
<ToggleButton
|
||||
Grid.Column="2"
|
||||
x:Name="toggleSwitchMode"
|
||||
Margin="4,0,0,0"
|
||||
ToolTip="SWITCH TO LIST/TREE VIEW"
|
||||
Style="{StaticResource Style.ToggleButton.ListOrTree}"
|
||||
IsChecked="{Binding Source={x:Static source:App.Preference}, Path=UIUseListInChanges, Mode=TwoWay}"/>
|
||||
</Grid>
|
||||
|
||||
<TreeView
|
||||
Grid.Row="1"
|
||||
x:Name="changeTree"
|
||||
FontFamily="Consolas"
|
||||
Visibility="{Binding ElementName=toggleSwitchMode, Path=IsChecked, Converter={StaticResource InverseBoolToCollapsed}}"
|
||||
Background="{StaticResource Brush.BG2}"
|
||||
SelectedItemChanged="ChangeTreeItemSelected"
|
||||
PreviewMouseWheel="TreeMouseWheel">
|
||||
<TreeView.Resources>
|
||||
<converters:FileStatusToColor x:Key="StatusColorConverter"/>
|
||||
<converters:FileStatusToIcon x:Key="StatusIconConverter"/>
|
||||
</TreeView.Resources>
|
||||
<TreeView.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource Style.TreeView.ItemContainerStyle}">
|
||||
<Setter Property="IsExpanded" Value="{Binding IsNodeExpanded, Mode=TwoWay}"/>
|
||||
<EventSetter Event="ContextMenuOpening" Handler="TreeContextMenuOpening"/>
|
||||
</Style>
|
||||
</TreeView.ItemContainerStyle>
|
||||
<TreeView.ItemTemplate>
|
||||
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
|
||||
<StackPanel Orientation="Horizontal" Height="24">
|
||||
<Border x:Name="status" Width="14" Height="14" Visibility="Collapsed" Background="{Binding Change, Converter={StaticResource StatusColorConverter}}" CornerRadius="2" Margin="0,0,4,0">
|
||||
<TextBlock Text="{Binding Change, Converter={StaticResource StatusIconConverter}}" Foreground="{StaticResource Brush.FG}" TextAlignment="Center" VerticalAlignment="Center" FontSize="10" RenderOptions.BitmapScalingMode="HighQuality"/>
|
||||
</Border>
|
||||
<Path x:Name="icon" Width="14" Style="{StaticResource Style.Icon}" Fill="Goldenrod" Data="{StaticResource Icon.Folder.Fill}"/>
|
||||
<TextBlock Text="{Binding Name}" Foreground="{StaticResource Brush.FG}" TextAlignment="Center" VerticalAlignment="Center" Margin="4,0,0,0" FontSize="11"/>
|
||||
</StackPanel>
|
||||
|
||||
<HierarchicalDataTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding IsFile}" Value="True">
|
||||
<Setter TargetName="status" Property="Visibility" Value="Visible"/>
|
||||
<Setter TargetName="icon" Property="Visibility" Value="Collapsed"/>
|
||||
</DataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsFile}" Value="False"/>
|
||||
<Condition Binding="{Binding IsNodeExpanded}" Value="True"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter TargetName="icon" Property="Data" Value="{StaticResource Icon.Folder.Open}"/>
|
||||
</MultiDataTrigger>
|
||||
</HierarchicalDataTemplate.Triggers>
|
||||
</HierarchicalDataTemplate>
|
||||
</TreeView.ItemTemplate>
|
||||
</TreeView>
|
||||
|
||||
<DataGrid
|
||||
Grid.Row="1"
|
||||
x:Name="changeList2"
|
||||
Visibility="{Binding ElementName=toggleSwitchMode, Path=IsChecked, Converter={StaticResource BoolToCollapsed}}"
|
||||
RowHeight="24"
|
||||
SelectionChanged="ChangeListSelectionChanged"
|
||||
SelectionMode="Single"
|
||||
SelectionUnit="FullRow"
|
||||
Background="{StaticResource Brush.BG2}">
|
||||
<DataGrid.Resources>
|
||||
<converters:FileStatusToColor x:Key="StatusColorConverter"/>
|
||||
<converters:FileStatusToIcon x:Key="StatusIconConverter"/>
|
||||
|
||||
<Style x:Key="Style.DataGridText.VerticalCenter" TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
</DataGrid.Resources>
|
||||
|
||||
<DataGrid.Columns>
|
||||
<DataGridTemplateColumn Width="22">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<Border Width="14" Height="14" x:Name="status" Background="{Binding ., Converter={StaticResource StatusColorConverter}}" CornerRadius="2" Margin="2,0,4,0">
|
||||
<TextBlock Text="{Binding ., Converter={StaticResource StatusIconConverter}}" Foreground="{StaticResource Brush.FG}" TextAlignment="Center" VerticalAlignment="Center" FontSize="8"/>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTextColumn Width="*" Binding="{Binding Path}" Foreground="{StaticResource Brush.FG}" FontFamily="Consolas" ElementStyle="{StaticResource Style.DataGridText.VerticalCenter}"/>
|
||||
</DataGrid.Columns>
|
||||
|
||||
<DataGrid.RowStyle>
|
||||
<Style TargetType="{x:Type DataGridRow}" BasedOn="{StaticResource Style.DataGridRow}">
|
||||
<EventSetter Event="ContextMenuOpening" Handler="ChangeListContextMenuOpening"/>
|
||||
</Style>
|
||||
</DataGrid.RowStyle>
|
||||
</DataGrid>
|
||||
</Grid>
|
||||
|
||||
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Transparent"/>
|
||||
|
||||
<local:DiffViewer Grid.Column="2" x:Name="diffViewer" Background="{StaticResource Brush.BG3}"/>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
|
||||
<!-- FILE TREE -->
|
||||
<TabItem Header="FILES">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="200" MinWidth="200" MaxWidth="400"/>
|
||||
<ColumnDefinition Width="1"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Border Grid.Column="0" Margin="2" Background="{StaticResource Brush.BG2}">
|
||||
<TreeView x:Name="fileTree" SelectedItemChanged="FileTreeItemSelected" FontFamily="Consolas" PreviewMouseWheel="TreeMouseWheel">
|
||||
<TreeView.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource Style.TreeView.ItemContainerStyle}">
|
||||
<Setter Property="IsExpanded" Value="{Binding IsNodeExpanded, Mode=TwoWay}"/>
|
||||
<EventSetter Event="ContextMenuOpening" Handler="TreeContextMenuOpening"/>
|
||||
</Style>
|
||||
</TreeView.ItemContainerStyle>
|
||||
<TreeView.ItemTemplate>
|
||||
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
|
||||
<StackPanel Orientation="Horizontal" Height="24">
|
||||
<Path x:Name="icon" Width="14" Style="{StaticResource Style.Icon}" Fill="Goldenrod" Data="{StaticResource Icon.Folder.Fill}"/>
|
||||
<TextBlock Text="{Binding Name}" Foreground="{StaticResource Brush.FG}" TextAlignment="Center" VerticalAlignment="Center" Margin="6,0,0,0" FontSize="11"/>
|
||||
</StackPanel>
|
||||
|
||||
<HierarchicalDataTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding IsFile}" Value="True">
|
||||
<Setter TargetName="icon" Property="Data" Value="{StaticResource Icon.File}"/>
|
||||
<Setter TargetName="icon" Property="Fill" Value="{StaticResource Brush.FG}"/>
|
||||
<Setter TargetName="icon" Property="Opacity" Value=".75"/>
|
||||
</DataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsFile}" Value="False"/>
|
||||
<Condition Binding="{Binding IsNodeExpanded}" Value="True"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter TargetName="icon" Property="Data" Value="{StaticResource Icon.Folder.Open}"/>
|
||||
</MultiDataTrigger>
|
||||
</HierarchicalDataTemplate.Triggers>
|
||||
</HierarchicalDataTemplate>
|
||||
</TreeView.ItemTemplate>
|
||||
</TreeView>
|
||||
</Border>
|
||||
|
||||
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Transparent"/>
|
||||
|
||||
<Border Grid.Column="2" BorderThickness="1" Margin="2,0" BorderBrush="{StaticResource Brush.Border2}">
|
||||
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
|
||||
<TextBlock FontSize="10pt"
|
||||
FontFamily="Consolas"
|
||||
Padding="8"
|
||||
Opacity="0.8"
|
||||
Background="{StaticResource Brush.BG2}"
|
||||
Foreground="{StaticResource Brush.FG}"
|
||||
x:Name="filePreview"/>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</UserControl>
|
468
SourceGit/UI/CommitViewer.xaml.cs
Normal file
468
SourceGit/UI/CommitViewer.xaml.cs
Normal file
|
@ -0,0 +1,468 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Navigation;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Commit detail viewer
|
||||
/// </summary>
|
||||
public partial class CommitViewer : UserControl {
|
||||
private Git.Repository repo = null;
|
||||
private Git.Commit commit = null;
|
||||
private List<Git.Change> cachedChanges = new List<Git.Change>();
|
||||
private List<Git.Change> displayChanges = new List<Git.Change>();
|
||||
private string changeFilter = null;
|
||||
|
||||
/// <summary>
|
||||
/// Node for file tree.
|
||||
/// </summary>
|
||||
public class Node {
|
||||
public string FilePath { get; set; } = "";
|
||||
public string OriginalPath { get; set; } = "";
|
||||
public string Name { get; set; } = "";
|
||||
public bool IsFile { get; set; } = false;
|
||||
public bool IsNodeExpanded { get; set; } = true;
|
||||
public Git.Change Change { get; set; } = null;
|
||||
public List<Node> Children { get; set; } = new List<Node>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public CommitViewer() {
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
#region DATA
|
||||
public void SetData(Git.Repository opened, Git.Commit selected) {
|
||||
repo = opened;
|
||||
commit = selected;
|
||||
|
||||
SetBaseInfo(commit);
|
||||
|
||||
Task.Run(() => {
|
||||
cachedChanges.Clear();
|
||||
cachedChanges = commit.GetChanges(repo);
|
||||
|
||||
Dispatcher.Invoke(() => {
|
||||
changeList1.ItemsSource = null;
|
||||
changeList1.ItemsSource = cachedChanges;
|
||||
});
|
||||
|
||||
LayoutChanges();
|
||||
SetRevisionFiles(commit.GetFiles(repo));
|
||||
});
|
||||
}
|
||||
|
||||
private void Cleanup(object sender, RoutedEventArgs e) {
|
||||
fileTree.ItemsSource = null;
|
||||
changeList1.ItemsSource = null;
|
||||
changeList2.ItemsSource = null;
|
||||
displayChanges.Clear();
|
||||
cachedChanges.Clear();
|
||||
diffViewer.Reset();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region BASE_INFO
|
||||
private void SetBaseInfo(Git.Commit commit) {
|
||||
var parentIds = new List<string>();
|
||||
foreach (var p in commit.Parents) parentIds.Add(p.Substring(0, 8));
|
||||
|
||||
SHA.Text = commit.SHA;
|
||||
refs.ItemsSource = commit.Decorators;
|
||||
parents.ItemsSource = parentIds;
|
||||
author.Text = $"{commit.Author.Name} <{commit.Author.Email}>";
|
||||
authorTime.Text = commit.Author.Time;
|
||||
committer.Text = $"{commit.Committer.Name} <{commit.Committer.Email}>";
|
||||
committerTime.Text = commit.Committer.Time;
|
||||
subject.Text = commit.Subject;
|
||||
message.Text = commit.Message.Trim();
|
||||
|
||||
if (commit.Decorators.Count == 0) lblRefs.Visibility = Visibility.Collapsed;
|
||||
else lblRefs.Visibility = Visibility.Visible;
|
||||
|
||||
if (commit.Committer.Email == commit.Author.Email && commit.Committer.Time == commit.Author.Time) {
|
||||
committerRow.Height = new GridLength(0);
|
||||
} else {
|
||||
committerRow.Height = GridLength.Auto;
|
||||
}
|
||||
}
|
||||
|
||||
private void NavigateParent(object sender, RequestNavigateEventArgs e) {
|
||||
repo.OnNavigateCommit?.Invoke(e.Uri.OriginalString);
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region CHANGES
|
||||
private void LayoutChanges() {
|
||||
displayChanges.Clear();
|
||||
|
||||
if (string.IsNullOrEmpty(changeFilter)) {
|
||||
displayChanges.AddRange(cachedChanges);
|
||||
} else {
|
||||
foreach (var c in cachedChanges) {
|
||||
if (c.Path.ToUpper().Contains(changeFilter)) displayChanges.Add(c);
|
||||
}
|
||||
}
|
||||
|
||||
List<Node> changeTreeSource = new List<Node>();
|
||||
Dictionary<string, Node> folders = new Dictionary<string, Node>();
|
||||
bool isDefaultExpanded = displayChanges.Count < 50;
|
||||
|
||||
foreach (var c in displayChanges) {
|
||||
var sepIdx = c.Path.IndexOf('/');
|
||||
if (sepIdx == -1) {
|
||||
Node node = new Node();
|
||||
node.FilePath = c.Path;
|
||||
node.IsFile = true;
|
||||
node.Name = c.Path;
|
||||
node.Change = c;
|
||||
node.IsNodeExpanded = isDefaultExpanded;
|
||||
if (c.OriginalPath != null) node.OriginalPath = c.OriginalPath;
|
||||
changeTreeSource.Add(node);
|
||||
} else {
|
||||
Node lastFolder = null;
|
||||
var start = 0;
|
||||
|
||||
while (sepIdx != -1) {
|
||||
var folder = c.Path.Substring(0, sepIdx);
|
||||
if (folders.ContainsKey(folder)) {
|
||||
lastFolder = folders[folder];
|
||||
} else if (lastFolder == null) {
|
||||
lastFolder = new Node();
|
||||
lastFolder.FilePath = folder;
|
||||
lastFolder.Name = folder.Substring(start);
|
||||
lastFolder.IsNodeExpanded = isDefaultExpanded;
|
||||
changeTreeSource.Add(lastFolder);
|
||||
folders.Add(folder, lastFolder);
|
||||
} else {
|
||||
var folderNode = new Node();
|
||||
folderNode.FilePath = folder;
|
||||
folderNode.Name = folder.Substring(start);
|
||||
folderNode.IsNodeExpanded = isDefaultExpanded;
|
||||
folders.Add(folder, folderNode);
|
||||
lastFolder.Children.Add(folderNode);
|
||||
lastFolder = folderNode;
|
||||
}
|
||||
|
||||
start = sepIdx + 1;
|
||||
sepIdx = c.Path.IndexOf('/', start);
|
||||
}
|
||||
|
||||
Node node = new Node();
|
||||
node.FilePath = c.Path;
|
||||
node.Name = c.Path.Substring(start);
|
||||
node.IsFile = true;
|
||||
node.Change = c;
|
||||
if (c.OriginalPath != null) node.OriginalPath = c.OriginalPath;
|
||||
lastFolder.Children.Add(node);
|
||||
}
|
||||
}
|
||||
|
||||
folders.Clear();
|
||||
SortTreeNodes(changeTreeSource);
|
||||
|
||||
Dispatcher.Invoke(() => {
|
||||
changeList2.ItemsSource = null;
|
||||
changeList2.ItemsSource = displayChanges;
|
||||
changeTree.ItemsSource = changeTreeSource;
|
||||
diffViewer.Reset();
|
||||
});
|
||||
}
|
||||
|
||||
private void SearchChangeFileTextChanged(object sender, TextChangedEventArgs e) {
|
||||
changeFilter = txtChangeFilter.Text.ToUpper();
|
||||
Task.Run(() => LayoutChanges());
|
||||
}
|
||||
|
||||
private async void ChangeTreeItemSelected(object sender, RoutedPropertyChangedEventArgs<object> e) {
|
||||
diffViewer.Reset();
|
||||
|
||||
var node = e.NewValue as Node;
|
||||
if (node == null || !node.IsFile) return;
|
||||
|
||||
var start = $"{commit.SHA}^";
|
||||
if (commit.Parents.Count == 0) {
|
||||
start = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
||||
}
|
||||
|
||||
List<string> data = new List<string>();
|
||||
|
||||
await Task.Run(() => {
|
||||
data = repo.Diff(start, commit.SHA, node.FilePath, node.OriginalPath);
|
||||
});
|
||||
|
||||
diffViewer.SetData(data, node.FilePath, node.OriginalPath);
|
||||
}
|
||||
|
||||
private async void ChangeListSelectionChanged(object sender, SelectionChangedEventArgs e) {
|
||||
if (e.AddedItems.Count != 1) return;
|
||||
|
||||
var change = e.AddedItems[0] as Git.Change;
|
||||
if (change == null) return;
|
||||
|
||||
var start = $"{commit.SHA}^";
|
||||
if (commit.Parents.Count == 0) {
|
||||
start = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
||||
}
|
||||
|
||||
List<string> data = new List<string>();
|
||||
|
||||
await Task.Run(() => {
|
||||
data = repo.Diff(start, commit.SHA, change.Path, change.OriginalPath);
|
||||
});
|
||||
|
||||
diffViewer.SetData(data, change.Path, change.OriginalPath);
|
||||
}
|
||||
|
||||
private void ChangeListContextMenuOpening(object sender, ContextMenuEventArgs e) {
|
||||
var row = sender as DataGridRow;
|
||||
if (row == null) return;
|
||||
|
||||
var change = row.DataContext as Git.Change;
|
||||
if (change == null) return;
|
||||
|
||||
var path = change.Path;
|
||||
var menu = new ContextMenu();
|
||||
if (change.Index != Git.Change.Status.Deleted) {
|
||||
MenuItem history = new MenuItem();
|
||||
history.Header = "File History";
|
||||
history.Click += (o, ev) => {
|
||||
var viewer = new FileHistories(repo, path);
|
||||
viewer.Show();
|
||||
};
|
||||
menu.Items.Add(history);
|
||||
|
||||
MenuItem blame = new MenuItem();
|
||||
blame.Header = "Blame";
|
||||
blame.Click += (obj, ev) => {
|
||||
Blame viewer = new Blame(repo, path, commit.SHA);
|
||||
viewer.Show();
|
||||
};
|
||||
menu.Items.Add(blame);
|
||||
|
||||
MenuItem explore = new MenuItem();
|
||||
explore.Header = "Reveal in File Explorer";
|
||||
explore.Click += (o, ev) => {
|
||||
var absPath = Path.GetFullPath(repo.Path + "\\" + path);
|
||||
Process.Start("explorer", $"/select,{absPath}");
|
||||
e.Handled = true;
|
||||
};
|
||||
menu.Items.Add(explore);
|
||||
|
||||
MenuItem saveAs = new MenuItem();
|
||||
saveAs.Header = "Save As ...";
|
||||
saveAs.Click += (obj, ev) => {
|
||||
var dialog = new System.Windows.Forms.FolderBrowserDialog();
|
||||
dialog.Description = change.Path;
|
||||
dialog.ShowNewFolderButton = true;
|
||||
|
||||
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
|
||||
var savePath = Path.Combine(dialog.SelectedPath, Path.GetFileName(path));
|
||||
repo.RunAndRedirect($"show {commit.SHA}:\"{path}\"", savePath);
|
||||
}
|
||||
};
|
||||
menu.Items.Add(saveAs);
|
||||
}
|
||||
|
||||
MenuItem copyPath = new MenuItem();
|
||||
copyPath.Header = "Copy Path";
|
||||
copyPath.Click += (obj, ev) => {
|
||||
Clipboard.SetText(path);
|
||||
};
|
||||
menu.Items.Add(copyPath);
|
||||
menu.IsOpen = true;
|
||||
e.Handled = true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region FILES
|
||||
private void SetRevisionFiles(List<string> files) {
|
||||
List<Node> fileTreeSource = new List<Node>();
|
||||
Dictionary<string, Node> folders = new Dictionary<string, Node>();
|
||||
|
||||
foreach (var path in files) {
|
||||
var sepIdx = path.IndexOf("/");
|
||||
if (sepIdx == -1) {
|
||||
Node node = new Node();
|
||||
node.FilePath = path;
|
||||
node.Name = path;
|
||||
node.IsFile = true;
|
||||
node.IsNodeExpanded = false;
|
||||
fileTreeSource.Add(node);
|
||||
} else {
|
||||
Node lastFolder = null;
|
||||
var start = 0;
|
||||
|
||||
while (sepIdx != -1) {
|
||||
var folder = path.Substring(0, sepIdx);
|
||||
if (folders.ContainsKey(folder)) {
|
||||
lastFolder = folders[folder];
|
||||
} else if (lastFolder == null) {
|
||||
lastFolder = new Node();
|
||||
lastFolder.FilePath = folder;
|
||||
lastFolder.Name = folder.Substring(start);
|
||||
lastFolder.IsNodeExpanded = false;
|
||||
fileTreeSource.Add(lastFolder);
|
||||
folders.Add(folder, lastFolder);
|
||||
} else {
|
||||
var folderNode = new Node();
|
||||
folderNode.FilePath = folder;
|
||||
folderNode.Name = folder.Substring(start);
|
||||
folderNode.IsNodeExpanded = false;
|
||||
folders.Add(folder, folderNode);
|
||||
lastFolder.Children.Add(folderNode);
|
||||
lastFolder = folderNode;
|
||||
}
|
||||
|
||||
start = sepIdx + 1;
|
||||
sepIdx = path.IndexOf('/', start);
|
||||
}
|
||||
|
||||
Node node = new Node();
|
||||
node.FilePath = path;
|
||||
node.Name = path.Substring(start);
|
||||
node.IsFile = true;
|
||||
node.IsNodeExpanded = false;
|
||||
lastFolder.Children.Add(node);
|
||||
}
|
||||
}
|
||||
|
||||
folders.Clear();
|
||||
SortTreeNodes(fileTreeSource);
|
||||
|
||||
Dispatcher.Invoke(() => {
|
||||
fileTree.ItemsSource = fileTreeSource;
|
||||
filePreview.Text = "";
|
||||
});
|
||||
}
|
||||
|
||||
private async void FileTreeItemSelected(object sender, RoutedPropertyChangedEventArgs<object> e) {
|
||||
filePreview.Text = "";
|
||||
|
||||
var node = e.NewValue as Node;
|
||||
if (node == null || !node.IsFile) return;
|
||||
|
||||
await Task.Run(() => {
|
||||
var data = commit.GetTextFileContent(repo, node.FilePath);
|
||||
Dispatcher.Invoke(() => filePreview.Text = data);
|
||||
});
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region TREE_COMMON
|
||||
private void SortTreeNodes(List<Node> list) {
|
||||
list.Sort((l, r) => {
|
||||
if (l.IsFile) {
|
||||
return r.IsFile ? l.Name.CompareTo(r.Name) : 1;
|
||||
} else {
|
||||
return r.IsFile ? -1 : l.Name.CompareTo(r.Name);
|
||||
}
|
||||
});
|
||||
|
||||
foreach (var sub in list) {
|
||||
if (sub.Children.Count > 0) SortTreeNodes(sub.Children);
|
||||
}
|
||||
}
|
||||
|
||||
private ScrollViewer GetScrollViewer(FrameworkElement owner) {
|
||||
if (owner == null) return null;
|
||||
if (owner is ScrollViewer) return owner as ScrollViewer;
|
||||
|
||||
int n = VisualTreeHelper.GetChildrenCount(owner);
|
||||
for (int i = 0; i < n; i++) {
|
||||
var child = VisualTreeHelper.GetChild(owner, i) as FrameworkElement;
|
||||
var deep = GetScrollViewer(child);
|
||||
if (deep != null) return deep;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void TreeMouseWheel(object sender, MouseWheelEventArgs e) {
|
||||
var scroll = GetScrollViewer(sender as TreeView);
|
||||
if (scroll == null) return;
|
||||
|
||||
if (e.Delta > 0) {
|
||||
scroll.LineUp();
|
||||
} else {
|
||||
scroll.LineDown();
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void TreeContextMenuOpening(object sender, ContextMenuEventArgs e) {
|
||||
var item = sender as TreeViewItem;
|
||||
if (item == null) return;
|
||||
|
||||
var node = item.DataContext as Node;
|
||||
if (node == null || !node.IsFile) return;
|
||||
|
||||
item.IsSelected = true;
|
||||
|
||||
ContextMenu menu = new ContextMenu();
|
||||
if (node.Change == null || node.Change.Index != Git.Change.Status.Deleted) {
|
||||
MenuItem history = new MenuItem();
|
||||
history.Header = "File History";
|
||||
history.Click += (o, ev) => {
|
||||
var viewer = new FileHistories(repo, node.FilePath);
|
||||
viewer.Show();
|
||||
};
|
||||
menu.Items.Add(history);
|
||||
|
||||
MenuItem blame = new MenuItem();
|
||||
blame.Header = "Blame";
|
||||
blame.Click += (obj, ev) => {
|
||||
Blame viewer = new Blame(repo, node.FilePath, commit.SHA);
|
||||
viewer.Show();
|
||||
};
|
||||
menu.Items.Add(blame);
|
||||
|
||||
MenuItem explore = new MenuItem();
|
||||
explore.Header = "Reveal in File Explorer";
|
||||
explore.Click += (o, ev) => {
|
||||
var path = Path.GetFullPath(repo.Path + "\\" + node.FilePath);
|
||||
Process.Start("explorer", $"/select,{path}");
|
||||
e.Handled = true;
|
||||
};
|
||||
menu.Items.Add(explore);
|
||||
|
||||
MenuItem saveAs = new MenuItem();
|
||||
saveAs.Header = "Save As ...";
|
||||
saveAs.Click += (obj, ev) => {
|
||||
var dialog = new System.Windows.Forms.FolderBrowserDialog();
|
||||
dialog.Description = node.FilePath;
|
||||
dialog.ShowNewFolderButton = true;
|
||||
|
||||
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
|
||||
var path = Path.Combine(dialog.SelectedPath, node.Name);
|
||||
repo.RunAndRedirect($"show {commit.SHA}:\"{node.FilePath}\"", path);
|
||||
}
|
||||
};
|
||||
menu.Items.Add(saveAs);
|
||||
}
|
||||
|
||||
MenuItem copyPath = new MenuItem();
|
||||
copyPath.Header = "Copy Path";
|
||||
copyPath.Click += (obj, ev) => {
|
||||
Clipboard.SetText(node.FilePath);
|
||||
};
|
||||
menu.Items.Add(copyPath);
|
||||
menu.IsOpen = true;
|
||||
e.Handled = true;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
92
SourceGit/UI/CreateBranch.xaml
Normal file
92
SourceGit/UI/CreateBranch.xaml
Normal file
|
@ -0,0 +1,92 @@
|
|||
<UserControl x:Class="SourceGit.UI.CreateBranch"
|
||||
x:Name="me"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:helpers="clr-namespace:SourceGit.Helpers"
|
||||
xmlns:converters="clr-namespace:SourceGit.Converters"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="192" d:DesignWidth="500" Height="224" Width="500">
|
||||
<UserControl.Resources>
|
||||
<converters:InverseBool x:Key="InverseBool"/>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.ColumnSpan="2" FontWeight="DemiBold" FontSize="18" Content="Create Local Branch"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right" Content="Based On :"/>
|
||||
<StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal">
|
||||
<Path x:Name="basedOnType" Width="12" Style="{StaticResource Style.Icon}"/>
|
||||
<Label x:Name="basedOnDesc" VerticalAlignment="Center" Content="master"/>
|
||||
</StackPanel>
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center" Content="New Branch Name :"/>
|
||||
<TextBox Grid.Row="3" Grid.Column="1"
|
||||
x:Name="txtName"
|
||||
VerticalContentAlignment="Center"
|
||||
Height="24"
|
||||
helpers:TextBoxHelper.Placeholder="Enter branch name.">
|
||||
<TextBox.Text>
|
||||
<Binding ElementName="me" Path="BranchName" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
|
||||
<Binding.ValidationRules>
|
||||
<helpers:BranchNameRule x:Name="nameValidator"/>
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
|
||||
<Label Grid.Row="4" Grid.Column="0" HorizontalAlignment="Right" Content="Local Changes :"/>
|
||||
<StackPanel Grid.Row="4" Grid.Column="1" Orientation="Horizontal">
|
||||
<RadioButton Content="Stash & Reapply" GroupName="LocalChanges" IsChecked="{Binding AutoStash, ElementName=me}"/>
|
||||
<RadioButton Content="Discard" Margin="8,0,0,0" GroupName="LocalChanges" IsChecked="{Binding AutoStash, ElementName=me, Mode=OneWay, Converter={StaticResource InverseBool}}"/>
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox Grid.Row="5" Grid.Column="1"
|
||||
x:Name="chkCheckout"
|
||||
VerticalAlignment="Center"
|
||||
IsChecked="True"
|
||||
Content="Check out after created"/>
|
||||
|
||||
<Grid Grid.Row="7" Grid.ColumnSpan="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Column="1" Click="Start" Content="SURE" Style="{StaticResource Style.Button.AccentBordered}"/>
|
||||
<Button Grid.Column="3" Click="Cancel" Content="CANCEL" Style="{StaticResource Style.Button.Bordered}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="0" Grid.RowSpan="8" Grid.ColumnSpan="2" x:Name="status" Visibility="Collapsed" Background="{StaticResource Brush.BG1}" Opacity=".9">
|
||||
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Path x:Name="statusIcon" Width="48" Height="48" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Loading}" RenderTransformOrigin=".5,.5">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
|
||||
<Label x:Name="statusMsg" Margin="0,8,0,0"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
143
SourceGit/UI/CreateBranch.xaml.cs
Normal file
143
SourceGit/UI/CreateBranch.xaml.cs
Normal file
|
@ -0,0 +1,143 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Create branch dialog
|
||||
/// </summary>
|
||||
public partial class CreateBranch : UserControl {
|
||||
private Git.Repository repo = null;
|
||||
private string based = null;
|
||||
|
||||
/// <summary>
|
||||
/// New branch name.
|
||||
/// </summary>
|
||||
public string BranchName {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Auto Stash
|
||||
/// </summary>
|
||||
public bool AutoStash { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="opened">Opened repository</param>
|
||||
public CreateBranch(Git.Repository opened) {
|
||||
InitializeComponent();
|
||||
|
||||
repo = opened;
|
||||
nameValidator.Repo = opened;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create branch based on current head.
|
||||
/// </summary>
|
||||
/// <param name="repo"></param>
|
||||
public static void Show(Git.Repository repo) {
|
||||
var current = repo.CurrentBranch();
|
||||
if (current != null) Show(repo, current);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create branch base on existed one.
|
||||
/// </summary>
|
||||
/// <param name="repo"></param>
|
||||
/// <param name="branch"></param>
|
||||
public static void Show(Git.Repository repo, Git.Branch branch) {
|
||||
var dialog = new CreateBranch(repo);
|
||||
dialog.based = branch.Name;
|
||||
dialog.basedOnType.Data = dialog.FindResource("Icon.Branch") as Geometry;
|
||||
dialog.basedOnDesc.Content = branch.Name;
|
||||
|
||||
if (!branch.IsLocal) dialog.txtName.Text = branch.Name.Substring(branch.Remote.Length + 1);
|
||||
PopupManager.Show(dialog);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create branch based on tag.
|
||||
/// </summary>
|
||||
/// <param name="repo"></param>
|
||||
/// <param name="tag"></param>
|
||||
public static void Show(Git.Repository repo, Git.Tag tag) {
|
||||
var dialog = new CreateBranch(repo);
|
||||
dialog.based = tag.Name;
|
||||
dialog.basedOnType.Data = dialog.FindResource("Icon.Tag") as Geometry;
|
||||
dialog.basedOnDesc.Content = tag.Name;
|
||||
PopupManager.Show(dialog);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create branch based on commit.
|
||||
/// </summary>
|
||||
/// <param name="repo"></param>
|
||||
/// <param name="commit"></param>
|
||||
public static void Show(Git.Repository repo, Git.Commit commit) {
|
||||
var dialog = new CreateBranch(repo);
|
||||
dialog.based = commit.SHA;
|
||||
dialog.basedOnType.Data = dialog.FindResource("Icon.Commit") as Geometry;
|
||||
dialog.basedOnDesc.Content = $"{commit.ShortSHA} {commit.Subject}";
|
||||
PopupManager.Show(dialog);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start create branch.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void Start(object sender, RoutedEventArgs e) {
|
||||
txtName.GetBindingExpression(TextBox.TextProperty).UpdateSource();
|
||||
if (Validation.GetHasError(txtName)) return;
|
||||
|
||||
PopupManager.Lock();
|
||||
|
||||
status.Visibility = Visibility.Visible;
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
|
||||
bool checkout = chkCheckout.IsChecked == true;
|
||||
await Task.Run(() => {
|
||||
if (checkout) {
|
||||
bool stashed = false;
|
||||
|
||||
if (repo.LocalChanges().Count > 0 && AutoStash) {
|
||||
Git.Stash.Push(repo, true, "CREATE BRANCH AUTO STASH", new List<string>());
|
||||
stashed = true;
|
||||
}
|
||||
|
||||
repo.Checkout($"-b {BranchName} {based}");
|
||||
|
||||
if (stashed) {
|
||||
var stashes = repo.Stashes();
|
||||
if (stashes.Count > 0) stashes[0].Pop(repo);
|
||||
}
|
||||
} else {
|
||||
Git.Branch.Create(repo, BranchName, based);
|
||||
}
|
||||
});
|
||||
|
||||
status.Visibility = Visibility.Collapsed;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
PopupManager.Close(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Cancel(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Close();
|
||||
}
|
||||
}
|
||||
}
|
68
SourceGit/UI/CreateTag.xaml
Normal file
68
SourceGit/UI/CreateTag.xaml
Normal file
|
@ -0,0 +1,68 @@
|
|||
<UserControl x:Class="SourceGit.UI.CreateTag"
|
||||
x:Name="me"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:helpers="clr-namespace:SourceGit.Helpers"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="224" d:DesignWidth="500" Width="500" Height="224">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="64"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.ColumnSpan="2" FontWeight="DemiBold" FontSize="18" Content="Create Tag"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center" Content="New Tag At :"/>
|
||||
<StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal">
|
||||
<Path Width="12" x:Name="basedOnType" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Commit}"/>
|
||||
<Label x:Name="basedOnDesc" VerticalAlignment="Center" Content="xxx"/>
|
||||
</StackPanel>
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="0" HorizontalAlignment="Right" Content="Tag Name :"/>
|
||||
<TextBox Grid.Row="3" Grid.Column="1"
|
||||
x:Name="tagName"
|
||||
Height="24"
|
||||
helpers:TextBoxHelper.Placeholder="Recommanded format :v1.0.0-alpha">
|
||||
<TextBox.Text>
|
||||
<Binding ElementName="me" Path="TagName" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
|
||||
<Binding.ValidationRules>
|
||||
<helpers:TagNameRule x:Name="nameValidator"/>
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
|
||||
<Label Grid.Row="4" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,4" Content="Tag Message :"/>
|
||||
<TextBox Grid.Row="4" Grid.Column="1"
|
||||
x:Name="tagMessage"
|
||||
Height="56"
|
||||
AcceptsReturn="True"
|
||||
helpers:TextBoxHelper.Placeholder="Optional"/>
|
||||
|
||||
<Grid Grid.Row="6" Grid.ColumnSpan="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Column="1" Click="Start" Content="SURE" Style="{StaticResource Style.Button.AccentBordered}"/>
|
||||
<Button Grid.Column="3" Click="Cancel" Content="CANCEL" Style="{StaticResource Style.Button.Bordered}"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
92
SourceGit/UI/CreateTag.xaml.cs
Normal file
92
SourceGit/UI/CreateTag.xaml.cs
Normal file
|
@ -0,0 +1,92 @@
|
|||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Create tag dialog
|
||||
/// </summary>
|
||||
public partial class CreateTag : UserControl {
|
||||
private Git.Repository repo = null;
|
||||
private string based = null;
|
||||
|
||||
/// <summary>
|
||||
/// Tag name
|
||||
/// </summary>
|
||||
public string TagName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="repo"></param>
|
||||
public CreateTag(Git.Repository opened) {
|
||||
InitializeComponent();
|
||||
|
||||
repo = opened;
|
||||
nameValidator.Repo = opened;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create tag using current branch.
|
||||
/// </summary>
|
||||
/// <param name="repo">Opened repository.</param>
|
||||
public static void Show(Git.Repository repo) {
|
||||
Show(repo, repo.Branches().First(b => b.IsCurrent));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create tag using branch
|
||||
/// </summary>
|
||||
/// <param name="repo"></param>
|
||||
/// <param name="branch"></param>
|
||||
public static void Show(Git.Repository repo, Git.Branch branch) {
|
||||
if (branch == null) {
|
||||
App.RaiseError("Empty repository!");
|
||||
return;
|
||||
}
|
||||
|
||||
var dialog = new CreateTag(repo);
|
||||
dialog.based = branch.Head;
|
||||
dialog.basedOnType.Data = dialog.FindResource("Icon.Branch") as Geometry;
|
||||
dialog.basedOnDesc.Content = branch.Name;
|
||||
PopupManager.Show(dialog);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create tag using commit.
|
||||
/// </summary>
|
||||
/// <param name="repo"></param>
|
||||
/// <param name="commit"></param>
|
||||
public static void Show(Git.Repository repo, Git.Commit commit) {
|
||||
var dialog = new CreateTag(repo);
|
||||
dialog.based = commit.SHA;
|
||||
dialog.basedOnType.Data = dialog.FindResource("Icon.Commit") as Geometry;
|
||||
dialog.basedOnDesc.Content = $"{commit.ShortSHA} {commit.Subject}";
|
||||
PopupManager.Show(dialog);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start to create tag.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Start(object sender, RoutedEventArgs e) {
|
||||
tagName.GetBindingExpression(TextBox.TextProperty).UpdateSource();
|
||||
if (Validation.GetHasError(tagName)) return;
|
||||
|
||||
Git.Tag.Add(repo, TagName, based, tagMessage.Text);
|
||||
PopupManager.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Cancel(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Close();
|
||||
}
|
||||
}
|
||||
}
|
450
SourceGit/UI/Dashboard.xaml
Normal file
450
SourceGit/UI/Dashboard.xaml
Normal file
|
@ -0,0 +1,450 @@
|
|||
<UserControl x:Class="SourceGit.UI.Dashboard"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:source="clr-namespace:SourceGit"
|
||||
xmlns:local="clr-namespace:SourceGit.UI"
|
||||
xmlns:git="clr-namespace:SourceGit.Git"
|
||||
xmlns:converters="clr-namespace:SourceGit.Converters"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
Unloaded="Cleanup">
|
||||
<UserControl.Resources>
|
||||
<RoutedUICommand x:Key="OpenSearchBarCommand" Text="OpenSearchBar"/>
|
||||
<RoutedUICommand x:Key="HideSearchBarCommand" Text="HideSearchBar"/>
|
||||
</UserControl.Resources>
|
||||
|
||||
<UserControl.InputBindings>
|
||||
<KeyBinding Key="F" Modifiers="Ctrl" Command="{StaticResource OpenSearchBarCommand}"/>
|
||||
<KeyBinding Key="ESC" Command="{StaticResource HideSearchBarCommand}"/>
|
||||
</UserControl.InputBindings>
|
||||
|
||||
<UserControl.CommandBindings>
|
||||
<CommandBinding Command="{StaticResource OpenSearchBarCommand}" Executed="OpenSearchBar"/>
|
||||
<CommandBinding Command="{StaticResource HideSearchBarCommand}" Executed="HideSearchBar"/>
|
||||
</UserControl.CommandBindings>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- TitleBar -->
|
||||
<Grid Grid.Row="0" Panel.ZIndex="9999">
|
||||
<Border Background="{StaticResource Brush.BG1}">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect ShadowDepth="2" Direction="270" Opacity=".3"/>
|
||||
</Border.Effect>
|
||||
</Border>
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Navigation -->
|
||||
<StackPanel Grid.Column="0" Margin="8,0,0,0" Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<Button Click="Close" ToolTip="Back To Welcome" Padding="0">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Path Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Manager}"/>
|
||||
<Label Content="Repositories"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Path Width="12" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Navigator}"/>
|
||||
<Path Margin="6,0,0,0" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Git}"/>
|
||||
<Label x:Name="repoName"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Common Git Options -->
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal">
|
||||
<Button Click="OpenFetch" Margin="4,0">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Path Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Fetch}"/>
|
||||
<Label Content="Fetch"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Click="OpenPull" Margin="4,0">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Path Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Pull}"/>
|
||||
<Label Content="Pull"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Click="OpenPush" Margin="4,0">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Path Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Push}"/>
|
||||
<Label Content="Push"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Click="OpenStash" Margin="2,0,0,0">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Path Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.SaveStash}"/>
|
||||
<Label Content="Stash"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Click="OpenApply" Margin="4,0">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Path Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Apply}"/>
|
||||
<Label Content="Apply"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<!-- External Options -->
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button Click="OpenSearch" Margin="4,0" ToolTip="Search Commit">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Path Width="14" Height="14" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Search}"/>
|
||||
<Label Content="Search"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Click="OpenExplorer" Margin="4,0" ToolTip="Open In File Browser">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Path Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Folder.Open}"/>
|
||||
<Label Content="Explore"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Click="OpenTerminal" Margin="4,0" ToolTip="Open Git Bash">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Path Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Terminal}"/>
|
||||
<Label Content="Terminal"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<!-- Main body -->
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="200" MinWidth="200" MaxWidth="300"/>
|
||||
<ColumnDefinition Width="1"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Left panel -->
|
||||
<Grid Grid.Column="0" x:Name="main" Background="{StaticResource Brush.BG4}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.Resources>
|
||||
<converters:BoolToCollapsed x:Key="Bool2Collapsed"/>
|
||||
</Grid.Resources>
|
||||
|
||||
<!-- WORKSPACE -->
|
||||
<Label Grid.Row="0" Margin="4,0,0,0" Content="WORKSPACE" Style="{StaticResource Style.Label.GroupHeader}" />
|
||||
<ListView
|
||||
Grid.Row="1"
|
||||
x:Name="workspace"
|
||||
Background="{StaticResource Brush.BG3}"
|
||||
Style="{StaticResource Style.ListView.Borderless}">
|
||||
<ListViewItem x:Name="historiesSwitch" Selected="SwitchHistories" IsSelected="True">
|
||||
<StackPanel Margin="16,0,0,0" Orientation="Horizontal">
|
||||
<Path Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Histories}"/>
|
||||
<Label Margin="4,0,0,0" Content="Histories"/>
|
||||
</StackPanel>
|
||||
</ListViewItem>
|
||||
|
||||
<ListViewItem x:Name="workingCopySwitch" Selected="SwitchWorkingCopy">
|
||||
<Grid Margin="16,0,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Path Grid.Column="0" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.WorkingCopy}"/>
|
||||
<Label Grid.Column="1" Margin="4,0,0,0" Content="Commit"/>
|
||||
<Border Grid.Column="2" x:Name="localChangesBadge" Style="{StaticResource Style.Border.Badge}">
|
||||
<Label x:Name="localChangesCount" Margin="4,-2,4,-2" Content="999" FontSize="10"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
</ListViewItem>
|
||||
|
||||
<ListViewItem x:Name="stashesSwitch" Selected="SwitchStashes">
|
||||
<Grid Margin="16,0,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Path Grid.Column="0" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Stashes}"/>
|
||||
<Label Grid.Column="1" Margin="4,0,0,0" Content="Stashes"/>
|
||||
<Border Grid.Column="2" x:Name="stashBadge" Style="{StaticResource Style.Border.Badge}">
|
||||
<Label x:Name="stashCount" Margin="4,-2,4,-2" Content="999" FontSize="10"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
</ListViewItem>
|
||||
</ListView>
|
||||
|
||||
<!-- LOCAL BRANCHES -->
|
||||
<Grid Grid.Row="2" Margin="4,0,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="16"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="16"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Column="0" Content="LOCAL BRANCHES" Style="{StaticResource Style.Label.GroupHeader}"/>
|
||||
<Button Grid.Column="1" Click="OpenGitFlow" Background="Transparent" ToolTip="GIT FLOW">
|
||||
<Path Width="14" Height="14" Style="{StaticResource Style.Icon}" Data="{DynamicResource Icon.Flow}"/>
|
||||
</Button>
|
||||
<Button Grid.Column="3" Click="OpenNewBranch" Background="Transparent" ToolTip="NEW BRANCH">
|
||||
<Path Width="14" Height="14" Style="{StaticResource Style.Icon}" Data="{DynamicResource Icon.Branch.Add}"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
<TreeView
|
||||
Grid.Row="3"
|
||||
x:Name="localBranchTree"
|
||||
Background="{StaticResource Brush.BG3}"
|
||||
FontFamily="Consolas"
|
||||
LostFocus="TreeLostFocus"
|
||||
SelectedItemChanged="LocalBranchSelected"
|
||||
PreviewMouseWheel="TreeMouseWheel">
|
||||
<TreeView.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource Style.TreeView.ItemContainerStyle}">
|
||||
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
|
||||
<EventSetter Event="ContextMenuOpening" Handler="LocalBranchContextMenuOpening"/>
|
||||
<EventSetter Event="MouseDoubleClick" Handler="LocalBranchMouseDoubleClick"/>
|
||||
</Style>
|
||||
</TreeView.ItemContainerStyle>
|
||||
<TreeView.ItemTemplate>
|
||||
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
|
||||
<Grid Height="24">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="16"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Path Grid.Column="0" Width="10" x:Name="icon" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Branch}"/>
|
||||
<Label Grid.Column="1" x:Name="name" Content="{Binding Name}" Padding="4,0,0,0"/>
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal">
|
||||
<Border Style="{StaticResource Style.Border.Badge}" Visibility="{Binding TrackVisibility}">
|
||||
<Label Margin="4,-2,4,-2" Content="{Binding Branch.UpstreamTrack}" FontSize="10"/>
|
||||
</Border>
|
||||
<ToggleButton
|
||||
Visibility="{Binding FilterVisibility}"
|
||||
IsChecked="{Binding IsFiltered, Mode=OneWay}"
|
||||
Checked="FilterChanged"
|
||||
Unchecked="FilterChanged"
|
||||
Style="{StaticResource Style.ToggleButton.Filter}"
|
||||
ToolTip="FILTER"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<HierarchicalDataTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding IsCurrent}" Value="True">
|
||||
<Setter TargetName="name" Property="FontWeight" Value="ExtraBold"/>
|
||||
<Setter TargetName="icon" Property="Data" Value="{StaticResource Icon.Check}"/>
|
||||
</DataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding Branch}" Value="{x:Null}"/>
|
||||
<Condition Binding="{Binding IsExpanded}" Value="False"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter TargetName="icon" Property="Data" Value="{StaticResource Icon.Folder.Fill}"/>
|
||||
</MultiDataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding Branch}" Value="{x:Null}"/>
|
||||
<Condition Binding="{Binding IsExpanded}" Value="True"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter TargetName="icon" Property="Data" Value="{StaticResource Icon.Folder.Open}"/>
|
||||
</MultiDataTrigger>
|
||||
</HierarchicalDataTemplate.Triggers>
|
||||
</HierarchicalDataTemplate>
|
||||
</TreeView.ItemTemplate>
|
||||
</TreeView>
|
||||
|
||||
<!-- REMOTES -->
|
||||
<Grid Grid.Row="4" Margin="4,0,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="16"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Column="0" Content="REMOTES" Style="{StaticResource Style.Label.GroupHeader}"/>
|
||||
<Button Grid.Column="1" Click="OpenRemote" ToolTip="ADD REMOTE">
|
||||
<Path Width="14" Height="14" Style="{StaticResource Style.Icon}" Data="{DynamicResource Icon.Remote.Add}"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
<TreeView
|
||||
Grid.Row="5"
|
||||
x:Name="remoteBranchTree"
|
||||
Background="{StaticResource Brush.BG3}"
|
||||
FontFamily="Consolas"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||
SelectedItemChanged="RemoteBranchSelected"
|
||||
LostFocus="TreeLostFocus"
|
||||
PreviewMouseWheel="TreeMouseWheel">
|
||||
<TreeView.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource Style.TreeView.ItemContainerStyle}">
|
||||
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
|
||||
<EventSetter Event="ContextMenuOpening" Handler="RemoteContextMenuOpening"/>
|
||||
</Style>
|
||||
</TreeView.ItemContainerStyle>
|
||||
|
||||
<TreeView.Resources>
|
||||
<HierarchicalDataTemplate DataType="{x:Type local:RemoteNode}" ItemsSource="{Binding Children}">
|
||||
<Grid Height="24">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="16"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Path Grid.Column="0" Width="10" x:Name="icon" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Remote}"/>
|
||||
<TextBlock Grid.Column="1" x:Name="name" Text="{Binding Name}" Padding="4,0,0,0" VerticalAlignment="Center" Foreground="{StaticResource Brush.FG}" ClipToBounds="True"/>
|
||||
</Grid>
|
||||
</HierarchicalDataTemplate>
|
||||
|
||||
<HierarchicalDataTemplate DataType="{x:Type local:BranchNode}" ItemsSource="{Binding Children}">
|
||||
<Grid Height="24">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="16"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Path Grid.Column="0" Width="10" x:Name="icon" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Branch}"/>
|
||||
<TextBlock Grid.Column="1" x:Name="name" Text="{Binding Name}" Padding="4,0,0,0" VerticalAlignment="Center" Foreground="{StaticResource Brush.FG}" ClipToBounds="True"/>
|
||||
<ToggleButton
|
||||
Grid.Column="2"
|
||||
Visibility="{Binding FilterVisibility}"
|
||||
IsChecked="{Binding IsFiltered, Mode=OneWay}"
|
||||
Checked="FilterChanged"
|
||||
Unchecked="FilterChanged"
|
||||
Style="{StaticResource Style.ToggleButton.Filter}"
|
||||
ToolTip="FILTER"/>
|
||||
</Grid>
|
||||
|
||||
<HierarchicalDataTemplate.Triggers>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding Branch}" Value="{x:Null}"/>
|
||||
<Condition Binding="{Binding IsExpanded}" Value="False"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter TargetName="icon" Property="Data" Value="{StaticResource Icon.Folder.Fill}"/>
|
||||
</MultiDataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding Branch}" Value="{x:Null}"/>
|
||||
<Condition Binding="{Binding IsExpanded}" Value="True"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter TargetName="icon" Property="Data" Value="{StaticResource Icon.Folder.Open}"/>
|
||||
</MultiDataTrigger>
|
||||
</HierarchicalDataTemplate.Triggers>
|
||||
</HierarchicalDataTemplate>
|
||||
</TreeView.Resources>
|
||||
</TreeView>
|
||||
|
||||
<!-- TAGS -->
|
||||
<ToggleButton
|
||||
x:Name="tagListToggle"
|
||||
Grid.Row="6"
|
||||
Style="{StaticResource Style.ToggleButton.Expender}"
|
||||
IsChecked="{Binding Source={x:Static source:App.Preference}, Path=UIShowTags, Mode=TwoWay}">
|
||||
<Grid Margin="4,0,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="16"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Column="0" x:Name="tagCount" Content="TAGS" Style="{StaticResource Style.Label.GroupHeader}"/>
|
||||
<Button Grid.Column="1" Click="OpenNewTag" ToolTip="NEW TAG">
|
||||
<Path Width="14" Height="14" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Tag.Add}"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
</ToggleButton>
|
||||
<ListView
|
||||
Grid.Row="7"
|
||||
x:Name="tagList"
|
||||
Visibility="{Binding ElementName=tagListToggle, Path=IsChecked, Converter={StaticResource Bool2Collapsed}}"
|
||||
Background="{StaticResource Brush.BG3}"
|
||||
Height="200"
|
||||
LostFocus="TagLostFocus"
|
||||
SelectionChanged="TagSelectionChanged"
|
||||
ContextMenuOpening="TagContextMenuOpening"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||
Style="{StaticResource Style.ListView.Borderless}">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate DataType="{x:Type git:Tag}">
|
||||
<Grid Margin="16, 0, 0, 0" Height="20">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="16"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Path Grid.Column="0" Width="10" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Tag}"/>
|
||||
<Label Grid.Column="1" Margin="4,0,0,0" Content="{Binding Name}" Padding="4,0,0,0"/>
|
||||
<ToggleButton
|
||||
Grid.Column="2"
|
||||
IsChecked="{Binding IsFiltered, Mode=TwoWay}"
|
||||
Checked="FilterChanged"
|
||||
Unchecked="FilterChanged"
|
||||
Style="{StaticResource Style.ToggleButton.Filter}"
|
||||
ToolTip="FILTER"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Grid>
|
||||
|
||||
<!-- Splitter -->
|
||||
<GridSplitter Grid.Column="1" Width="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Transparent"/>
|
||||
|
||||
<!-- Right -->
|
||||
<Grid Grid.Column="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Abort panel -->
|
||||
<Grid x:Name="abortPanel" Grid.Row="0" Background="LightGoldenrodYellow" Visibility="Collapsed">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Column="0" x:Name="txtMergeProcessing" FontWeight="DemiBold" Foreground="{StaticResource Brush.BG4}"/>
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal">
|
||||
<Button x:Name="btnResolve" Click="Resolve" Content="RESOLVE" Margin="4">
|
||||
<Button.Style>
|
||||
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource Style.Button.Bordered}">
|
||||
<Setter Property="Background" Value="{StaticResource Brush.BG1}"/>
|
||||
<Setter Property="Margin" Value="2"/>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
</Button>
|
||||
<Button x:Name="btnContinue" Click="Continue" Content="CONTINUE" Style="{StaticResource Style.Button.AccentBordered}" Margin="4"/>
|
||||
<Button Grid.Column="3" Click="Abort" Content="ABORT" Style="{StaticResource Style.Button.Bordered}" Foreground="{StaticResource Brush.BG1}" Margin="4"/>
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
|
||||
<!-- Others -->
|
||||
<local:Histories Grid.Row="1" x:Name="histories" Visibility="Visible"/>
|
||||
<local:WorkingCopy Grid.Row="1" x:Name="commits" Visibility="Collapsed"/>
|
||||
<local:Stashes Grid.Row="1" x:Name="stashes" Visibility="Collapsed"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<!-- Popups -->
|
||||
<local:PopupManager Grid.Row="1"/>
|
||||
</Grid>
|
||||
</UserControl>
|
984
SourceGit/UI/Dashboard.xaml.cs
Normal file
984
SourceGit/UI/Dashboard.xaml.cs
Normal file
|
@ -0,0 +1,984 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Branch node in tree.
|
||||
/// </summary>
|
||||
public class BranchNode {
|
||||
public string Name { get; set; }
|
||||
public Git.Branch Branch { get; set; }
|
||||
public bool IsExpanded { get; set; }
|
||||
public bool IsCurrent => Branch != null ? Branch.IsCurrent : false;
|
||||
public bool IsFiltered => Branch != null ? Branch.IsFiltered : false;
|
||||
public string Track => Branch != null ? Branch.UpstreamTrack : "";
|
||||
public Visibility FilterVisibility => Branch == null ? Visibility.Collapsed : Visibility.Visible;
|
||||
public Visibility TrackVisibility => (Branch != null && !Branch.IsSameWithUpstream) ? Visibility.Visible : Visibility.Collapsed;
|
||||
public List<BranchNode> Children { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remote node in tree.
|
||||
/// </summary>
|
||||
public class RemoteNode {
|
||||
public string Name { get; set; }
|
||||
public bool IsExpanded { get; set; }
|
||||
public List<BranchNode> Children { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dashboard for opened repository.
|
||||
/// </summary>
|
||||
public partial class Dashboard : UserControl {
|
||||
private Git.Repository repo = null;
|
||||
private List<BranchNode> cachedLocalBranches = new List<BranchNode>();
|
||||
private List<RemoteNode> cachedRemotes = new List<RemoteNode>();
|
||||
private string abortCommand = null;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="repo">Opened repository.</param>
|
||||
public Dashboard(Git.Repository opened) {
|
||||
opened.OnWorkingCopyChanged = UpdateLocalChanges;
|
||||
opened.OnTagChanged = UpdateTags;
|
||||
opened.OnStashChanged = UpdateStashes;
|
||||
opened.OnBranchChanged = () => UpdateBranches(false);
|
||||
opened.OnCommitsChanged = UpdateHistories;
|
||||
opened.OnNavigateCommit = commit => {
|
||||
Dispatcher.Invoke(() => {
|
||||
workspace.SelectedItem = historiesSwitch;
|
||||
histories.Navigate(commit);
|
||||
});
|
||||
};
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
repo = opened;
|
||||
repoName.Content = repo.Name;
|
||||
histories.Repo = opened;
|
||||
commits.Repo = opened;
|
||||
|
||||
UpdateBranches();
|
||||
UpdateHistories();
|
||||
UpdateLocalChanges();
|
||||
UpdateStashes();
|
||||
UpdateTags();
|
||||
}
|
||||
|
||||
#region DATA_UPDATE
|
||||
private void UpdateHistories() {
|
||||
Dispatcher.Invoke(() => {
|
||||
histories.SetLoadingEnabled(true);
|
||||
});
|
||||
|
||||
Task.Run(() => {
|
||||
var args = "-5000 ";
|
||||
if (repo.LogFilters.Count > 0) {
|
||||
args = args + string.Join(" ", repo.LogFilters);
|
||||
} else {
|
||||
args = args + "--branches --remotes --tags";
|
||||
}
|
||||
|
||||
var commits = repo.Commits(args);
|
||||
histories.SetCommits(commits);
|
||||
});
|
||||
}
|
||||
|
||||
private void UpdateLocalChanges() {
|
||||
Task.Run(() => {
|
||||
var changes = repo.LocalChanges();
|
||||
var conflicts = commits.SetData(changes);
|
||||
|
||||
Dispatcher.Invoke(() => {
|
||||
localChangesBadge.Visibility = changes.Count == 0 ? Visibility.Collapsed : Visibility.Visible;
|
||||
localChangesCount.Content = changes.Count;
|
||||
btnContinue.Visibility = conflicts ? Visibility.Collapsed : Visibility.Visible;
|
||||
DetectMergeState();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void UpdateStashes() {
|
||||
Task.Run(() => {
|
||||
var data = repo.Stashes();
|
||||
Dispatcher.Invoke(() => {
|
||||
stashBadge.Visibility = data.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
|
||||
stashCount.Content = data.Count;
|
||||
stashes.SetData(repo, data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void BackupBranchNodeExpandState(Dictionary<string, bool> states, List<BranchNode> nodes, string prefix) {
|
||||
foreach (var node in nodes) {
|
||||
var path = prefix + "/" + node.Name;
|
||||
states.Add(path, node.IsExpanded);
|
||||
BackupBranchNodeExpandState(states, node.Children, path);
|
||||
}
|
||||
}
|
||||
|
||||
private void MakeBranchNode(Git.Branch branch, List<BranchNode> collection, Dictionary<string, BranchNode> folders, Dictionary<string, bool> expandStates, string prefix) {
|
||||
var subs = branch.Name.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (!branch.IsLocal) {
|
||||
if (subs.Length < 2) return;
|
||||
subs = subs.Skip(1).ToArray();
|
||||
}
|
||||
|
||||
branch.IsFiltered = repo.LogFilters.Contains(branch.FullName);
|
||||
|
||||
if (subs.Length == 1) {
|
||||
var node = new BranchNode() {
|
||||
Name = subs[0],
|
||||
Branch = branch,
|
||||
Children = new List<BranchNode>(),
|
||||
};
|
||||
collection.Add(node);
|
||||
} else {
|
||||
BranchNode lastFolder = null;
|
||||
string path = prefix;
|
||||
for (int i = 0; i < subs.Length - 1; i++) {
|
||||
path = path + "/" + subs[i];
|
||||
if (folders.ContainsKey(path)) {
|
||||
lastFolder = folders[path];
|
||||
} else if (lastFolder == null) {
|
||||
lastFolder = new BranchNode() {
|
||||
Name = subs[i],
|
||||
IsExpanded = expandStates.ContainsKey(path) ? expandStates[path] : false,
|
||||
Children = new List<BranchNode>(),
|
||||
};
|
||||
collection.Add(lastFolder);
|
||||
folders.Add(path, lastFolder);
|
||||
} else {
|
||||
var folder = new BranchNode() {
|
||||
Name = subs[i],
|
||||
IsExpanded = expandStates.ContainsKey(path) ? expandStates[path] : false,
|
||||
Children = new List<BranchNode>(),
|
||||
};
|
||||
lastFolder.Children.Add(folder);
|
||||
folders.Add(path, folder);
|
||||
lastFolder = folder;
|
||||
}
|
||||
}
|
||||
|
||||
BranchNode node = new BranchNode();
|
||||
node.Name = subs[subs.Length - 1];
|
||||
node.Branch = branch;
|
||||
node.Children = new List<BranchNode>();
|
||||
lastFolder.Children.Add(node);
|
||||
}
|
||||
}
|
||||
|
||||
private void SortBranchNodes(List<BranchNode> collection) {
|
||||
collection.Sort((l, r) => {
|
||||
if (l.Branch != null) {
|
||||
return r.Branch != null ? l.Branch.Name.CompareTo(r.Branch.Name) : -1;
|
||||
} else {
|
||||
return r.Branch == null ? l.Name.CompareTo(r.Name) : 1;
|
||||
}
|
||||
});
|
||||
|
||||
foreach (var sub in collection) {
|
||||
if (sub.Children.Count > 0) SortBranchNodes(sub.Children);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateBranches(bool force = true) {
|
||||
Task.Run(() => {
|
||||
var branches = repo.Branches(force);
|
||||
var localBranchNodes = new List<BranchNode>();
|
||||
var remoteNodes = new List<RemoteNode>();
|
||||
var remoteMap = new Dictionary<string, RemoteNode>();
|
||||
var folders = new Dictionary<string, BranchNode>();
|
||||
var states = new Dictionary<string, bool>();
|
||||
|
||||
BackupBranchNodeExpandState(states, cachedLocalBranches, "locals");
|
||||
foreach (var r in cachedRemotes) {
|
||||
var prefix = $"remotes/{r.Name}";
|
||||
states.Add(prefix, r.IsExpanded);
|
||||
BackupBranchNodeExpandState(states, r.Children, prefix);
|
||||
}
|
||||
|
||||
foreach (var b in branches) {
|
||||
if (b.IsLocal) {
|
||||
MakeBranchNode(b, localBranchNodes, folders, states, "locals");
|
||||
} else {
|
||||
RemoteNode remote = null;
|
||||
|
||||
if (!remoteMap.ContainsKey(b.Remote)) {
|
||||
var key = "remotes/" + b.Remote;
|
||||
remote = new RemoteNode() {
|
||||
Name = b.Remote,
|
||||
IsExpanded = states.ContainsKey(key) ? states[key] : false,
|
||||
Children = new List<BranchNode>(),
|
||||
};
|
||||
remoteNodes.Add(remote);
|
||||
remoteMap.Add(b.Remote, remote);
|
||||
} else {
|
||||
remote = remoteMap[b.Remote];
|
||||
}
|
||||
|
||||
MakeBranchNode(b, remote.Children, folders, states, "remotes");
|
||||
}
|
||||
}
|
||||
|
||||
SortBranchNodes(localBranchNodes);
|
||||
foreach (var r in remoteNodes) SortBranchNodes(r.Children);
|
||||
|
||||
cachedLocalBranches = localBranchNodes;
|
||||
cachedRemotes = remoteNodes;
|
||||
|
||||
Dispatcher.Invoke(() => {
|
||||
localBranchTree.ItemsSource = localBranchNodes;
|
||||
remoteBranchTree.ItemsSource = remoteNodes;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void UpdateTags() {
|
||||
Task.Run(() => {
|
||||
var tags = repo.Tags(true);
|
||||
foreach (var t in tags) t.IsFiltered = repo.LogFilters.Contains(t.Name);
|
||||
|
||||
Dispatcher.Invoke(() => {
|
||||
tagCount.Content = $"TAGS ({tags.Count})";
|
||||
tagList.ItemsSource = tags;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void Cleanup(object sender, RoutedEventArgs e) {
|
||||
localBranchTree.ItemsSource = null;
|
||||
remoteBranchTree.ItemsSource = null;
|
||||
tagList.ItemsSource = null;
|
||||
cachedLocalBranches.Clear();
|
||||
cachedRemotes.Clear();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region TOOLBAR
|
||||
private void Close(object sender, RoutedEventArgs e) {
|
||||
if (PopupManager.IsLocked()) return;
|
||||
PopupManager.Close();
|
||||
|
||||
cachedLocalBranches.Clear();
|
||||
cachedRemotes.Clear();
|
||||
|
||||
repo.Close();
|
||||
}
|
||||
|
||||
private void OpenFetch(object sender, RoutedEventArgs e) {
|
||||
Fetch.Show(repo);
|
||||
}
|
||||
|
||||
private void OpenPull(object sender, RoutedEventArgs e) {
|
||||
Pull.Show(repo);
|
||||
}
|
||||
|
||||
private void OpenPush(object sender, RoutedEventArgs e) {
|
||||
Push.Show(repo);
|
||||
}
|
||||
|
||||
private void OpenStash(object sender, RoutedEventArgs e) {
|
||||
Stash.Show(repo, new List<string>());
|
||||
}
|
||||
|
||||
private void OpenApply(object sender, RoutedEventArgs e) {
|
||||
Apply.Show(repo);
|
||||
}
|
||||
|
||||
private void OpenSearch(object sender, RoutedEventArgs e) {
|
||||
workspace.SelectedItem = historiesSwitch;
|
||||
if (histories.searchBar.Margin.Top == 0) {
|
||||
histories.HideSearchBar();
|
||||
} else {
|
||||
histories.OpenSearchBar();
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenExplorer(object sender, RoutedEventArgs e) {
|
||||
Process.Start(repo.Path);
|
||||
}
|
||||
|
||||
private void OpenTerminal(object sender, RoutedEventArgs e) {
|
||||
var bash = Path.Combine(App.Preference.GitExecutable, "..", "bash.exe");
|
||||
if (!File.Exists(bash)) {
|
||||
App.RaiseError("Can NOT locate bash.exe. Make sure bash.exe exists under the same folder with git.exe");
|
||||
return;
|
||||
}
|
||||
|
||||
var start = new ProcessStartInfo();
|
||||
start.WorkingDirectory = repo.Path;
|
||||
start.FileName = bash;
|
||||
Process.Start(start);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region HOT_KEYS
|
||||
public void OpenSearchBar(object sender, ExecutedRoutedEventArgs e) {
|
||||
workspace.SelectedItem = historiesSwitch;
|
||||
histories.OpenSearchBar();
|
||||
}
|
||||
|
||||
public void HideSearchBar(object sender, ExecutedRoutedEventArgs e) {
|
||||
if (histories.Visibility == Visibility.Visible) {
|
||||
histories.HideSearchBar();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region MERGE_ABORTS
|
||||
public void DetectMergeState() {
|
||||
var gitDir = Path.Combine(repo.Path, ".git");
|
||||
var cherryPickMerge = Path.Combine(gitDir, "CHERRY_PICK_HEAD");
|
||||
var rebaseMerge = Path.Combine(gitDir, "REBASE_HEAD");
|
||||
var revertMerge = Path.Combine(gitDir, "REVERT_HEAD");
|
||||
var otherMerge = Path.Combine(gitDir, "MERGE_HEAD");
|
||||
|
||||
if (File.Exists(cherryPickMerge)) {
|
||||
abortCommand = "cherry-pick";
|
||||
txtMergeProcessing.Content = "Cherry-Pick merge request detected! Press 'Abort' to restore original HEAD";
|
||||
} else if (File.Exists(rebaseMerge)) {
|
||||
abortCommand = "rebase";
|
||||
txtMergeProcessing.Content = "Rebase merge request detected! Press 'Abort' to restore original HEAD";
|
||||
} else if (File.Exists(revertMerge)) {
|
||||
abortCommand = "revert";
|
||||
txtMergeProcessing.Content = "Revert merge request detected! Press 'Abort' to restore original HEAD";
|
||||
} else if (File.Exists(otherMerge)) {
|
||||
abortCommand = "merge";
|
||||
txtMergeProcessing.Content = "Merge request detected! Press 'Abort' to restore original HEAD";
|
||||
} else {
|
||||
abortCommand = null;
|
||||
}
|
||||
|
||||
if (abortCommand != null) {
|
||||
abortPanel.Visibility = Visibility.Visible;
|
||||
if (commits.Visibility == Visibility.Visible) {
|
||||
btnResolve.Visibility = Visibility.Collapsed;
|
||||
} else {
|
||||
btnResolve.Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
commits.LoadMergeMessage();
|
||||
} else {
|
||||
abortPanel.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
private void Resolve(object sender, RoutedEventArgs e) {
|
||||
workspace.SelectedItem = workingCopySwitch;
|
||||
}
|
||||
|
||||
private async void Continue(object sender, RoutedEventArgs e) {
|
||||
if (abortCommand == null) return;
|
||||
|
||||
await Task.Run(() => {
|
||||
repo.SetWatcherEnabled(false);
|
||||
var errs = repo.RunCommand($"-c core.editor=true {abortCommand} --continue", null);
|
||||
repo.AssertCommand(errs);
|
||||
});
|
||||
|
||||
commits.ClearMessage();
|
||||
}
|
||||
|
||||
private async void Abort(object sender, RoutedEventArgs e) {
|
||||
if (abortCommand == null) return;
|
||||
|
||||
await Task.Run(() => {
|
||||
repo.SetWatcherEnabled(false);
|
||||
var errs = repo.RunCommand($"{abortCommand} --abort", null);
|
||||
repo.AssertCommand(errs);
|
||||
});
|
||||
|
||||
commits.ClearMessage();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region WORKSPACE
|
||||
private void SwitchWorkingCopy(object sender, RoutedEventArgs e) {
|
||||
if (commits == null || histories == null || stashes == null) return;
|
||||
|
||||
commits.Visibility = Visibility.Visible;
|
||||
histories.Visibility = Visibility.Collapsed;
|
||||
stashes.Visibility = Visibility.Collapsed;
|
||||
|
||||
if (abortPanel.Visibility == Visibility.Visible) {
|
||||
btnResolve.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
private void SwitchHistories(object sender, RoutedEventArgs e) {
|
||||
if (commits == null || histories == null || stashes == null) return;
|
||||
|
||||
commits.Visibility = Visibility.Collapsed;
|
||||
histories.Visibility = Visibility.Visible;
|
||||
stashes.Visibility = Visibility.Collapsed;
|
||||
|
||||
if (abortPanel.Visibility == Visibility.Visible) {
|
||||
btnResolve.Visibility = Visibility.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
private void SwitchStashes(object sender, RoutedEventArgs e) {
|
||||
if (commits == null || histories == null || stashes == null) return;
|
||||
|
||||
commits.Visibility = Visibility.Collapsed;
|
||||
histories.Visibility = Visibility.Collapsed;
|
||||
stashes.Visibility = Visibility.Visible;
|
||||
|
||||
if (abortPanel.Visibility == Visibility.Visible) {
|
||||
btnResolve.Visibility = Visibility.Visible;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region LOCAL_BRANCHES
|
||||
private void OpenNewBranch(object sender, RoutedEventArgs e) {
|
||||
CreateBranch.Show(repo);
|
||||
}
|
||||
|
||||
private void OpenGitFlow(object sender, RoutedEventArgs ev) {
|
||||
var button = sender as Button;
|
||||
if (button.ContextMenu == null) {
|
||||
button.ContextMenu = new ContextMenu();
|
||||
button.ContextMenu.PlacementTarget = button;
|
||||
button.ContextMenu.Placement = PlacementMode.Bottom;
|
||||
button.ContextMenu.StaysOpen = false;
|
||||
button.ContextMenu.Focusable = true;
|
||||
} else {
|
||||
button.ContextMenu.Items.Clear();
|
||||
}
|
||||
|
||||
if (repo.IsGitFlowEnabled()) {
|
||||
var startFeature = new MenuItem();
|
||||
startFeature.Header = "Start Feature ...";
|
||||
startFeature.Click += (o, e) => {
|
||||
GitFlowStartBranch.Show(repo, Git.Branch.Type.Feature);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var startRelease = new MenuItem();
|
||||
startRelease.Header = "Start Release ...";
|
||||
startRelease.Click += (o, e) => {
|
||||
GitFlowStartBranch.Show(repo, Git.Branch.Type.Release);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var startHotfix = new MenuItem();
|
||||
startHotfix.Header = "Start Hotfix ...";
|
||||
startHotfix.Click += (o, e) => {
|
||||
GitFlowStartBranch.Show(repo, Git.Branch.Type.Hotfix);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
button.ContextMenu.Items.Add(startFeature);
|
||||
button.ContextMenu.Items.Add(startRelease);
|
||||
button.ContextMenu.Items.Add(startHotfix);
|
||||
} else {
|
||||
var init = new MenuItem();
|
||||
init.Header = "Initialize Git-Flow";
|
||||
init.Click += (o, e) => {
|
||||
GitFlowSetup.Show(repo);
|
||||
e.Handled = true;
|
||||
};
|
||||
button.ContextMenu.Items.Add(init);
|
||||
}
|
||||
|
||||
button.ContextMenu.IsOpen = true;
|
||||
ev.Handled = true;
|
||||
}
|
||||
|
||||
private void LocalBranchSelected(object sender, RoutedPropertyChangedEventArgs<object> e) {
|
||||
var node = e.NewValue as BranchNode;
|
||||
if (node == null || node.Branch == null) return;
|
||||
repo.OnNavigateCommit?.Invoke(node.Branch.Head);
|
||||
}
|
||||
|
||||
private void LocalBranchMouseDoubleClick(object sender, MouseButtonEventArgs e) {
|
||||
var node = (sender as TreeViewItem).DataContext as BranchNode;
|
||||
if (node == null || node.Branch == null) return;
|
||||
Task.Run(() => repo.Checkout(node.Branch.Name));
|
||||
}
|
||||
|
||||
private void LocalBranchContextMenuOpening(object sender, ContextMenuEventArgs ev) {
|
||||
var node = (sender as TreeViewItem).DataContext as BranchNode;
|
||||
if (node == null || node.Branch == null) return;
|
||||
|
||||
var menu = new ContextMenu();
|
||||
var branch = node.Branch;
|
||||
|
||||
var push = new MenuItem();
|
||||
push.Header = $"Push '{branch.Name}'";
|
||||
push.Click += (o, e) => {
|
||||
Push.Show(repo, branch);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
if (branch.IsCurrent) {
|
||||
var discard = new MenuItem();
|
||||
discard.Header = "Discard all changes";
|
||||
discard.Click += (o, e) => {
|
||||
Discard.Show(repo, null);
|
||||
e.Handled = true;
|
||||
};
|
||||
menu.Items.Add(discard);
|
||||
menu.Items.Add(new Separator());
|
||||
|
||||
if (!string.IsNullOrEmpty(branch.Upstream)) {
|
||||
var upstream = branch.Upstream.Substring(13);
|
||||
var fastForward = new MenuItem();
|
||||
fastForward.Header = $"Fast-Forward to '{upstream}'";
|
||||
fastForward.Click += (o, e) => {
|
||||
Merge.StartDirectly(repo, upstream, branch.Name);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var pull = new MenuItem();
|
||||
pull.Header = $"Pull '{upstream}'";
|
||||
pull.Click += (o, e) => {
|
||||
Pull.Show(repo);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
menu.Items.Add(fastForward);
|
||||
menu.Items.Add(pull);
|
||||
}
|
||||
|
||||
menu.Items.Add(push);
|
||||
} else {
|
||||
var current = repo.CurrentBranch();
|
||||
|
||||
var checkout = new MenuItem();
|
||||
checkout.Header = $"Checkout {branch.Name}";
|
||||
checkout.Click += (o, e) => {
|
||||
Task.Run(() => repo.Checkout(node.Branch.Name));
|
||||
e.Handled = true;
|
||||
};
|
||||
menu.Items.Add(checkout);
|
||||
menu.Items.Add(new Separator());
|
||||
menu.Items.Add(push);
|
||||
|
||||
var merge = new MenuItem();
|
||||
merge.Header = $"Merge '{branch.Name}' into '{current.Name}'";
|
||||
merge.Click += (o, e) => {
|
||||
Merge.Show(repo, branch.Name, current.Name);
|
||||
e.Handled = true;
|
||||
};
|
||||
menu.Items.Add(merge);
|
||||
|
||||
var rebase = new MenuItem();
|
||||
rebase.Header = $"Rebase '{current.Name}' on '{branch.Name}'";
|
||||
rebase.Click += (o, e) => {
|
||||
Rebase.Show(repo, branch);
|
||||
e.Handled = true;
|
||||
};
|
||||
menu.Items.Add(rebase);
|
||||
}
|
||||
|
||||
if (branch.Kind != Git.Branch.Type.Normal) {
|
||||
menu.Items.Add(new Separator());
|
||||
|
||||
var icon = new System.Windows.Shapes.Path();
|
||||
icon.Style = FindResource("Style.Icon") as Style;
|
||||
icon.Data = FindResource("Icon.Flow") as Geometry;
|
||||
icon.Width = 10;
|
||||
|
||||
var finish = new MenuItem();
|
||||
finish.Header = $"Git Flow - Finish '{branch.Name}'";
|
||||
finish.Icon = icon;
|
||||
finish.Click += (o, e) => {
|
||||
GitFlowFinishBranch.Show(repo, branch);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
menu.Items.Add(finish);
|
||||
}
|
||||
|
||||
var rename = new MenuItem();
|
||||
rename.Header = $"Rename '{branch.Name}'";
|
||||
rename.Click += (o, e) => {
|
||||
RenameBranch.Show(repo, branch);
|
||||
e.Handled = true;
|
||||
};
|
||||
menu.Items.Add(new Separator());
|
||||
menu.Items.Add(rename);
|
||||
|
||||
var delete = new MenuItem();
|
||||
delete.Header = $"Delete '{branch.Name}'";
|
||||
delete.IsEnabled = !branch.IsCurrent;
|
||||
delete.Click += (o, e) => {
|
||||
DeleteBranch.Show(repo, branch);
|
||||
e.Handled = true;
|
||||
};
|
||||
menu.Items.Add(delete);
|
||||
menu.Items.Add(new Separator());
|
||||
|
||||
var createBranch = new MenuItem();
|
||||
createBranch.Header = "Create Branch";
|
||||
createBranch.Click += (o, e) => {
|
||||
CreateBranch.Show(repo, branch);
|
||||
e.Handled = true;
|
||||
};
|
||||
menu.Items.Add(createBranch);
|
||||
|
||||
var createTag = new MenuItem();
|
||||
createTag.Header = "Create Tag";
|
||||
createTag.Click += (o, e) => {
|
||||
CreateTag.Show(repo, branch);
|
||||
e.Handled = true;
|
||||
};
|
||||
menu.Items.Add(createTag);
|
||||
menu.Items.Add(new Separator());
|
||||
|
||||
var copy = new MenuItem();
|
||||
copy.Header = "Copy Branch Name";
|
||||
copy.Click += (o, e) => {
|
||||
Clipboard.SetText(branch.Name);
|
||||
e.Handled = true;
|
||||
};
|
||||
menu.Items.Add(copy);
|
||||
|
||||
menu.IsOpen = true;
|
||||
ev.Handled = true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region REMOTE_BRANCHES
|
||||
private void OpenRemote(object sender, RoutedEventArgs e) {
|
||||
Remote.Show(repo);
|
||||
}
|
||||
|
||||
private void OpenRemoteContextMenu(RemoteNode node) {
|
||||
var fetch = new MenuItem();
|
||||
fetch.Header = $"Fetch '{node.Name}'";
|
||||
fetch.Click += (o, e) => {
|
||||
Fetch.Show(repo, node.Name);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var edit = new MenuItem();
|
||||
edit.Header = $"Edit '{node.Name}'";
|
||||
edit.Click += (o, e) => {
|
||||
var remotes = repo.Remotes();
|
||||
var found = remotes.Find(r => r.Name == node.Name);
|
||||
if (found != null) Remote.Show(repo, found);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var delete = new MenuItem();
|
||||
delete.Header = $"Delete '{node.Name}'";
|
||||
delete.Click += (o, e) => {
|
||||
DeleteRemote.Show(repo, node.Name);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var copy = new MenuItem();
|
||||
copy.Header = "Copy Remote URL";
|
||||
copy.Click += (o, e) => {
|
||||
var remotes = repo.Remotes();
|
||||
var found = remotes.Find(r => r.Name == node.Name);
|
||||
if (found != null) Clipboard.SetText(found.URL);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var menu = new ContextMenu();
|
||||
menu.Items.Add(fetch);
|
||||
menu.Items.Add(new Separator());
|
||||
menu.Items.Add(edit);
|
||||
menu.Items.Add(delete);
|
||||
menu.Items.Add(new Separator());
|
||||
menu.Items.Add(copy);
|
||||
menu.IsOpen = true;
|
||||
}
|
||||
|
||||
private void OpenRemoteBranchContextMenu(BranchNode node) {
|
||||
var branch = node.Branch;
|
||||
var current = repo.CurrentBranch();
|
||||
if (current == null) return;
|
||||
|
||||
var checkout = new MenuItem();
|
||||
checkout.Header = $"Checkout '{branch.Name}'";
|
||||
checkout.Click += (o, e) => {
|
||||
var branches = repo.Branches();
|
||||
var tracked = null as Git.Branch;
|
||||
var upstream = $"refs/remotes/{branch.Name}";
|
||||
|
||||
foreach (var b in branches) {
|
||||
if (b.IsLocal && b.Upstream == upstream) {
|
||||
tracked = b;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tracked == null) {
|
||||
CreateBranch.Show(repo, branch);
|
||||
} else if (!tracked.IsCurrent) {
|
||||
Task.Run(() => repo.Checkout(tracked.Name));
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var pull = new MenuItem();
|
||||
pull.Header = $"Pull '{branch.Name}' into '{current.Name}'";
|
||||
pull.Click += (o, e) => {
|
||||
Pull.Show(repo, branch.Name);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var merge = new MenuItem();
|
||||
merge.Header = $"Merge '{branch.Name}' into '{current.Name}'";
|
||||
merge.Click += (o, e) => {
|
||||
Merge.Show(repo, branch.Name, current.Name);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var rebase = new MenuItem();
|
||||
rebase.Header = $"Rebase '{current.Name}' on '{branch.Name}'";
|
||||
rebase.Click += (o, e) => {
|
||||
Rebase.Show(repo, branch);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var delete = new MenuItem();
|
||||
delete.Header = $"Delete '{branch.Name}'";
|
||||
delete.Click += (o, e) => {
|
||||
DeleteBranch.Show(repo, branch);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var createBranch = new MenuItem();
|
||||
createBranch.Header = "Create New Branch";
|
||||
createBranch.Click += (o, e) => {
|
||||
CreateBranch.Show(repo, branch);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var createTag = new MenuItem();
|
||||
createTag.Header = "Create New Tag";
|
||||
createTag.Click += (o, e) => {
|
||||
CreateTag.Show(repo, branch);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var copy = new MenuItem();
|
||||
copy.Header = "Copy Branch Name";
|
||||
copy.Click += (o, e) => {
|
||||
Clipboard.SetText(branch.Name);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var menu = new ContextMenu();
|
||||
menu.Items.Add(checkout);
|
||||
menu.Items.Add(new Separator());
|
||||
menu.Items.Add(pull);
|
||||
menu.Items.Add(merge);
|
||||
menu.Items.Add(rebase);
|
||||
menu.Items.Add(new Separator());
|
||||
menu.Items.Add(delete);
|
||||
menu.Items.Add(new Separator());
|
||||
menu.Items.Add(createBranch);
|
||||
menu.Items.Add(createTag);
|
||||
menu.Items.Add(new Separator());
|
||||
menu.Items.Add(copy);
|
||||
menu.IsOpen = true;
|
||||
}
|
||||
|
||||
private void RemoteBranchSelected(object sender, RoutedPropertyChangedEventArgs<object> e) {
|
||||
var node = e.NewValue as BranchNode;
|
||||
if (node == null || node.Branch == null) return;
|
||||
repo.OnNavigateCommit?.Invoke(node.Branch.Head);
|
||||
}
|
||||
|
||||
private void RemoteContextMenuOpening(object sender, ContextMenuEventArgs ev) {
|
||||
var remoteNode = (sender as TreeViewItem).DataContext as RemoteNode;
|
||||
if (remoteNode != null) {
|
||||
OpenRemoteContextMenu(remoteNode);
|
||||
ev.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var branchNode = (sender as TreeViewItem).DataContext as BranchNode;
|
||||
if (branchNode != null && branchNode.Branch != null) {
|
||||
OpenRemoteBranchContextMenu(branchNode);
|
||||
ev.Handled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region TAGS
|
||||
private void OpenNewTag(object sender, RoutedEventArgs e) {
|
||||
CreateTag.Show(repo);
|
||||
}
|
||||
|
||||
private void TagLostFocus(object sender, RoutedEventArgs e) {
|
||||
(sender as ListView).UnselectAll();
|
||||
}
|
||||
|
||||
private void TagSelectionChanged(object sender, SelectionChangedEventArgs e) {
|
||||
if (e.AddedItems.Count == 1) {
|
||||
var item = e.AddedItems[0] as Git.Tag;
|
||||
repo.OnNavigateCommit?.Invoke(item.SHA);
|
||||
}
|
||||
}
|
||||
|
||||
private void TagContextMenuOpening(object sender, ContextMenuEventArgs e) {
|
||||
var tag = (sender as ListView).SelectedItem as Git.Tag;
|
||||
if (tag == null) return;
|
||||
|
||||
var createBranch = new MenuItem();
|
||||
createBranch.Header = "Create New Branch";
|
||||
createBranch.Click += (o, ev) => {
|
||||
CreateBranch.Show(repo, tag);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var pushTag = new MenuItem();
|
||||
pushTag.Header = $"Push '{tag.Name}'";
|
||||
pushTag.Click += (o, ev) => {
|
||||
PushTag.Show(repo, tag);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var deleteTag = new MenuItem();
|
||||
deleteTag.Header = $"Delete '{tag.Name}'";
|
||||
deleteTag.Click += (o, ev) => {
|
||||
DeleteTag.Show(repo, tag);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var copy = new MenuItem();
|
||||
copy.Header = "Copy Name";
|
||||
copy.Click += (o, ev) => {
|
||||
Clipboard.SetText(tag.Name);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var menu = new ContextMenu();
|
||||
menu.Items.Add(createBranch);
|
||||
menu.Items.Add(new Separator());
|
||||
menu.Items.Add(pushTag);
|
||||
menu.Items.Add(deleteTag);
|
||||
menu.Items.Add(new Separator());
|
||||
menu.Items.Add(copy);
|
||||
menu.IsOpen = true;
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region TREES
|
||||
private TreeViewItem FindTreeViewItem(ItemsControl item, BranchNode node) {
|
||||
if (item == null) return null;
|
||||
|
||||
var data = item.DataContext as BranchNode;
|
||||
if (data == node) return item as TreeViewItem;
|
||||
|
||||
for (int i = 0; i < item.Items.Count; i++) {
|
||||
var childContainer = item.ItemContainerGenerator.ContainerFromIndex(i) as ItemsControl;
|
||||
var child = FindTreeViewItem(childContainer, node);
|
||||
if (child != null) return child;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void TreeLostFocus(object sender, RoutedEventArgs e) {
|
||||
var tree = sender as TreeView;
|
||||
var remote = tree.SelectedItem as RemoteNode;
|
||||
if (remote != null) {
|
||||
var remoteItem = tree.ItemContainerGenerator.ContainerFromItem(remote) as TreeViewItem;
|
||||
if (remoteItem != null) remoteItem.IsSelected = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var node = tree.SelectedItem as BranchNode;
|
||||
if (node == null) return;
|
||||
|
||||
var item = FindTreeViewItem(tree, node);
|
||||
if (item != null) item.IsSelected = false;
|
||||
}
|
||||
|
||||
private ScrollViewer GetScrollViewer(FrameworkElement owner) {
|
||||
if (owner == null) return null;
|
||||
if (owner is ScrollViewer) return owner as ScrollViewer;
|
||||
|
||||
int n = VisualTreeHelper.GetChildrenCount(owner);
|
||||
for (int i = 0; i < n; i++) {
|
||||
var child = VisualTreeHelper.GetChild(owner, i) as FrameworkElement;
|
||||
var deep = GetScrollViewer(child);
|
||||
if (deep != null) return deep;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void TreeMouseWheel(object sender, MouseWheelEventArgs e) {
|
||||
var scroll = GetScrollViewer(sender as TreeView);
|
||||
if (scroll == null) return;
|
||||
|
||||
if (e.Delta > 0) {
|
||||
scroll.LineUp();
|
||||
} else {
|
||||
scroll.LineDown();
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region FILETER
|
||||
private void FilterChanged(object sender, RoutedEventArgs e) {
|
||||
var toggle = sender as ToggleButton;
|
||||
if (toggle == null) return;
|
||||
|
||||
if (toggle.DataContext is BranchNode) {
|
||||
var branch = (toggle.DataContext as BranchNode).Branch;
|
||||
if (branch == null) return;
|
||||
|
||||
if (toggle.IsChecked == true) {
|
||||
if (!repo.LogFilters.Contains(branch.FullName)) {
|
||||
repo.LogFilters.Add(branch.FullName);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(branch.Upstream) && !repo.LogFilters.Contains(branch.Upstream)) {
|
||||
repo.LogFilters.Add(branch.Upstream);
|
||||
UpdateBranches(false);
|
||||
}
|
||||
} else {
|
||||
repo.LogFilters.Remove(branch.FullName);
|
||||
if (!string.IsNullOrEmpty(branch.Upstream)) {
|
||||
repo.LogFilters.Remove(branch.Upstream);
|
||||
UpdateBranches(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (toggle.DataContext is Git.Tag) {
|
||||
var tag = toggle.DataContext as Git.Tag;
|
||||
|
||||
if (toggle.IsChecked == true) {
|
||||
if (!repo.LogFilters.Contains(tag.Name)) {
|
||||
repo.LogFilters.Add(tag.Name);
|
||||
}
|
||||
} else {
|
||||
repo.LogFilters.Remove(tag.Name);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateHistories();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
51
SourceGit/UI/DeleteBranch.xaml
Normal file
51
SourceGit/UI/DeleteBranch.xaml
Normal file
|
@ -0,0 +1,51 @@
|
|||
<UserControl x:Class="SourceGit.UI.DeleteBranch"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:SourceGit.UI"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="160" d:DesignWidth="500" Height="128" Width="500">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.ColumnSpan="2" FontWeight="DemiBold" FontSize="18" Content="Confirm To Delete Branch"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right" Content="Branch :"/>
|
||||
<StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal">
|
||||
<Path Width="12" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Branch}" Margin="4,0"/>
|
||||
<Label x:Name="branchName"/>
|
||||
</StackPanel>
|
||||
|
||||
<Grid Grid.Row="4" Grid.ColumnSpan="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Column="1" Click="Sure" Content="SURE" Style="{StaticResource Style.Button.AccentBordered}"/>
|
||||
<Button Grid.Column="3" Click="Cancel" Content="CANCEL" Style="{StaticResource Style.Button.Bordered}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="0" Grid.RowSpan="5" Grid.ColumnSpan="2" x:Name="status" Visibility="Collapsed" Background="{StaticResource Brush.BG1}" Opacity=".9">
|
||||
<Path x:Name="statusIcon" Width="48" Height="48" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Loading}" RenderTransformOrigin=".5,.5">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
66
SourceGit/UI/DeleteBranch.xaml.cs
Normal file
66
SourceGit/UI/DeleteBranch.xaml.cs
Normal file
|
@ -0,0 +1,66 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
/// <summary>
|
||||
/// Confirm to delete branch
|
||||
/// </summary>
|
||||
public partial class DeleteBranch : UserControl {
|
||||
private Git.Repository repo = null;
|
||||
private Git.Branch branch = null;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="opened">Opened repository.</param>
|
||||
/// <param name="target">Branch to be deleted.</param>
|
||||
public DeleteBranch(Git.Repository opened, Git.Branch target) {
|
||||
InitializeComponent();
|
||||
repo = opened;
|
||||
branch = target;
|
||||
branchName.Content = target.Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show this dialog.
|
||||
/// </summary>
|
||||
/// <param name="opened"></param>
|
||||
/// <param name="branch"></param>
|
||||
public static void Show(Git.Repository opened, Git.Branch branch) {
|
||||
PopupManager.Show(new DeleteBranch(opened, branch));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void Sure(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Lock();
|
||||
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
status.Visibility = Visibility.Visible;
|
||||
|
||||
await Task.Run(() => branch.Delete(repo));
|
||||
|
||||
status.Visibility = Visibility.Collapsed;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
PopupManager.Close(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Cancel(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Close();
|
||||
}
|
||||
}
|
||||
}
|
50
SourceGit/UI/DeleteRemote.xaml
Normal file
50
SourceGit/UI/DeleteRemote.xaml
Normal file
|
@ -0,0 +1,50 @@
|
|||
<UserControl x:Class="SourceGit.UI.DeleteRemote"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="160" d:DesignWidth="500" Height="128" Width="500">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.ColumnSpan="2" FontWeight="DemiBold" FontSize="18" Content="Confirm To Delete Remote"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right" Content="Remote :"/>
|
||||
<StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal">
|
||||
<Path Width="12" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Remote}" Margin="4,0"/>
|
||||
<Label x:Name="remoteName"/>
|
||||
</StackPanel>
|
||||
|
||||
<Grid Grid.Row="4" Grid.ColumnSpan="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Column="1" Click="Sure" Content="SURE" Style="{StaticResource Style.Button.AccentBordered}"/>
|
||||
<Button Grid.Column="3" Click="Cancel" Content="CANCEL" Style="{StaticResource Style.Button.Bordered}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="0" Grid.RowSpan="5" Grid.ColumnSpan="2" x:Name="status" Visibility="Collapsed" Background="{StaticResource Brush.BG1}" Opacity=".9">
|
||||
<Path x:Name="statusIcon" Width="48" Height="48" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Loading}" RenderTransformOrigin=".5,.5">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
67
SourceGit/UI/DeleteRemote.xaml.cs
Normal file
67
SourceGit/UI/DeleteRemote.xaml.cs
Normal file
|
@ -0,0 +1,67 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Confirm to delete a remote
|
||||
/// </summary>
|
||||
public partial class DeleteRemote : UserControl {
|
||||
private Git.Repository repo = null;
|
||||
private string remote = null;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="opened">Opened repository</param>
|
||||
/// <param name="target">Remote to be deleted</param>
|
||||
public DeleteRemote(Git.Repository opened, string target) {
|
||||
InitializeComponent();
|
||||
repo = opened;
|
||||
remote = target;
|
||||
remoteName.Content = target;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show this dialog
|
||||
/// </summary>
|
||||
/// <param name="opened"></param>
|
||||
/// <param name="remote"></param>
|
||||
public static void Show(Git.Repository opened, string remote) {
|
||||
PopupManager.Show(new DeleteRemote(opened, remote));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void Sure(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Lock();
|
||||
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
status.Visibility = Visibility.Visible;
|
||||
|
||||
await Task.Run(() => Git.Remote.Delete(repo, remote));
|
||||
|
||||
status.Visibility = Visibility.Collapsed;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
PopupManager.Close(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Cancel(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Close();
|
||||
}
|
||||
}
|
||||
}
|
53
SourceGit/UI/DeleteTag.xaml
Normal file
53
SourceGit/UI/DeleteTag.xaml
Normal file
|
@ -0,0 +1,53 @@
|
|||
<UserControl x:Class="SourceGit.UI.DeleteTag"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="160" d:DesignWidth="500" Height="160" Width="500">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.ColumnSpan="2" FontWeight="DemiBold" FontSize="18" Content="Confirm To Delete Tag"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right" Content="Tag :"/>
|
||||
<StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal">
|
||||
<Path Width="12" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Tag}" Margin="4,0"/>
|
||||
<Label x:Name="tagName"/>
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox Grid.Row="3" Grid.Column="1" x:Name="chkWithRemote" Content="Delete from remote repositories"/>
|
||||
|
||||
<Grid Grid.Row="5" Grid.ColumnSpan="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Column="1" Click="Start" Content="SURE" Style="{StaticResource Style.Button.AccentBordered}"/>
|
||||
<Button Grid.Column="3" Click="Cancel" Content="CANCEL" Style="{StaticResource Style.Button.Bordered}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="0" Grid.RowSpan="6" Grid.ColumnSpan="2" x:Name="status" Visibility="Collapsed" Background="{StaticResource Brush.BG1}" Opacity=".9">
|
||||
<Path x:Name="statusIcon" Width="48" Height="48" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Loading}" RenderTransformOrigin=".5,.5">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
69
SourceGit/UI/DeleteTag.xaml.cs
Normal file
69
SourceGit/UI/DeleteTag.xaml.cs
Normal file
|
@ -0,0 +1,69 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Delete tag dialog.
|
||||
/// </summary>
|
||||
public partial class DeleteTag : UserControl {
|
||||
private Git.Repository repo = null;
|
||||
private Git.Tag tag = null;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="repo">Opened repo</param>
|
||||
/// <param name="tag">Delete tag</param>
|
||||
public DeleteTag(Git.Repository repo, Git.Tag tag) {
|
||||
this.repo = repo;
|
||||
this.tag = tag;
|
||||
|
||||
InitializeComponent();
|
||||
tagName.Content = tag.Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display this dialog.
|
||||
/// </summary>
|
||||
/// <param name="repo"></param>
|
||||
/// <param name="tag"></param>
|
||||
public static void Show(Git.Repository repo, Git.Tag tag) {
|
||||
PopupManager.Show(new DeleteTag(repo, tag));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start request.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void Start(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Lock();
|
||||
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
status.Visibility = Visibility.Visible;
|
||||
|
||||
var push = chkWithRemote.IsChecked == true;
|
||||
await Task.Run(() => Git.Tag.Delete(repo, tag.Name, push));
|
||||
|
||||
status.Visibility = Visibility.Collapsed;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
PopupManager.Close(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Cancel(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Close();
|
||||
}
|
||||
}
|
||||
}
|
139
SourceGit/UI/DiffViewer.xaml
Normal file
139
SourceGit/UI/DiffViewer.xaml
Normal file
|
@ -0,0 +1,139 @@
|
|||
<UserControl x:Class="SourceGit.UI.DiffViewer"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
mc:Ignorable="d"
|
||||
FontFamily="Consolas">
|
||||
<Border BorderThickness="1" BorderBrush="{StaticResource Brush.Border2}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Border Grid.Row="0" BorderBrush="{StaticResource Brush.Border2}" BorderThickness="0,0,0,1">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,4">
|
||||
<StackPanel x:Name="orgFileNamePanel" Orientation="Horizontal">
|
||||
<Path Width="10" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.File}"/>
|
||||
<TextBlock x:Name="orgFileName" Margin="4,0,0,0" VerticalAlignment="Center" Foreground="{StaticResource Brush.FG}"/>
|
||||
<TextBlock Margin="8,0" VerticalAlignment="Center" Text="→" Foreground="{StaticResource Brush.FG}"/>
|
||||
</StackPanel>
|
||||
<Path Width="10" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.File}"/>
|
||||
<TextBlock x:Name="fileName" Margin="4,0" VerticalAlignment="Center" Foreground="{StaticResource Brush.FG}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Grid Grid.Row="1" ClipToBounds="True">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" MinWidth="100"/>
|
||||
<ColumnDefinition Width="2"/>
|
||||
<ColumnDefinition Width="*" MinWidth="100"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid Grid.Column="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="1"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox
|
||||
x:Name="leftLineNumber"
|
||||
Grid.Column="0"
|
||||
AcceptsReturn="True"
|
||||
AcceptsTab="True"
|
||||
BorderThickness="0"
|
||||
Background="Transparent"
|
||||
IsReadOnly="True"
|
||||
Margin="4,0,4,0"
|
||||
FontSize="13"
|
||||
HorizontalContentAlignment="Right"
|
||||
VerticalAlignment="Stretch"/>
|
||||
|
||||
<Rectangle Grid.Column="1" Width="1" Fill="{StaticResource Brush.Border2}"/>
|
||||
|
||||
<RichTextBox
|
||||
x:Name="leftText"
|
||||
Grid.Column="2"
|
||||
AcceptsReturn="True"
|
||||
AcceptsTab="True"
|
||||
IsReadOnly="True"
|
||||
BorderThickness="0"
|
||||
Background="Transparent"
|
||||
Foreground="{StaticResource Brush.FG}"
|
||||
Height="Auto"
|
||||
FontSize="13"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
RenderOptions.ClearTypeHint="Enabled"
|
||||
ScrollViewer.ScrollChanged="OnViewerScroll"
|
||||
PreviewMouseWheel="OnViewerMouseWheel"
|
||||
SizeChanged="LeftSizeChanged"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<RichTextBox.Document>
|
||||
<FlowDocument PageWidth="0"/>
|
||||
</RichTextBox.Document>
|
||||
</RichTextBox>
|
||||
</Grid>
|
||||
|
||||
<GridSplitter Grid.Column="1" Width="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="{StaticResource Brush.Border2}"/>
|
||||
|
||||
<Grid Grid.Column="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="1"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox
|
||||
x:Name="rightLineNumber"
|
||||
Grid.Column="0"
|
||||
AcceptsReturn="True"
|
||||
AcceptsTab="True"
|
||||
IsReadOnly="True"
|
||||
BorderThickness="0"
|
||||
Background="Transparent"
|
||||
Margin="4,0,4,0"
|
||||
FontSize="13"
|
||||
HorizontalContentAlignment="Right"
|
||||
VerticalAlignment="Stretch"/>
|
||||
|
||||
<Rectangle Grid.Column="1" Width="1" Fill="{StaticResource Brush.Border2}"/>
|
||||
|
||||
<RichTextBox
|
||||
x:Name="rightText"
|
||||
Grid.Column="2"
|
||||
AcceptsReturn="True"
|
||||
AcceptsTab="True"
|
||||
IsReadOnly="True"
|
||||
BorderThickness="0"
|
||||
Background="Transparent"
|
||||
Foreground="{StaticResource Brush.FG}"
|
||||
Height="Auto"
|
||||
FontSize="13"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
RenderOptions.ClearTypeHint="Enabled"
|
||||
ScrollViewer.ScrollChanged="OnViewerScroll"
|
||||
PreviewMouseWheel="OnViewerMouseWheel"
|
||||
SizeChanged="RightSizeChanged"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<RichTextBox.Document>
|
||||
<FlowDocument PageWidth="0"/>
|
||||
</RichTextBox.Document>
|
||||
</RichTextBox>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Border x:Name="mask" Grid.RowSpan="2" Background="{StaticResource Brush.BG3}" Visibility="Collapsed">
|
||||
<StackPanel Orientation="Vertical" VerticalAlignment="Center" Opacity=".2">
|
||||
<Path Width="64" Height="64" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Diff}"/>
|
||||
<Label Margin="0,8,0,0" Content="SELECT FILE TO VIEW CHANGES" FontSize="18" FontWeight="UltraBold" HorizontalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
</UserControl>
|
294
SourceGit/UI/DiffViewer.xaml.cs
Normal file
294
SourceGit/UI/DiffViewer.xaml.cs
Normal file
|
@ -0,0 +1,294 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Viewer for git diff
|
||||
/// </summary>
|
||||
public partial class DiffViewer : UserControl {
|
||||
private double minWidth = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Line mode.
|
||||
/// </summary>
|
||||
public enum LineMode {
|
||||
Normal,
|
||||
Indicator,
|
||||
Empty,
|
||||
Added,
|
||||
Deleted,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public DiffViewer() {
|
||||
InitializeComponent();
|
||||
Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="lines"></param>
|
||||
/// <param name="file"></param>
|
||||
/// <param name="orgFile"></param>
|
||||
public void SetData(List<string> lines, string file, string orgFile = null) {
|
||||
minWidth = Math.Max(leftText.ActualWidth, rightText.ActualWidth) - 16;
|
||||
|
||||
fileName.Text = file;
|
||||
if (!string.IsNullOrEmpty(orgFile)) {
|
||||
orgFileNamePanel.Visibility = Visibility.Visible;
|
||||
orgFileName.Text = orgFile;
|
||||
} else {
|
||||
orgFileNamePanel.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
leftText.Document.Blocks.Clear();
|
||||
rightText.Document.Blocks.Clear();
|
||||
|
||||
leftLineNumber.Text = "";
|
||||
rightLineNumber.Text = "";
|
||||
|
||||
Regex regex = new Regex(@"^@@ \-(\d+),?\d* \+(\d+),?\d* @@", RegexOptions.None);
|
||||
bool started = false;
|
||||
|
||||
List<Paragraph> leftData = new List<Paragraph>();
|
||||
List<Paragraph> rightData = new List<Paragraph>();
|
||||
List<string> leftNumbers = new List<string>();
|
||||
List<string> rightNumbers = new List<string>();
|
||||
|
||||
int leftLine = 0;
|
||||
int rightLine = 0;
|
||||
bool bLastLeft = true;
|
||||
|
||||
foreach (var line in lines) {
|
||||
if (!started) {
|
||||
var match = regex.Match(line);
|
||||
if (!match.Success) continue;
|
||||
|
||||
MakeParagraph(leftData, line, LineMode.Indicator);
|
||||
MakeParagraph(rightData, line, LineMode.Indicator);
|
||||
leftNumbers.Add("");
|
||||
rightNumbers.Add("");
|
||||
|
||||
leftLine = int.Parse(match.Groups[1].Value);
|
||||
rightLine = int.Parse(match.Groups[2].Value);
|
||||
started = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line[0] == '-') {
|
||||
MakeParagraph(leftData, line.Substring(1), LineMode.Deleted);
|
||||
leftNumbers.Add(leftLine.ToString());
|
||||
leftLine++;
|
||||
bLastLeft = true;
|
||||
} else if (line[0] == '+') {
|
||||
MakeParagraph(rightData, line.Substring(1), LineMode.Added);
|
||||
rightNumbers.Add(rightLine.ToString());
|
||||
rightLine++;
|
||||
bLastLeft = false;
|
||||
} else if (line[0] == '\\') {
|
||||
if (bLastLeft) {
|
||||
MakeParagraph(leftData, line.Substring(1), LineMode.Indicator);
|
||||
leftNumbers.Add("");
|
||||
} else {
|
||||
MakeParagraph(rightData, line.Substring(1), LineMode.Indicator);
|
||||
rightNumbers.Add("");
|
||||
}
|
||||
} else {
|
||||
FitBothSide(leftData, leftNumbers, rightData, rightNumbers);
|
||||
bLastLeft = true;
|
||||
|
||||
var match = regex.Match(line);
|
||||
if (match.Success) {
|
||||
MakeParagraph(leftData, line, LineMode.Indicator);
|
||||
MakeParagraph(rightData, line, LineMode.Indicator);
|
||||
leftNumbers.Add("");
|
||||
rightNumbers.Add("");
|
||||
|
||||
leftLine = int.Parse(match.Groups[1].Value);
|
||||
rightLine = int.Parse(match.Groups[2].Value);
|
||||
} else {
|
||||
var data = line.Substring(1);
|
||||
MakeParagraph(leftData, data, LineMode.Normal);
|
||||
MakeParagraph(rightData, data, LineMode.Normal);
|
||||
leftNumbers.Add(leftLine.ToString());
|
||||
rightNumbers.Add(rightLine.ToString());
|
||||
leftLine++;
|
||||
rightLine++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FitBothSide(leftData, leftNumbers, rightData, rightNumbers);
|
||||
|
||||
if (leftData.Count == 0) {
|
||||
MakeParagraph(leftData, "NOT SUPPORTED OR NO DATA", LineMode.Indicator);
|
||||
MakeParagraph(rightData, "NOT SUPPORTED OR NO DATA", LineMode.Indicator);
|
||||
leftNumbers.Add("");
|
||||
rightNumbers.Add("");
|
||||
}
|
||||
|
||||
leftLineNumber.Text = string.Join("\n", leftNumbers);
|
||||
rightLineNumber.Text = string.Join("\n", rightNumbers);
|
||||
leftText.Document.PageWidth = minWidth + 16;
|
||||
rightText.Document.PageWidth = minWidth + 16;
|
||||
leftText.Document.Blocks.AddRange(leftData);
|
||||
rightText.Document.Blocks.AddRange(rightData);
|
||||
leftText.ScrollToHome();
|
||||
|
||||
mask.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset data.
|
||||
/// </summary>
|
||||
public void Reset() {
|
||||
mask.Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make paragraph.
|
||||
/// </summary>
|
||||
/// <param name="collection"></param>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="mode"></param>
|
||||
private void MakeParagraph(List<Paragraph> collection, string content, LineMode mode) {
|
||||
Paragraph p = new Paragraph(new Run(content));
|
||||
p.Margin = new Thickness(0);
|
||||
p.Padding = new Thickness();
|
||||
p.LineHeight = 1;
|
||||
p.Background = Brushes.Transparent;
|
||||
p.Foreground = FindResource("Brush.FG") as SolidColorBrush;
|
||||
p.FontStyle = FontStyles.Normal;
|
||||
|
||||
switch (mode) {
|
||||
case LineMode.Normal:
|
||||
break;
|
||||
case LineMode.Indicator:
|
||||
p.Foreground = Brushes.Gray;
|
||||
p.FontStyle = FontStyles.Italic;
|
||||
break;
|
||||
case LineMode.Empty:
|
||||
p.Background = new SolidColorBrush(Color.FromArgb(40, 0, 0, 0));
|
||||
break;
|
||||
case LineMode.Added:
|
||||
p.Background = new SolidColorBrush(Color.FromArgb(60, 0, 255, 0));
|
||||
break;
|
||||
case LineMode.Deleted:
|
||||
p.Background = new SolidColorBrush(Color.FromArgb(60, 255, 0, 0));
|
||||
break;
|
||||
}
|
||||
|
||||
var formatter = new FormattedText(
|
||||
content,
|
||||
CultureInfo.CurrentUICulture,
|
||||
FlowDirection.LeftToRight,
|
||||
new Typeface(leftText.FontFamily, p.FontStyle, p.FontWeight, p.FontStretch),
|
||||
leftText.FontSize,
|
||||
Brushes.Black,
|
||||
new NumberSubstitution(),
|
||||
TextFormattingMode.Ideal);
|
||||
|
||||
if (minWidth < formatter.Width) minWidth = formatter.Width;
|
||||
collection.Add(p);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fit both side with empty lines.
|
||||
/// </summary>
|
||||
/// <param name="left"></param>
|
||||
/// <param name="leftNumbers"></param>
|
||||
/// <param name="right"></param>
|
||||
/// <param name="rightNumbers"></param>
|
||||
private void FitBothSide(List<Paragraph> left, List<string> leftNumbers, List<Paragraph> right, List<string> rightNumbers) {
|
||||
int leftCount = left.Count;
|
||||
int rightCount = right.Count;
|
||||
int diff = 0;
|
||||
List<Paragraph> fitContent = null;
|
||||
List<string> fitNumber = null;
|
||||
|
||||
if (leftCount > rightCount) {
|
||||
diff = leftCount - rightCount;
|
||||
fitContent = right;
|
||||
fitNumber = rightNumbers;
|
||||
} else if (rightCount > leftCount) {
|
||||
diff = rightCount - leftCount;
|
||||
fitContent = left;
|
||||
fitNumber = leftNumbers;
|
||||
}
|
||||
|
||||
for (int i = 0; i < diff; i++) {
|
||||
MakeParagraph(fitContent, "", LineMode.Empty);
|
||||
fitNumber.Add("");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sync scroll both sides.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void OnViewerScroll(object sender, ScrollChangedEventArgs e) {
|
||||
if (e.VerticalChange != 0) {
|
||||
if (leftText.VerticalOffset != e.VerticalOffset) {
|
||||
leftText.ScrollToVerticalOffset(e.VerticalOffset);
|
||||
}
|
||||
|
||||
if (rightText.VerticalOffset != e.VerticalOffset) {
|
||||
rightText.ScrollToVerticalOffset(e.VerticalOffset);
|
||||
}
|
||||
|
||||
leftLineNumber.Margin = new Thickness(4, -e.VerticalOffset, 4, 0);
|
||||
rightLineNumber.Margin = new Thickness(4, -e.VerticalOffset, 4, 0);
|
||||
} else {
|
||||
if (leftText.HorizontalOffset != e.HorizontalOffset) {
|
||||
leftText.ScrollToHorizontalOffset(e.HorizontalOffset);
|
||||
}
|
||||
|
||||
if (rightText.HorizontalOffset != e.HorizontalOffset) {
|
||||
rightText.ScrollToHorizontalOffset(e.HorizontalOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scroll using mouse wheel.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void OnViewerMouseWheel(object sender, MouseWheelEventArgs e) {
|
||||
var text = sender as RichTextBox;
|
||||
if (text == null) return;
|
||||
|
||||
if (e.Delta > 0) {
|
||||
text.LineUp();
|
||||
} else {
|
||||
text.LineDown();
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void LeftSizeChanged(object sender, SizeChangedEventArgs e) {
|
||||
if (leftText.Document.PageWidth < leftText.ActualWidth) {
|
||||
leftText.Document.PageWidth = leftText.ActualWidth;
|
||||
}
|
||||
}
|
||||
|
||||
private void RightSizeChanged(object sender, SizeChangedEventArgs e) {
|
||||
if (rightText.Document.PageWidth < rightText.ActualWidth) {
|
||||
rightText.Document.PageWidth = rightText.ActualWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
55
SourceGit/UI/Discard.xaml
Normal file
55
SourceGit/UI/Discard.xaml
Normal file
|
@ -0,0 +1,55 @@
|
|||
<UserControl x:Class="SourceGit.UI.Discard"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:SourceGit.UI"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="160" d:DesignWidth="500" Height="160" Width="500">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.ColumnSpan="2" FontWeight="DemiBold" FontSize="18" Content="Confirm To Discard Changes"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right" Content="Changes :"/>
|
||||
<StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal">
|
||||
<Path x:Name="icon" Width="12" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.File}" Fill="{StaticResource Brush.FG2}" Margin="4,0"/>
|
||||
<Label x:Name="txtPath"/>
|
||||
</StackPanel>
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="1" Content="You can't undo this action!!!" Foreground="{StaticResource Brush.FG2}"/>
|
||||
|
||||
<Grid Grid.Row="5" Grid.ColumnSpan="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Column="1" Click="Sure" Content="SURE" Style="{StaticResource Style.Button.AccentBordered}"/>
|
||||
<Button Grid.Column="3" Click="Cancel" Content="CANCEL" Style="{StaticResource Style.Button.Bordered}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="0" Grid.RowSpan="6" Grid.ColumnSpan="2" x:Name="status" Visibility="Collapsed" Background="{StaticResource Brush.BG1}" Opacity=".9">
|
||||
<Path x:Name="statusIcon" Width="48" Height="48" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Loading}" RenderTransformOrigin=".5,.5">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
67
SourceGit/UI/Discard.xaml.cs
Normal file
67
SourceGit/UI/Discard.xaml.cs
Normal file
|
@ -0,0 +1,67 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Confirm to discard changes dialog.
|
||||
/// </summary>
|
||||
public partial class Discard : UserControl {
|
||||
private Git.Repository repo = null;
|
||||
private List<Git.Change> changes = null;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="opened"></param>
|
||||
/// <param name="targets"></param>
|
||||
public Discard(Git.Repository opened, List<Git.Change> targets) {
|
||||
repo = opened;
|
||||
changes = targets;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
if (changes == null || changes.Count == 0) {
|
||||
txtPath.Content = "All local changes in working copy.";
|
||||
icon.Data = FindResource("Icon.Folder") as Geometry;
|
||||
} else if (changes.Count == 1) {
|
||||
txtPath.Content = changes[0].Path;
|
||||
} else {
|
||||
txtPath.Content = $"Total {changes.Count} changes ...";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show this dialog
|
||||
/// </summary>
|
||||
/// <param name="opened"></param>
|
||||
/// <param name="targets"></param>
|
||||
public static void Show(Git.Repository opened, List<Git.Change> targets) {
|
||||
PopupManager.Show(new Discard(opened, targets));
|
||||
}
|
||||
|
||||
private async void Sure(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Lock();
|
||||
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
status.Visibility = Visibility.Visible;
|
||||
|
||||
await Task.Run(() => repo.Discard(changes));
|
||||
|
||||
status.Visibility = Visibility.Collapsed;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
PopupManager.Close(true);
|
||||
}
|
||||
|
||||
private void Cancel(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Close();
|
||||
}
|
||||
}
|
||||
}
|
78
SourceGit/UI/Fetch.xaml
Normal file
78
SourceGit/UI/Fetch.xaml
Normal file
|
@ -0,0 +1,78 @@
|
|||
<UserControl x:Class="SourceGit.UI.Fetch"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:git="clr-namespace:SourceGit.Git"
|
||||
xmlns:converters="clr-namespace:SourceGit.Converters"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="192" d:DesignWidth="500" Height="192" Width="500">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid.Resources>
|
||||
<converters:InverseBool x:Key="InverseBool"/>
|
||||
</Grid.Resources>
|
||||
|
||||
<Label Grid.Row="0" Grid.ColumnSpan="2" FontWeight="DemiBold" FontSize="18" Content="Fetch Remote Changes"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right" Content="Remote :"/>
|
||||
<ComboBox x:Name="combRemotes" Grid.Row="2" Grid.Column="1" VerticalAlignment="Center" IsEnabled="{Binding ElementName=chkFetchAll, Path=IsChecked, Converter={StaticResource InverseBool}}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate DataType="{x:Type git:Remote}">
|
||||
<StackPanel Orientation="Horizontal" Height="20">
|
||||
<Path Margin="4,0,0,0" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Remote}"/>
|
||||
<Label Content="{Binding Name}" Padding="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<CheckBox Grid.Row="3" Grid.Column="1"
|
||||
x:Name="chkFetchAll"
|
||||
IsChecked="True"
|
||||
Content="Fetch all remotes"/>
|
||||
|
||||
<CheckBox Grid.Row="4" Grid.Column="1"
|
||||
x:Name="chkPrune"
|
||||
IsChecked="True"
|
||||
Content="Prune remote dead branches"/>
|
||||
|
||||
<Grid Grid.Row="6" Grid.ColumnSpan="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Column="1" Click="Start" Content="SURE" Style="{StaticResource Style.Button.AccentBordered}"/>
|
||||
<Button Grid.Column="3" Click="Cancel" Content="CANCEL" Style="{StaticResource Style.Button.Bordered}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="0" Grid.RowSpan="7" Grid.ColumnSpan="2" x:Name="status" Visibility="Collapsed" Background="{StaticResource Brush.BG1}" Opacity=".9">
|
||||
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Path x:Name="statusIcon" Width="48" Height="48" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Loading}" RenderTransformOrigin=".5,.5">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
|
||||
<Label x:Name="statusMsg" Margin="0,8,0,0"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
85
SourceGit/UI/Fetch.xaml.cs
Normal file
85
SourceGit/UI/Fetch.xaml.cs
Normal file
|
@ -0,0 +1,85 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Fetch dialog.
|
||||
/// </summary>
|
||||
public partial class Fetch : UserControl {
|
||||
private Git.Repository repo = null;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="opened">Opened repository</param>
|
||||
/// <param name="preferRemote">Prefer selected remote.</param>
|
||||
public Fetch(Git.Repository opened, string preferRemote) {
|
||||
repo = opened;
|
||||
InitializeComponent();
|
||||
|
||||
Task.Run(() => {
|
||||
var remotes = repo.Remotes();
|
||||
Dispatcher.Invoke(() => {
|
||||
combRemotes.ItemsSource = remotes;
|
||||
if (preferRemote != null) {
|
||||
combRemotes.SelectedIndex = remotes.FindIndex(r => r.Name == preferRemote);
|
||||
chkFetchAll.IsChecked = false;
|
||||
} else {
|
||||
combRemotes.SelectedIndex = 0;
|
||||
chkFetchAll.IsChecked = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show fetch dialog.
|
||||
/// </summary>
|
||||
/// <param name="repo"></param>
|
||||
/// <param name="preferRemote"></param>
|
||||
public static void Show(Git.Repository repo, string preferRemote = null) {
|
||||
PopupManager.Show(new Fetch(repo, preferRemote));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start fetch
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void Start(object sender, RoutedEventArgs e) {
|
||||
bool prune = chkPrune.IsChecked == true;
|
||||
|
||||
PopupManager.Lock();
|
||||
|
||||
status.Visibility = Visibility.Visible;
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
|
||||
if (chkFetchAll.IsChecked == true) {
|
||||
await Task.Run(() => repo.Fetch(null, prune, msg => Dispatcher.Invoke(() => statusMsg.Content = msg)));
|
||||
} else {
|
||||
var remote = combRemotes.SelectedItem as Git.Remote;
|
||||
await Task.Run(() => repo.Fetch(remote, prune, msg => Dispatcher.Invoke(() => statusMsg.Content = msg)));
|
||||
}
|
||||
|
||||
status.Visibility = Visibility.Collapsed;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
PopupManager.Close(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Cancel(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Close();
|
||||
}
|
||||
}
|
||||
}
|
169
SourceGit/UI/FileHistories.xaml
Normal file
169
SourceGit/UI/FileHistories.xaml
Normal file
|
@ -0,0 +1,169 @@
|
|||
<Window x:Class="SourceGit.UI.FileHistories"
|
||||
x:Name="me"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:SourceGit.UI"
|
||||
xmlns:git="clr-namespace:SourceGit.Git"
|
||||
mc:Ignorable="d"
|
||||
Height="600" Width="800">
|
||||
|
||||
<!-- Enable WindowChrome -->
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome UseAeroCaptionButtons="False" CornerRadius="0" CaptionHeight="32"/>
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
<!-- Layout Window -->
|
||||
<Border Background="{StaticResource Brush.BG1}">
|
||||
<!-- Fix Maximize BUG -->
|
||||
<Border.Style>
|
||||
<Style TargetType="{x:Type Border}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding WindowState, ElementName=me}" Value="Maximized">
|
||||
<Setter Property="Margin" Value="6"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding WindowState, ElementName=me}" Value="Normal">
|
||||
<Setter Property="Margin" Value="0"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Border.Style>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Titlebar -->
|
||||
<Grid Grid.Row="0" Background="{StaticResource Brush.BG4}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- LOGO & TITLE -->
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal">
|
||||
<Path
|
||||
Width="20" Height="20" Margin="6,-1,2,0"
|
||||
Style="{StaticResource Style.Icon}"
|
||||
Data="{StaticResource Icon.Git}"
|
||||
Fill="#FFF05133"
|
||||
WindowChrome.IsHitTestVisibleInChrome="True"
|
||||
MouseLeftButtonDown="LogoMouseButtonDown"/>
|
||||
<Label Content="SOURCE GIT - FILE HISTORIES" FontWeight="Light"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Options -->
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" WindowChrome.IsHitTestVisibleInChrome="True">
|
||||
<Button Click="Minimize" Width="32" Style="{StaticResource Style.Button.HighlightHover}">
|
||||
<Path Style="{StaticResource Style.WindowControlIcon}" Data="{StaticResource Icon.Minimize}"/>
|
||||
</Button>
|
||||
<Button Click="MaximizeOrRestore" Width="32" Style="{StaticResource Style.Button.HighlightHover}">
|
||||
<Path>
|
||||
<Path.Style>
|
||||
<Style TargetType="{x:Type Path}" BasedOn="{StaticResource Style.WindowControlIcon}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding WindowState, ElementName=me}" Value="Maximized">
|
||||
<Setter Property="Data" Value="{StaticResource Icon.Restore}"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding WindowState, ElementName=me}" Value="Normal">
|
||||
<Setter Property="Data" Value="{StaticResource Icon.Maximize}"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Path.Style>
|
||||
</Path>
|
||||
</Button>
|
||||
<Button Click="Quit" Width="32">
|
||||
<Button.Style>
|
||||
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource Style.Button.HighlightHover}">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="Red"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
|
||||
<Path Width="10" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Close}"/>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Body -->
|
||||
<Border Grid.Row="1" ClipToBounds="True">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="300"/>
|
||||
<ColumnDefinition Width="1"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<DataGrid
|
||||
x:Name="commitList"
|
||||
Margin="2,0,0,0"
|
||||
Grid.Column="0"
|
||||
Background="{StaticResource Brush.BG2}"
|
||||
BorderThickness="0"
|
||||
SelectionMode="Single"
|
||||
SelectionChanged="CommitSelectionChanged">
|
||||
|
||||
<DataGrid.Columns>
|
||||
<DataGridTemplateColumn Width="*">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<Border BorderBrush="{StaticResource Brush.BG4}" BorderThickness="0,0,0,1" Padding="4">
|
||||
<StackPanel Orientation="Vertical" Margin="2" MaxWidth="290">
|
||||
<Grid TextBlock.FontSize="11" TextBlock.Foreground="{StaticResource Brush.FG2}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="72"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="120"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Grid.Column="0" Padding="0">
|
||||
<Hyperlink RequestNavigate="NavigateToCommit" NavigateUri="{Binding SHA}" Foreground="DarkOrange" ToolTip="GOTO COMMIT">
|
||||
<Run Text="{Binding ShortSHA, Mode=OneWay}"/>
|
||||
</Hyperlink>
|
||||
</Label>
|
||||
<TextBlock Grid.Column="1" Text="{Binding Author.Name}"/>
|
||||
<TextBlock Grid.Column="2" Text="{Binding Author.Time}"/>
|
||||
</Grid>
|
||||
|
||||
<TextBlock MaxWidth="280" Foreground="{StaticResource Brush.FG}" Text="{Binding Subject}" TextAlignment="Left" Padding="0" Margin="0,8,2,0"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
<!-- Loading tip -->
|
||||
<Path x:Name="loading" Grid.Column="0" Data="{StaticResource Icon.Loading}" RenderTransformOrigin=".5,.5">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
</Path.RenderTransform>
|
||||
|
||||
<Path.Style>
|
||||
<Style BasedOn="{StaticResource Style.Icon}" TargetType="{x:Type Path}">
|
||||
<Setter Property="Width" Value="48"/>
|
||||
<Setter Property="Height" Value="48"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Center"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="Fill" Value="{StaticResource Brush.FG2}"/>
|
||||
</Style>
|
||||
</Path.Style>
|
||||
</Path>
|
||||
|
||||
<Rectangle Grid.Column="1" Width="1" Fill="{StaticResource Brush.BG4}"/>
|
||||
|
||||
<local:DiffViewer x:Name="diff" Grid.Column="2" Margin="2,0"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Window>
|
||||
|
122
SourceGit/UI/FileHistories.xaml.cs
Normal file
122
SourceGit/UI/FileHistories.xaml.cs
Normal file
|
@ -0,0 +1,122 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Windows.Navigation;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// File histories panel.
|
||||
/// </summary>
|
||||
public partial class FileHistories : Window {
|
||||
private Git.Repository repo = null;
|
||||
private string file = null;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="repo"></param>
|
||||
/// <param name="file"></param>
|
||||
public FileHistories(Git.Repository repo, string file) {
|
||||
this.repo = repo;
|
||||
this.file = file;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
// Move to center
|
||||
var parent = App.Current.MainWindow;
|
||||
Left = parent.Left + (parent.Width - Width) * 0.5;
|
||||
Top = parent.Top + (parent.Height - Height) * 0.5;
|
||||
|
||||
// Show loading
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
loading.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
loading.Visibility = Visibility.Visible;
|
||||
|
||||
// Load commits
|
||||
Task.Run(() => {
|
||||
var commits = repo.Commits($"-n 10000 -- \"{file}\"");
|
||||
Dispatcher.Invoke(() => {
|
||||
commitList.ItemsSource = commits;
|
||||
commitList.SelectedIndex = 0;
|
||||
|
||||
loading.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
loading.Visibility = Visibility.Collapsed;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logo click
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void LogoMouseButtonDown(object sender, MouseButtonEventArgs e) {
|
||||
var element = e.OriginalSource as FrameworkElement;
|
||||
if (element == null) return;
|
||||
|
||||
var pos = PointToScreen(new Point(0, 33));
|
||||
SystemCommands.ShowSystemMenu(this, pos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Minimize
|
||||
/// </summary>
|
||||
private void Minimize(object sender, RoutedEventArgs e) {
|
||||
SystemCommands.MinimizeWindow(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximize/Restore
|
||||
/// </summary>
|
||||
private void MaximizeOrRestore(object sender, RoutedEventArgs e) {
|
||||
if (WindowState == WindowState.Normal) {
|
||||
SystemCommands.MaximizeWindow(this);
|
||||
} else {
|
||||
SystemCommands.RestoreWindow(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quit
|
||||
/// </summary>
|
||||
private void Quit(object sender, RoutedEventArgs e) {
|
||||
Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Commit selection change event.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void CommitSelectionChanged(object sender, SelectionChangedEventArgs e) {
|
||||
if (e.AddedItems.Count != 1) return;
|
||||
|
||||
var commit = e.AddedItems[0] as Git.Commit;
|
||||
var start = $"{commit.SHA}^";
|
||||
if (commit.Parents.Count == 0) start = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
||||
|
||||
List<string> data = new List<string>();
|
||||
await Task.Run(() => {
|
||||
data = repo.Diff(start, commit.SHA, file);
|
||||
});
|
||||
diff.SetData(data, $"{file} @ {commit.ShortSHA}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigate to given string
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void NavigateToCommit(object sender, RequestNavigateEventArgs e) {
|
||||
repo.OnNavigateCommit?.Invoke(e.Uri.OriginalString);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
50
SourceGit/UI/GitFlowFinishBranch.xaml
Normal file
50
SourceGit/UI/GitFlowFinishBranch.xaml
Normal file
|
@ -0,0 +1,50 @@
|
|||
<UserControl x:Class="SourceGit.UI.GitFlowFinishBranch"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="160" d:DesignWidth="500" Height="128" Width="500">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.ColumnSpan="2" x:Name="txtTitle" FontWeight="DemiBold" FontSize="18"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" x:Name="txtBranchType" HorizontalAlignment="Right"/>
|
||||
<StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal">
|
||||
<Path Width="12" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Branch}" Margin="4,0"/>
|
||||
<Label x:Name="txtBranchName"/>
|
||||
</StackPanel>
|
||||
|
||||
<Grid Grid.Row="4" Grid.ColumnSpan="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Column="1" Click="Sure" Content="SURE" Style="{StaticResource Style.Button.AccentBordered}"/>
|
||||
<Button Grid.Column="3" Click="Cancel" Content="CANCEL" Style="{StaticResource Style.Button.Bordered}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="0" Grid.RowSpan="5" Grid.ColumnSpan="2" x:Name="status" Visibility="Collapsed" Background="{StaticResource Brush.BG1}" Opacity=".9">
|
||||
<Path x:Name="statusIcon" Width="48" Height="48" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Loading}" RenderTransformOrigin=".5,.5">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
87
SourceGit/UI/GitFlowFinishBranch.xaml.cs
Normal file
87
SourceGit/UI/GitFlowFinishBranch.xaml.cs
Normal file
|
@ -0,0 +1,87 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Confirm finish git-flow branch dialog
|
||||
/// </summary>
|
||||
public partial class GitFlowFinishBranch : UserControl {
|
||||
private Git.Repository repo = null;
|
||||
private Git.Branch branch = null;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="repo"></param>
|
||||
/// <param name="branch"></param>
|
||||
public GitFlowFinishBranch(Git.Repository repo, Git.Branch branch) {
|
||||
this.repo = repo;
|
||||
this.branch = branch;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
switch (branch.Kind) {
|
||||
case Git.Branch.Type.Feature:
|
||||
txtTitle.Content = "Git Flow - Finish Feature";
|
||||
txtBranchType.Content = "Feature :";
|
||||
break;
|
||||
case Git.Branch.Type.Release:
|
||||
txtTitle.Content = "Git Flow - Finish Release";
|
||||
txtBranchType.Content = "Release :";
|
||||
break;
|
||||
case Git.Branch.Type.Hotfix:
|
||||
txtTitle.Content = "Git Flow - Finish Hotfix";
|
||||
txtBranchType.Content = "Hotfix :";
|
||||
break;
|
||||
default:
|
||||
PopupManager.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
txtBranchName.Content = branch.Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show this dialog.
|
||||
/// </summary>
|
||||
/// <param name="repo"></param>
|
||||
/// <param name="branch"></param>
|
||||
public static void Show(Git.Repository repo, Git.Branch branch) {
|
||||
PopupManager.Show(new GitFlowFinishBranch(repo, branch));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Do finish
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void Sure(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Lock();
|
||||
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
status.Visibility = Visibility.Visible;
|
||||
|
||||
await Task.Run(() => repo.FinishGitFlowBranch(branch));
|
||||
|
||||
status.Visibility = Visibility.Collapsed;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
PopupManager.Close(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel finish
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Cancel(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Close();
|
||||
}
|
||||
}
|
||||
}
|
68
SourceGit/UI/GitFlowSetup.xaml
Normal file
68
SourceGit/UI/GitFlowSetup.xaml
Normal file
|
@ -0,0 +1,68 @@
|
|||
<UserControl x:Class="SourceGit.UI.GitFlowSetup"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:helpers="clr-namespace:SourceGit.Helpers"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="160" d:DesignWidth="500" Height="304" Width="500">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.ColumnSpan="2" FontWeight="DemiBold" FontSize="18" Content="Git Flow - Initialize"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center" Content="Production Branch :"/>
|
||||
<TextBox Grid.Row="2" Grid.Column="1" x:Name="txtMaster" Padding="2,0" TextChanged="ValidateNames" VerticalContentAlignment="Center" helpers:TextBoxHelper.Placeholder="master" Height="24" Text="master"/>
|
||||
<Label Grid.Row="3" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center" Content="Development Branch :"/>
|
||||
<TextBox Grid.Row="3" Grid.Column="1" x:Name="txtDevelop" Padding="2,0" TextChanged="ValidateNames" VerticalContentAlignment="Center" helpers:TextBoxHelper.Placeholder="develop" Height="24" Text="develop"/>
|
||||
|
||||
<Label Grid.Row="5" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center" Content="Feature Prefix :"/>
|
||||
<TextBox Grid.Row="5" Grid.Column="1" x:Name="txtFeature" Padding="2,0" TextChanged="ValidateNames" VerticalContentAlignment="Center" helpers:TextBoxHelper.Placeholder="feature/" Height="24" Text="feature/"/>
|
||||
<Label Grid.Row="6" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center" Content="Release Prefix :"/>
|
||||
<TextBox Grid.Row="6" Grid.Column="1" x:Name="txtRelease" Padding="2,0" TextChanged="ValidateNames" VerticalContentAlignment="Center" helpers:TextBoxHelper.Placeholder="release/" Height="24" Text="release/"/>
|
||||
<Label Grid.Row="7" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center" Content="Hotfix Prefix :"/>
|
||||
<TextBox Grid.Row="7" Grid.Column="1" x:Name="txtHotfix" Padding="2,0" TextChanged="ValidateNames" VerticalContentAlignment="Center" helpers:TextBoxHelper.Placeholder="hotfix/" Height="24" Text="hotfix/"/>
|
||||
<Label Grid.Row="8" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center" Content="Version Tag Prefix :"/>
|
||||
<TextBox Grid.Row="8" Grid.Column="1" x:Name="txtVersion" Padding="2,0" TextChanged="ValidateNames" VerticalContentAlignment="Center" helpers:TextBoxHelper.Placeholder="Optional." Height="24" Text=""/>
|
||||
|
||||
<Grid Grid.Row="10" Grid.ColumnSpan="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Column="0" x:Name="txtValidation" Foreground="Red"/>
|
||||
|
||||
<Button Grid.Column="1" x:Name="btnSure" Click="Sure" Content="SURE" Style="{StaticResource Style.Button.AccentBordered}"/>
|
||||
<Button Grid.Column="3" Click="Cancel" Content="CANCEL" Style="{StaticResource Style.Button.Bordered}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="0" Grid.RowSpan="11" Grid.ColumnSpan="2" x:Name="status" Visibility="Collapsed" Background="{StaticResource Brush.BG1}" Opacity=".9">
|
||||
<Path x:Name="statusIcon" Width="48" Height="48" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Loading}" RenderTransformOrigin=".5,.5">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
134
SourceGit/UI/GitFlowSetup.xaml.cs
Normal file
134
SourceGit/UI/GitFlowSetup.xaml.cs
Normal file
|
@ -0,0 +1,134 @@
|
|||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Dialog to initialize git flow.
|
||||
/// </summary>
|
||||
public partial class GitFlowSetup : UserControl {
|
||||
private Git.Repository repo = null;
|
||||
private Regex regex = new Regex(@"^[\w\-/\.]+$");
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="opened"></param>
|
||||
public GitFlowSetup(Git.Repository opened) {
|
||||
repo = opened;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open this dialog.
|
||||
/// </summary>
|
||||
/// <param name="repo"></param>
|
||||
public static void Show(Git.Repository repo) {
|
||||
PopupManager.Show(new GitFlowSetup(repo));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start to initialize git-flow.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void Sure(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Lock();
|
||||
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
status.Visibility = Visibility.Visible;
|
||||
|
||||
var master = txtMaster.Text;
|
||||
var dev = txtDevelop.Text;
|
||||
var feature = txtFeature.Text;
|
||||
var release = txtRelease.Text;
|
||||
var hotfix = txtHotfix.Text;
|
||||
var version = txtVersion.Text;
|
||||
|
||||
await Task.Run(() => repo.EnableGitFlow(master, dev, feature, release, hotfix, version));
|
||||
|
||||
status.Visibility = Visibility.Collapsed;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
PopupManager.Close(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Cancel(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate input names.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void ValidateNames(object sender, TextChangedEventArgs e) {
|
||||
if (!IsLoaded) return;
|
||||
|
||||
var master = txtMaster.Text;
|
||||
var dev = txtDevelop.Text;
|
||||
var feature = txtFeature.Text;
|
||||
var release = txtRelease.Text;
|
||||
var hotfix = txtHotfix.Text;
|
||||
|
||||
if (!ValidateBranch("Production", master)) return;
|
||||
if (!ValidateBranch("Development", dev)) return;
|
||||
|
||||
if (dev == master) {
|
||||
txtValidation.Content = "Development branch is same with production!";
|
||||
btnSure.IsEnabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ValidatePrefix("Feature", feature)) return;
|
||||
if (!ValidatePrefix("Release", release)) return;
|
||||
if (!ValidatePrefix("Hotfix", hotfix)) return;
|
||||
|
||||
txtValidation.Content = "";
|
||||
btnSure.IsEnabled = true;
|
||||
}
|
||||
|
||||
private bool ValidateBranch(string type, string name) {
|
||||
if (string.IsNullOrEmpty(name)) {
|
||||
txtValidation.Content = $"{type} branch name can't be empty";
|
||||
btnSure.IsEnabled = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!regex.IsMatch(name)) {
|
||||
txtValidation.Content = $"{type} branch name contains invalid characters.";
|
||||
btnSure.IsEnabled = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool ValidatePrefix(string type, string prefix) {
|
||||
if (string.IsNullOrEmpty(prefix)) {
|
||||
txtValidation.Content = $"{type} prefix is required!";
|
||||
btnSure.IsEnabled = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!regex.IsMatch(prefix)) {
|
||||
txtValidation.Content = $"{type} prefix contains invalid characters.";
|
||||
btnSure.IsEnabled = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
57
SourceGit/UI/GitFlowStartBranch.xaml
Normal file
57
SourceGit/UI/GitFlowStartBranch.xaml
Normal file
|
@ -0,0 +1,57 @@
|
|||
<UserControl x:Class="SourceGit.UI.GitFlowStartBranch"
|
||||
x:Name="me"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:helpers="clr-namespace:SourceGit.Helpers"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="160" d:DesignWidth="500" Height="128" Width="500">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.ColumnSpan="2" x:Name="txtTitle" FontWeight="DemiBold" FontSize="18"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" x:Name="txtPrefix" HorizontalAlignment="Right" FontSize="16" VerticalAlignment="Center"/>
|
||||
<TextBox Grid.Row="2" Grid.Column="1" x:Name="txtName" VerticalContentAlignment="Center" helpers:TextBoxHelper.Placeholder="Enter name" Height="24">
|
||||
<TextBox.Text>
|
||||
<Binding ElementName="me" Path="SubName" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
|
||||
<Binding.ValidationRules>
|
||||
<helpers:BranchNameRule x:Name="nameValidator"/>
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
|
||||
<Grid Grid.Row="4" Grid.ColumnSpan="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Column="1" Click="Sure" Content="SURE" Style="{StaticResource Style.Button.AccentBordered}"/>
|
||||
<Button Grid.Column="3" Click="Cancel" Content="CANCEL" Style="{StaticResource Style.Button.Bordered}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="0" Grid.RowSpan="5" Grid.ColumnSpan="2" x:Name="status" Visibility="Collapsed" Background="{StaticResource Brush.BG1}" Opacity=".9">
|
||||
<Path x:Name="statusIcon" Width="48" Height="48" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Loading}" RenderTransformOrigin=".5,.5">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
103
SourceGit/UI/GitFlowStartBranch.xaml.cs
Normal file
103
SourceGit/UI/GitFlowStartBranch.xaml.cs
Normal file
|
@ -0,0 +1,103 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Start git-flow branch dialog.
|
||||
/// </summary>
|
||||
public partial class GitFlowStartBranch : UserControl {
|
||||
private Git.Repository repo = null;
|
||||
private Git.Branch.Type type = Git.Branch.Type.Feature;
|
||||
|
||||
/// <summary>
|
||||
/// Sub-name for this git-flow branch.
|
||||
/// </summary>
|
||||
public string SubName {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="repo"></param>
|
||||
/// <param name="type"></param>
|
||||
public GitFlowStartBranch(Git.Repository repo, Git.Branch.Type type) {
|
||||
this.repo = repo;
|
||||
this.type = type;
|
||||
|
||||
InitializeComponent();
|
||||
nameValidator.Repo = repo;
|
||||
|
||||
switch (type) {
|
||||
case Git.Branch.Type.Feature:
|
||||
var featurePrefix = repo.GetFeaturePrefix();
|
||||
txtTitle.Content = "Git Flow - Start Feature";
|
||||
txtPrefix.Content = featurePrefix;
|
||||
nameValidator.Prefix = featurePrefix;
|
||||
break;
|
||||
case Git.Branch.Type.Release:
|
||||
var releasePrefix = repo.GetReleasePrefix();
|
||||
txtTitle.Content = "Git Flow - Start Release";
|
||||
txtPrefix.Content = releasePrefix;
|
||||
nameValidator.Prefix = releasePrefix;
|
||||
break;
|
||||
case Git.Branch.Type.Hotfix:
|
||||
var hotfixPrefix = repo.GetHotfixPrefix();
|
||||
txtTitle.Content = "Git Flow - Start Hotfix";
|
||||
txtPrefix.Content = hotfixPrefix;
|
||||
nameValidator.Prefix = hotfixPrefix;
|
||||
break;
|
||||
default:
|
||||
PopupManager.Close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display this dialog
|
||||
/// </summary>
|
||||
/// <param name="repo"></param>
|
||||
/// <param name="type"></param>
|
||||
public static void Show(Git.Repository repo, Git.Branch.Type type) {
|
||||
PopupManager.Show(new GitFlowStartBranch(repo, type));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start git-flow branch
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void Sure(object sender, RoutedEventArgs e) {
|
||||
txtName.GetBindingExpression(TextBox.TextProperty).UpdateSource();
|
||||
if (Validation.GetHasError(txtName)) return;
|
||||
|
||||
PopupManager.Lock();
|
||||
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
status.Visibility = Visibility.Visible;
|
||||
|
||||
await Task.Run(() => repo.StartGitFlowBranch(type, SubName));
|
||||
|
||||
status.Visibility = Visibility.Collapsed;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
PopupManager.Close(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Cancel(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Close();
|
||||
}
|
||||
}
|
||||
}
|
187
SourceGit/UI/Histories.xaml
Normal file
187
SourceGit/UI/Histories.xaml
Normal file
|
@ -0,0 +1,187 @@
|
|||
<UserControl x:Class="SourceGit.UI.Histories"
|
||||
x:Name="me"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:SourceGit.UI"
|
||||
xmlns:git="clr-namespace:SourceGit.Git"
|
||||
xmlns:helpers="clr-namespace:SourceGit.Helpers"
|
||||
xmlns:sourcegit="clr-namespace:SourceGit"
|
||||
xmlns:converters="clr-namespace:SourceGit.Converters"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
Unloaded="Cleanup">
|
||||
<Grid x:Name="layout">
|
||||
<!-- List Panel (SearchBar + DataGrid) -->
|
||||
<Grid x:Name="commitListPanel" Background="{StaticResource Brush.BG2}" ClipToBounds="True">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Loading Tip -->
|
||||
<Path x:Name="loading" Grid.RowSpan="2" Data="{StaticResource Icon.Loading}" RenderTransformOrigin=".5,.5">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
</Path.RenderTransform>
|
||||
|
||||
<Path.Style>
|
||||
<Style BasedOn="{StaticResource Style.Icon}" TargetType="{x:Type Path}">
|
||||
<Setter Property="Width" Value="48"/>
|
||||
<Setter Property="Height" Value="48"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Center"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="Fill" Value="{StaticResource Brush.FG2}"/>
|
||||
</Style>
|
||||
</Path.Style>
|
||||
</Path>
|
||||
|
||||
<!-- SearchBar -->
|
||||
<Grid x:Name="searchBar" Margin="0,-32,0,0" Grid.Row="0">
|
||||
<TextBox x:Name="txtSearch" Margin="4" Height="24" Padding="0,0,16,0" helpers:TextBoxHelper.Placeholder="SEARCH SHA/SUBJECT/AUTHOR. PRESS ENTER TO SEARCH, ESC TO QUIT" PreviewKeyDown="PreviewSearchKeyDown"/>
|
||||
<Button HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,8,0" Style="{StaticResource Style.Button.HighlightHover}" ToolTip="CLEAR" Click="ClearSearch">
|
||||
<Path Width="10" Height="10" Fill="{StaticResource Brush.Border1}" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Close}"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<!-- Commit DataGrid -->
|
||||
<DataGrid
|
||||
Grid.Row="1"
|
||||
x:Name="commitList"
|
||||
RowHeight="{x:Static helpers:CommitGraphMaker.UNIT_HEIGHT}"
|
||||
ScrollViewer.ScrollChanged="CommitListScrolled"
|
||||
SelectionChanged="CommitSelectChanged"
|
||||
SelectionUnit="FullRow">
|
||||
<DataGrid.Resources>
|
||||
<converters:IndentToMargin x:Key="CommitTitleMargin"/>
|
||||
|
||||
<Style x:Key="Style.DataGridText" TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="FontFamily" Value="Consolas"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource Brush.FG}"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="Padding" Value="16,0"/>
|
||||
</Style>
|
||||
<Style x:Key="Style.DataGridText.NoPadding" TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="FontFamily" Value="Consolas"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource Brush.FG}"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
</DataGrid.Resources>
|
||||
<DataGrid.Columns>
|
||||
<DataGridTemplateColumn Width="*" IsReadOnly="True">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Margin="{Binding GraphOffset, Converter={StaticResource CommitTitleMargin}}">
|
||||
<ItemsControl x:Name="Decorator" ItemsSource="{Binding Decorators}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<WrapPanel Orientation="Horizontal" VerticalAlignment="Center"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="{x:Type git:Decorator}">
|
||||
<Border x:Name="BG" Height="16" Margin="2,0">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="18"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Border Grid.Column="0" Background="{StaticResource Brush.BG5}">
|
||||
<Path x:Name="Icon" Width="8" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Branch}"/>
|
||||
</Border>
|
||||
|
||||
<TextBlock x:Name="Name" Grid.Column="1" Text="{Binding Name}" FontSize="11" Padding="4,0" Foreground="Black" VerticalAlignment="Center" TextWrapping="NoWrap"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<DataTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding Type}" Value="{x:Static git:DecoratorType.Tag}">
|
||||
<Setter TargetName="BG" Property="Background" Value="#FF02C302"/>
|
||||
<Setter TargetName="Icon" Property="Data" Value="{StaticResource Icon.Tag}"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding Type}" Value="{x:Static git:DecoratorType.LocalBranchHead}">
|
||||
<Setter TargetName="BG" Property="Background" Value="#FFFFB835"/>
|
||||
<Setter TargetName="Icon" Property="Data" Value="{StaticResource Icon.Branch}"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding Type}" Value="{x:Static git:DecoratorType.RemoteBranchHead}">
|
||||
<Setter TargetName="BG" Property="Background" Value="#FFFFB835"/>
|
||||
<Setter TargetName="Icon" Property="Data" Value="{StaticResource Icon.Remote}"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding Type}" Value="{x:Static git:DecoratorType.CurrentBranchHead}">
|
||||
<Setter TargetName="BG" Property="Background" Value="#FFFFB835"/>
|
||||
<Setter TargetName="Icon" Property="Data" Value="{StaticResource Icon.Check}"/>
|
||||
<Setter TargetName="Icon" Property="Fill" Value="Orange"/>
|
||||
<Setter TargetName="Name" Property="FontWeight" Value="Bold"/>
|
||||
<Setter TargetName="Name" Property="Foreground" Value="{StaticResource Brush.FG}"/>
|
||||
</DataTrigger>
|
||||
</DataTemplate.Triggers>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<TextBlock Text="{Binding Subject}" VerticalAlignment="Center" Margin="2,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<DataTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding HasDecorators}" Value="False">
|
||||
<Setter TargetName="Decorator" Property="Visibility" Value="Collapsed"/>
|
||||
</DataTrigger>
|
||||
</DataTemplate.Triggers>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
|
||||
<DataGridTextColumn Width="100" IsReadOnly="True" Binding="{Binding Committer.Name}" ElementStyle="{StaticResource Style.DataGridText}"/>
|
||||
<DataGridTextColumn Width="84" IsReadOnly="True" Binding="{Binding ShortSHA}" ElementStyle="{StaticResource Style.DataGridText}"/>
|
||||
<DataGridTextColumn Width="128" IsReadOnly="True" Binding="{Binding Committer.Time}" ElementStyle="{StaticResource Style.DataGridText.NoPadding}"/>
|
||||
</DataGrid.Columns>
|
||||
|
||||
<DataGrid.RowStyle>
|
||||
<Style TargetType="{x:Type DataGridRow}" BasedOn="{StaticResource Style.DataGridRow}">
|
||||
<EventSetter Event="ContextMenuOpening" Handler="CommitContextMenuOpening"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsMerged}" Value="False">
|
||||
<Setter Property="Opacity" Value=".4"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</DataGrid.RowStyle>
|
||||
</DataGrid>
|
||||
|
||||
<!-- Commit Graph -->
|
||||
<Border Grid.Row="1" Margin="0,0,310,0" ClipToBounds="True" IsHitTestVisible="False">
|
||||
<Canvas x:Name="commitGraph"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<!-- Split -->
|
||||
<GridSplitter x:Name="splitter" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="{StaticResource Brush.BG1}"/>
|
||||
|
||||
<!-- Detail for selected commit -->
|
||||
<Grid x:Name="commitDetailPanel" Background="{StaticResource Brush.BG1}">
|
||||
<!-- Selected commit detail -->
|
||||
<local:CommitViewer x:Name="commitViewer"/>
|
||||
|
||||
<!-- Mask for select multi rows in commit list -->
|
||||
<Border x:Name="mask4MultiSelection" Background="{StaticResource Brush.BG1}" Visibility="Collapsed">
|
||||
<StackPanel Orientation="Vertical" VerticalAlignment="Center" Opacity=".2">
|
||||
<Path Width="160" Height="160" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Git}"/>
|
||||
<Label x:Name="txtTotalSelected" Margin="0,16,0,0" FontSize="24" FontWeight="UltraBold" HorizontalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- SWITCH LAYOUT -->
|
||||
<ToggleButton
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"
|
||||
Margin="0,4,8,0"
|
||||
Style="{StaticResource Style.ToggleButton.Orientation}"
|
||||
ToolTip="Toggle Horizontal/Vertical Layout"
|
||||
IsChecked="{Binding Source={x:Static sourcegit:App.Preference}, Path=UIUseHorizontalLayout, Mode=TwoWay}"
|
||||
Checked="ChangeOrientation" Unchecked="ChangeOrientation"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
649
SourceGit/UI/Histories.xaml.cs
Normal file
649
SourceGit/UI/Histories.xaml.cs
Normal file
|
@ -0,0 +1,649 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Commit histories viewer
|
||||
/// </summary>
|
||||
public partial class Histories : UserControl {
|
||||
|
||||
/// <summary>
|
||||
/// Current opened repository.
|
||||
/// </summary>
|
||||
public Git.Repository Repo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cached commits.
|
||||
/// </summary>
|
||||
private List<Git.Commit> cachedCommits = new List<Git.Commit>();
|
||||
|
||||
/// <summary>
|
||||
/// Is in search mode?
|
||||
/// </summary>
|
||||
private bool isSearchMode = false;
|
||||
|
||||
/// <summary>
|
||||
/// Regex to test search input.
|
||||
/// </summary>
|
||||
private Regex commitRegex = new Regex(@"^[0-9a-f]{6,40}$", RegexOptions.None);
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public Histories() {
|
||||
InitializeComponent();
|
||||
ChangeOrientation(null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigate to given commit.
|
||||
/// </summary>
|
||||
/// <param name="commit"></param>
|
||||
public void Navigate(string commit) {
|
||||
if (string.IsNullOrEmpty(commit)) return;
|
||||
|
||||
foreach (var item in commitList.ItemsSource) {
|
||||
var c = item as Git.Commit;
|
||||
if (c.SHA.StartsWith(commit)) {
|
||||
commitList.SelectedItem = c;
|
||||
commitList.ScrollIntoView(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loading tips.
|
||||
/// </summary>
|
||||
/// <param name="enabled"></param>
|
||||
public void SetLoadingEnabled(bool enabled) {
|
||||
if (enabled) {
|
||||
loading.Visibility = Visibility.Visible;
|
||||
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
loading.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
} else {
|
||||
loading.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
loading.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
#region DATA
|
||||
public void SetCommits(List<Git.Commit> commits) {
|
||||
cachedCommits = commits;
|
||||
if (isSearchMode) return;
|
||||
|
||||
var maker = Helpers.CommitGraphMaker.Parse(commits);
|
||||
|
||||
Dispatcher.Invoke(() => {
|
||||
commitGraph.Children.Clear();
|
||||
isSearchMode = false;
|
||||
txtSearch.Text = "";
|
||||
|
||||
// Draw all lines.
|
||||
foreach (var path in maker.Lines) {
|
||||
var size = path.Points.Count;
|
||||
var geo = new StreamGeometry();
|
||||
var last = path.Points[0];
|
||||
|
||||
using (var ctx = geo.Open()) {
|
||||
ctx.BeginFigure(last, false, false);
|
||||
|
||||
for (int i = 1; i < size; i++) {
|
||||
var cur = path.Points[i];
|
||||
|
||||
if (cur.X > last.X) {
|
||||
ctx.QuadraticBezierTo(new Point(cur.X, last.Y), cur, true, false);
|
||||
} else if (cur.X < last.X) {
|
||||
if (i < size - 1) {
|
||||
cur.Y += Helpers.CommitGraphMaker.HALF_HEIGHT;
|
||||
|
||||
var midY = (last.Y + cur.Y) / 2;
|
||||
var midX = (last.X + cur.X) / 2;
|
||||
ctx.PolyQuadraticBezierTo(new Point[] {
|
||||
new Point(last.X, midY),
|
||||
new Point(midX, midY),
|
||||
new Point(cur.X, midY),
|
||||
cur
|
||||
}, true, false);
|
||||
} else {
|
||||
ctx.QuadraticBezierTo(new Point(last.X, cur.Y), cur, true, false);
|
||||
}
|
||||
} else {
|
||||
ctx.LineTo(cur, true, false);
|
||||
}
|
||||
|
||||
last = cur;
|
||||
}
|
||||
}
|
||||
|
||||
geo.Freeze();
|
||||
|
||||
var p = new Path();
|
||||
p.Data = geo;
|
||||
p.Stroke = path.Brush;
|
||||
p.StrokeThickness = 2;
|
||||
commitGraph.Children.Add(p);
|
||||
}
|
||||
maker.Lines.Clear();
|
||||
|
||||
// Draw short links
|
||||
foreach (var link in maker.Links) {
|
||||
var geo = new StreamGeometry();
|
||||
|
||||
using (var ctx = geo.Open()) {
|
||||
ctx.BeginFigure(link.Start, false, false);
|
||||
ctx.QuadraticBezierTo(link.Control, link.End, true, false);
|
||||
}
|
||||
|
||||
geo.Freeze();
|
||||
|
||||
var p = new Path();
|
||||
p.Data = geo;
|
||||
p.Stroke = link.Brush;
|
||||
p.StrokeThickness = 2;
|
||||
commitGraph.Children.Add(p);
|
||||
}
|
||||
maker.Links.Clear();
|
||||
|
||||
// Draw points.
|
||||
foreach (var dot in maker.Dots) {
|
||||
var ellipse = new Ellipse();
|
||||
ellipse.Height = 6;
|
||||
ellipse.Width = 6;
|
||||
ellipse.Fill = dot.Color;
|
||||
ellipse.SetValue(Canvas.LeftProperty, dot.X);
|
||||
ellipse.SetValue(Canvas.TopProperty, dot.Y);
|
||||
commitGraph.Children.Add(ellipse);
|
||||
}
|
||||
maker.Dots.Clear();
|
||||
|
||||
commitList.ItemsSource = new List<Git.Commit>(cachedCommits);
|
||||
Navigate(maker.Highlight);
|
||||
SetLoadingEnabled(false);
|
||||
});
|
||||
}
|
||||
|
||||
public void SetSearchResult(List<Git.Commit> commits) {
|
||||
isSearchMode = true;
|
||||
|
||||
foreach (var c in commits) c.GraphOffset = 0;
|
||||
|
||||
Dispatcher.Invoke(() => {
|
||||
commitGraph.Children.Clear();
|
||||
commitList.ItemsSource = new List<Git.Commit>(commits);
|
||||
SetLoadingEnabled(false);
|
||||
});
|
||||
}
|
||||
|
||||
private void Cleanup(object sender, RoutedEventArgs e) {
|
||||
commitGraph.Children.Clear();
|
||||
commitList.ItemsSource = null;
|
||||
cachedCommits.Clear();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region SEARCH_BAR
|
||||
public void OpenSearchBar() {
|
||||
if (searchBar.Margin.Top == 0) return;
|
||||
|
||||
ThicknessAnimation anim = new ThicknessAnimation();
|
||||
anim.From = new Thickness(0, -32, 0, 0);
|
||||
anim.To = new Thickness(0);
|
||||
anim.Duration = TimeSpan.FromSeconds(.3);
|
||||
searchBar.BeginAnimation(Grid.MarginProperty, anim);
|
||||
|
||||
txtSearch.Focus();
|
||||
}
|
||||
|
||||
public void HideSearchBar() {
|
||||
if (searchBar.Margin.Top != 0) return;
|
||||
|
||||
ClearSearch(null, null);
|
||||
|
||||
ThicknessAnimation anim = new ThicknessAnimation();
|
||||
anim.From = new Thickness(0);
|
||||
anim.To = new Thickness(0, -32, 0, 0);
|
||||
anim.Duration = TimeSpan.FromSeconds(.3);
|
||||
searchBar.BeginAnimation(Grid.MarginProperty, anim);
|
||||
}
|
||||
|
||||
private void ClearSearch(object sender, RoutedEventArgs e) {
|
||||
txtSearch.Text = "";
|
||||
if (isSearchMode) {
|
||||
isSearchMode = false;
|
||||
SetLoadingEnabled(true);
|
||||
Task.Run(() => SetCommits(cachedCommits));
|
||||
}
|
||||
}
|
||||
|
||||
private void PreviewSearchKeyDown(object sender, KeyEventArgs e) {
|
||||
if (e.Key == Key.Enter) {
|
||||
string search = txtSearch.Text;
|
||||
if (string.IsNullOrEmpty(search)) {
|
||||
ClearSearch(sender, e);
|
||||
} else if (commitRegex.IsMatch(search)) {
|
||||
SetLoadingEnabled(true);
|
||||
Task.Run(() => {
|
||||
var commits = Repo.Commits($"search -n 1");
|
||||
SetSearchResult(commits);
|
||||
});
|
||||
} else {
|
||||
SetLoadingEnabled(true);
|
||||
|
||||
Task.Run(() => {
|
||||
List<Git.Commit> found = new List<Git.Commit>();
|
||||
|
||||
foreach (var commit in cachedCommits) {
|
||||
if (commit.Subject.IndexOf(search) >= 0 ||
|
||||
(commit.Author != null && commit.Author.Name == search) ||
|
||||
(commit.Committer != null && commit.Committer.Name == search) ||
|
||||
commit.Message.IndexOf(search) >= 0) {
|
||||
found.Add(commit);
|
||||
}
|
||||
}
|
||||
|
||||
SetSearchResult(found);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region COMMIT_DATAGRID_AND_GRAPH
|
||||
private void CommitListScrolled(object sender, ScrollChangedEventArgs e) {
|
||||
commitGraph.Margin = new Thickness(0, -e.VerticalOffset * Helpers.CommitGraphMaker.UNIT_HEIGHT, 0, 0);
|
||||
}
|
||||
|
||||
private void CommitSelectChanged(object sender, SelectionChangedEventArgs e) {
|
||||
mask4MultiSelection.Visibility = Visibility.Collapsed;
|
||||
|
||||
var selected = commitList.SelectedItems;
|
||||
if (selected.Count == 1) {
|
||||
var commit = selected[0] as Git.Commit;
|
||||
if (commit != null) commitViewer.SetData(Repo, commit);
|
||||
} else if (selected.Count > 1) {
|
||||
mask4MultiSelection.Visibility = Visibility.Visible;
|
||||
txtTotalSelected.Content = $"SELECTED {selected.Count} COMMITS";
|
||||
}
|
||||
}
|
||||
|
||||
private MenuItem GetCurrentBranchContextMenu(Git.Branch branch) {
|
||||
var icon = new Path();
|
||||
icon.Style = FindResource("Style.Icon") as Style;
|
||||
icon.Data = FindResource("Icon.Branch") as Geometry;
|
||||
icon.VerticalAlignment = VerticalAlignment.Bottom;
|
||||
icon.Width = 10;
|
||||
|
||||
var submenu = new MenuItem();
|
||||
submenu.Header = branch.Name;
|
||||
submenu.Icon = icon;
|
||||
|
||||
if (!string.IsNullOrEmpty(branch.Upstream)) {
|
||||
var upstream = branch.Upstream.Substring(13);
|
||||
var fastForward = new MenuItem();
|
||||
fastForward.Header = $"Fast-Forward to '{upstream}'";
|
||||
fastForward.Click += (o, e) => {
|
||||
Merge.StartDirectly(Repo, upstream, branch.Name);
|
||||
e.Handled = true;
|
||||
};
|
||||
submenu.Items.Add(fastForward);
|
||||
|
||||
var pull = new MenuItem();
|
||||
pull.Header = $"Pull '{upstream}' ...";
|
||||
pull.Click += (o, e) => {
|
||||
Pull.Show(Repo);
|
||||
e.Handled = true;
|
||||
};
|
||||
submenu.Items.Add(pull);
|
||||
}
|
||||
|
||||
var push = new MenuItem();
|
||||
push.Header = $"Push '{branch.Name}' ...";
|
||||
push.Click += (o, e) => {
|
||||
Push.Show(Repo, branch);
|
||||
e.Handled = true;
|
||||
};
|
||||
submenu.Items.Add(push);
|
||||
submenu.Items.Add(new Separator());
|
||||
|
||||
if (branch.Kind != Git.Branch.Type.Normal) {
|
||||
var flowIcon = new Path();
|
||||
flowIcon.Style = FindResource("Style.Icon") as Style;
|
||||
flowIcon.Data = FindResource("Icon.Flow") as Geometry;
|
||||
flowIcon.Width = 10;
|
||||
|
||||
var finish = new MenuItem();
|
||||
finish.Header = $"Git Flow - Finish '{branch.Name}'";
|
||||
finish.Icon = flowIcon;
|
||||
finish.Click += (o, e) => {
|
||||
GitFlowFinishBranch.Show(Repo, branch);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
submenu.Items.Add(finish);
|
||||
submenu.Items.Add(new Separator());
|
||||
}
|
||||
|
||||
var rename = new MenuItem();
|
||||
rename.Header = "Rename ...";
|
||||
rename.Click += (o, e) => {
|
||||
RenameBranch.Show(Repo, branch);
|
||||
e.Handled = true;
|
||||
};
|
||||
submenu.Items.Add(rename);
|
||||
|
||||
return submenu;
|
||||
}
|
||||
|
||||
private MenuItem GetOtherBranchContextMenu(Git.Branch current, Git.Branch branch, bool merged) {
|
||||
var icon = new Path();
|
||||
icon.Style = FindResource("Style.Icon") as Style;
|
||||
icon.Data = FindResource("Icon.Branch") as Geometry;
|
||||
icon.VerticalAlignment = VerticalAlignment.Bottom;
|
||||
icon.Width = 10;
|
||||
|
||||
var submenu = new MenuItem();
|
||||
submenu.Header = branch.Name;
|
||||
submenu.Icon = icon;
|
||||
|
||||
var checkout = new MenuItem();
|
||||
checkout.Header = $"Checkout '{branch.Name}'";
|
||||
checkout.Click += (o, e) => {
|
||||
if (branch.IsLocal) {
|
||||
Task.Run(() => Repo.Checkout(branch.Name));
|
||||
} else {
|
||||
var upstream = $"refs/remotes/{branch.Name}";
|
||||
var tracked = Repo.Branches().Find(b => b.IsLocal && b.Upstream == upstream);
|
||||
|
||||
if (tracked == null) {
|
||||
CreateBranch.Show(Repo, branch);
|
||||
} else if (!tracked.IsCurrent) {
|
||||
Task.Run(() => Repo.Checkout(tracked.Name));
|
||||
}
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
};
|
||||
submenu.Items.Add(checkout);
|
||||
|
||||
var merge = new MenuItem();
|
||||
merge.Header = $"Merge into '{current.Name}' ...";
|
||||
merge.IsEnabled = !merged;
|
||||
merge.Click += (o, e) => {
|
||||
Merge.Show(Repo, branch.Name, current.Name);
|
||||
e.Handled = true;
|
||||
};
|
||||
submenu.Items.Add(merge);
|
||||
submenu.Items.Add(new Separator());
|
||||
|
||||
if (branch.Kind != Git.Branch.Type.Normal) {
|
||||
var flowIcon = new Path();
|
||||
flowIcon.Style = FindResource("Style.Icon") as Style;
|
||||
flowIcon.Data = FindResource("Icon.Flow") as Geometry;
|
||||
flowIcon.Width = 10;
|
||||
|
||||
var finish = new MenuItem();
|
||||
finish.Header = $"Git Flow - Finish '{branch.Name}'";
|
||||
finish.Icon = flowIcon;
|
||||
finish.Click += (o, e) => {
|
||||
GitFlowFinishBranch.Show(Repo, branch);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
submenu.Items.Add(finish);
|
||||
submenu.Items.Add(new Separator());
|
||||
}
|
||||
|
||||
var rename = new MenuItem();
|
||||
rename.Header = "Rename ...";
|
||||
rename.Visibility = branch.IsLocal ? Visibility.Visible : Visibility.Collapsed;
|
||||
rename.Click += (o, e) => {
|
||||
RenameBranch.Show(Repo, current);
|
||||
e.Handled = true;
|
||||
};
|
||||
submenu.Items.Add(rename);
|
||||
|
||||
var delete = new MenuItem();
|
||||
delete.Header = "Delete ...";
|
||||
delete.Click += (o, e) => {
|
||||
DeleteBranch.Show(Repo, branch);
|
||||
};
|
||||
submenu.Items.Add(delete);
|
||||
|
||||
return submenu;
|
||||
}
|
||||
|
||||
private MenuItem GetTagContextMenu(Git.Tag tag) {
|
||||
var icon = new Path();
|
||||
icon.Style = FindResource("Style.Icon") as Style;
|
||||
icon.Data = FindResource("Icon.Tag") as Geometry;
|
||||
icon.Width = 10;
|
||||
|
||||
var submenu = new MenuItem();
|
||||
submenu.Header = tag.Name;
|
||||
submenu.Icon = icon;
|
||||
submenu.MinWidth = 200;
|
||||
|
||||
var push = new MenuItem();
|
||||
push.Header = "Push ...";
|
||||
push.Click += (o, e) => {
|
||||
PushTag.Show(Repo, tag);
|
||||
e.Handled = true;
|
||||
};
|
||||
submenu.Items.Add(push);
|
||||
|
||||
var delete = new MenuItem();
|
||||
delete.Header = "Delete ...";
|
||||
delete.Click += (o, e) => {
|
||||
DeleteTag.Show(Repo, tag);
|
||||
e.Handled = true;
|
||||
};
|
||||
submenu.Items.Add(delete);
|
||||
|
||||
return submenu;
|
||||
}
|
||||
|
||||
private void CommitContextMenuOpening(object sender, ContextMenuEventArgs ev) {
|
||||
var row = sender as DataGridRow;
|
||||
if (row == null) return;
|
||||
|
||||
var commit = row.DataContext as Git.Commit;
|
||||
if (commit == null) return;
|
||||
commitList.SelectedItem = commit;
|
||||
|
||||
var current = Repo.CurrentBranch();
|
||||
if (current == null) return;
|
||||
|
||||
var menu = new ContextMenu();
|
||||
menu.MinWidth = 200;
|
||||
|
||||
// Decorators.
|
||||
{
|
||||
var localBranchContextMenus = new List<MenuItem>();
|
||||
var remoteBranchContextMenus = new List<MenuItem>();
|
||||
var tagContextMenus = new List<MenuItem>();
|
||||
|
||||
foreach (var d in commit.Decorators) {
|
||||
if (d.Type == Git.DecoratorType.CurrentBranchHead) {
|
||||
menu.Items.Add(GetCurrentBranchContextMenu(current));
|
||||
} else if (d.Type == Git.DecoratorType.LocalBranchHead) {
|
||||
var branch = Repo.Branches().Find(b => b.Name == d.Name);
|
||||
if (branch != null) {
|
||||
localBranchContextMenus.Add(GetOtherBranchContextMenu(current, branch, commit.IsMerged));
|
||||
}
|
||||
} else if (d.Type == Git.DecoratorType.RemoteBranchHead) {
|
||||
var branch = Repo.Branches().Find(b => b.Name == d.Name);
|
||||
if (branch != null) {
|
||||
remoteBranchContextMenus.Add(GetOtherBranchContextMenu(current, branch, commit.IsMerged));
|
||||
}
|
||||
} else if (d.Type == Git.DecoratorType.Tag) {
|
||||
var tag = Repo.Tags().Find(t => t.Name == d.Name);
|
||||
if (tag != null) tagContextMenus.Add(GetTagContextMenu(tag));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var m in localBranchContextMenus) menu.Items.Add(m);
|
||||
foreach (var m in remoteBranchContextMenus) menu.Items.Add(m);
|
||||
if (menu.Items.Count > 0) menu.Items.Add(new Separator());
|
||||
|
||||
if (tagContextMenus.Count > 0) {
|
||||
foreach (var m in tagContextMenus) menu.Items.Add(m);
|
||||
menu.Items.Add(new Separator());
|
||||
}
|
||||
}
|
||||
|
||||
// Reset
|
||||
var reset = new MenuItem();
|
||||
reset.Header = $"Reset '{current.Name}' To Here";
|
||||
reset.Visibility = commit.IsHEAD ? Visibility.Collapsed : Visibility.Visible;
|
||||
reset.Click += (o, e) => {
|
||||
Reset.Show(Repo, commit);
|
||||
e.Handled = true;
|
||||
};
|
||||
menu.Items.Add(reset);
|
||||
|
||||
// Rebase or interactive rebase
|
||||
var rebase = new MenuItem();
|
||||
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) {
|
||||
if (Repo.LocalChanges().Count > 0) {
|
||||
App.RaiseError("You have local changes!!!");
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var dialog = new InteractiveRebase(Repo, commit);
|
||||
dialog.Owner = App.Current.MainWindow;
|
||||
dialog.ShowDialog();
|
||||
} else {
|
||||
Rebase.Show(Repo, commit);
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
};
|
||||
menu.Items.Add(rebase);
|
||||
|
||||
// Cherry-Pick
|
||||
var cherryPick = new MenuItem();
|
||||
cherryPick.Header = "Cherry-Pick This Commit";
|
||||
cherryPick.Visibility = commit.IsMerged ? Visibility.Collapsed : Visibility.Visible;
|
||||
cherryPick.Click += (o, e) => {
|
||||
CherryPick.Show(Repo, commit);
|
||||
e.Handled = true;
|
||||
};
|
||||
menu.Items.Add(cherryPick);
|
||||
|
||||
// Revert commit
|
||||
var revert = new MenuItem();
|
||||
revert.Header = "Revert commit";
|
||||
revert.Visibility = !commit.IsMerged ? Visibility.Collapsed : Visibility.Visible;
|
||||
revert.Click += (o, e) => {
|
||||
Revert.Show(Repo, commit);
|
||||
e.Handled = true;
|
||||
};
|
||||
menu.Items.Add(revert);
|
||||
menu.Items.Add(new Separator());
|
||||
|
||||
// Common
|
||||
var createBranch = new MenuItem();
|
||||
createBranch.Header = "Create Branch";
|
||||
createBranch.Click += (o, e) => {
|
||||
CreateBranch.Show(Repo, commit);
|
||||
e.Handled = true;
|
||||
};
|
||||
menu.Items.Add(createBranch);
|
||||
var createTag = new MenuItem();
|
||||
createTag.Header = "Create Tag";
|
||||
createTag.Click += (o, e) => {
|
||||
CreateTag.Show(Repo, commit);
|
||||
e.Handled = true;
|
||||
};
|
||||
menu.Items.Add(createTag);
|
||||
menu.Items.Add(new Separator());
|
||||
|
||||
// Save as patch
|
||||
var patch = new MenuItem();
|
||||
patch.Header = "Save As Patch";
|
||||
patch.Click += (o, e) => {
|
||||
var dialog = new System.Windows.Forms.FolderBrowserDialog();
|
||||
dialog.ShowNewFolderButton = true;
|
||||
|
||||
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
|
||||
Repo.RunCommand($"format-patch {commit.SHA} -1 -o \"{dialog.SelectedPath}\"", null);
|
||||
}
|
||||
};
|
||||
menu.Items.Add(patch);
|
||||
menu.Items.Add(new Separator());
|
||||
|
||||
// Copy SHA
|
||||
var copySHA = new MenuItem();
|
||||
copySHA.Header = "Copy Commit SHA";
|
||||
copySHA.Click += (o, e) => {
|
||||
Clipboard.SetText(commit.SHA);
|
||||
};
|
||||
menu.Items.Add(copySHA);
|
||||
|
||||
// Copy info
|
||||
var copyInfo = new MenuItem();
|
||||
copyInfo.Header = "Copy Commit Info";
|
||||
copyInfo.Click += (o, e) => {
|
||||
Clipboard.SetText(string.Format(
|
||||
"SHA: {0}\nTITLE: {1}\nAUTHOR: {2} <{3}>\nTIME: {4}",
|
||||
commit.SHA, commit.Subject, commit.Committer.Name, commit.Committer.Email, commit.Committer.Time));
|
||||
};
|
||||
menu.Items.Add(copyInfo);
|
||||
|
||||
menu.IsOpen = true;
|
||||
ev.Handled = true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region LAYOUT
|
||||
private void ChangeOrientation(object sender, RoutedEventArgs e) {
|
||||
if (commitDetailPanel == null || splitter == null || commitListPanel == null) return;
|
||||
|
||||
layout.RowDefinitions.Clear();
|
||||
layout.ColumnDefinitions.Clear();
|
||||
|
||||
if (App.Preference.UIUseHorizontalLayout) {
|
||||
layout.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star), MinWidth = 200 });
|
||||
layout.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(2) });
|
||||
layout.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star), MinWidth = 200 });
|
||||
|
||||
Grid.SetRow(commitListPanel, 0);
|
||||
Grid.SetRow(splitter, 0);
|
||||
Grid.SetRow(commitDetailPanel, 0);
|
||||
Grid.SetColumn(commitListPanel, 0);
|
||||
Grid.SetColumn(splitter, 1);
|
||||
Grid.SetColumn(commitDetailPanel, 2);
|
||||
} else {
|
||||
layout.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star), MinHeight = 100 });
|
||||
layout.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(2) });
|
||||
layout.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star), MinHeight = 100 });
|
||||
|
||||
Grid.SetRow(commitListPanel, 0);
|
||||
Grid.SetRow(splitter, 1);
|
||||
Grid.SetRow(commitDetailPanel, 2);
|
||||
Grid.SetColumn(commitListPanel, 0);
|
||||
Grid.SetColumn(splitter, 0);
|
||||
Grid.SetColumn(commitDetailPanel, 0);
|
||||
}
|
||||
|
||||
layout.InvalidateVisual();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
53
SourceGit/UI/Init.xaml
Normal file
53
SourceGit/UI/Init.xaml
Normal file
|
@ -0,0 +1,53 @@
|
|||
<UserControl x:Class="SourceGit.UI.Init"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="160" d:DesignWidth="500" Height="160" Width="500">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.ColumnSpan="2" FontWeight="DemiBold" FontSize="18" Content="Initialize Repository"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right" Content="Path :"/>
|
||||
<StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal">
|
||||
<Path Width="12" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Folder}" Margin="4,0"/>
|
||||
<Label x:Name="txtPath"/>
|
||||
</StackPanel>
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="1" Content="Invalid repository detected. Run `git init` under this path?" Foreground="{StaticResource Brush.FG2}"/>
|
||||
|
||||
<Grid Grid.Row="5" Grid.ColumnSpan="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Column="1" Click="Sure" Content="SURE" Style="{StaticResource Style.Button.AccentBordered}"/>
|
||||
<Button Grid.Column="3" Click="Cancel" Content="CANCEL" Style="{StaticResource Style.Button.Bordered}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="0" Grid.RowSpan="6" Grid.ColumnSpan="2" x:Name="status" Visibility="Collapsed" Background="{StaticResource Brush.BG1}" Opacity=".9">
|
||||
<Path x:Name="statusIcon" Width="48" Height="48" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Loading}" RenderTransformOrigin=".5,.5">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
73
SourceGit/UI/Init.xaml.cs
Normal file
73
SourceGit/UI/Init.xaml.cs
Normal file
|
@ -0,0 +1,73 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// `git init` confirm panel.
|
||||
/// </summary>
|
||||
public partial class Init : UserControl {
|
||||
private string workingDir = null;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
public Init(string path) {
|
||||
workingDir = path;
|
||||
InitializeComponent();
|
||||
txtPath.Content = path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show this dialog.
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
public static void Show(string path) {
|
||||
PopupManager.Show(new Init(path));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Do `git init`
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void Sure(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Lock();
|
||||
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
status.Visibility = Visibility.Visible;
|
||||
|
||||
await Task.Run(() => {
|
||||
var errs = Git.Repository.RunCommand(workingDir, "init -q", null);
|
||||
if (errs != null) {
|
||||
App.RaiseError(errs);
|
||||
} else {
|
||||
App.Preference.AddRepository(workingDir, "");
|
||||
}
|
||||
});
|
||||
|
||||
status.Visibility = Visibility.Collapsed;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
PopupManager.Close(true);
|
||||
|
||||
var repo = App.Preference.FindRepository(workingDir);
|
||||
if (repo != null) repo.Open();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Cancel(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Close();
|
||||
}
|
||||
}
|
||||
}
|
256
SourceGit/UI/InteractiveRebase.xaml
Normal file
256
SourceGit/UI/InteractiveRebase.xaml
Normal file
|
@ -0,0 +1,256 @@
|
|||
<Window x:Class="SourceGit.UI.InteractiveRebase"
|
||||
x:Name="me"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:SourceGit.UI"
|
||||
xmlns:helpers="clr-namespace:SourceGit.Helpers"
|
||||
mc:Ignorable="d"
|
||||
Height="600" Width="800">
|
||||
|
||||
<!-- Enable WindowChrome -->
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome UseAeroCaptionButtons="False" CornerRadius="0" CaptionHeight="32"/>
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
<!-- Window Content -->
|
||||
<Border Background="{StaticResource Brush.BG1}">
|
||||
<!-- Fix Maximize BUG -->
|
||||
<Border.Style>
|
||||
<Style TargetType="{x:Type Border}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding WindowState, ElementName=me}" Value="Maximized">
|
||||
<Setter Property="Margin" Value="6"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding WindowState, ElementName=me}" Value="Normal">
|
||||
<Setter Property="Margin" Value="0"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Border.Style>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="1"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="40"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Title bar -->
|
||||
<Grid Grid.Row="0" Background="{StaticResource Brush.BG4}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Logo & TITLE -->
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal">
|
||||
<Image
|
||||
Source="pack://application:,,,/Resources/App.png"
|
||||
Width="20" Height="20" Margin="6,-1,2,0"
|
||||
VerticalAlignment="Center"
|
||||
RenderOptions.BitmapScalingMode="HighQuality"
|
||||
WindowChrome.IsHitTestVisibleInChrome="True"
|
||||
MouseLeftButtonDown="LogoMouseButtonDown"/>
|
||||
<Label Content="SOURCE GIT - INTERACTIVE REBASE" FontWeight="Light"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Options -->
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" WindowChrome.IsHitTestVisibleInChrome="True">
|
||||
<Button Click="Minimize" Width="32" Style="{StaticResource Style.Button.HighlightHover}">
|
||||
<Path Style="{StaticResource Style.WindowControlIcon}" Data="{StaticResource Icon.Minimize}"/>
|
||||
</Button>
|
||||
<Button Click="MaximizeOrRestore" Width="32" Style="{StaticResource Style.Button.HighlightHover}">
|
||||
<Path>
|
||||
<Path.Style>
|
||||
<Style TargetType="{x:Type Path}" BasedOn="{StaticResource Style.WindowControlIcon}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding WindowState, ElementName=me}" Value="Maximized">
|
||||
<Setter Property="Data" Value="{StaticResource Icon.Restore}"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding WindowState, ElementName=me}" Value="Normal">
|
||||
<Setter Property="Data" Value="{StaticResource Icon.Maximize}"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Path.Style>
|
||||
</Path>
|
||||
</Button>
|
||||
<Button Click="Quit" Width="32">
|
||||
<Button.Style>
|
||||
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource Style.Button.HighlightHover}">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="Red"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
|
||||
<Path Width="10" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Close}"/>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Commit List -->
|
||||
<ListView Grid.Row="1"
|
||||
x:Name="commitList"
|
||||
Background="Transparent"
|
||||
Style="{StaticResource Style.ListView.Borderless}"
|
||||
ItemsSource="{Binding ElementName=me, Path=Items}"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||
SelectionChanged="CommitSelectionChanged"
|
||||
BorderThickness="0">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate DataType="{x:Type local:InteractiveRebaseItem}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="128"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ComboBox
|
||||
Grid.Column="0"
|
||||
ItemsSource="{Binding Source={x:Static local:InteractiveRebaseModeInfo.Supported}}"
|
||||
SelectedIndex="{Binding Path=Mode, Mode=TwoWay}"
|
||||
BorderThickness="0">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate DataType="{x:Type local:InteractiveRebaseModeInfo}">
|
||||
<Grid Height="20">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="20"/>
|
||||
<ColumnDefinition Width="60"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Path Grid.Column="0" Width="12" Height="12" Margin="4,0,0,0" Fill="{Binding Theme}" Style="{StaticResource Style.Icon}" Data="M 0,0 A 180,180 180 1 1 1,1 Z"/>
|
||||
<Label Grid.Column="1" Content="{Binding Title}" Padding="4,0"/>
|
||||
<Label Grid.Column="2" Content="{Binding Desc}" Foreground="{StaticResource Brush.FG2}" FontSize="11" Padding="4,0"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<Grid Grid.Column="1">
|
||||
<ContentControl x:Name="MessageEditorAnchor" MouseDoubleClick="PopupMessageEditor">
|
||||
<TextBlock Text="{Binding Subject, Mode=TwoWay}" VerticalAlignment="Center" Foreground="{StaticResource Brush.FG}"/>
|
||||
</ContentControl>
|
||||
|
||||
<Popup x:Name="MessageEditor" Placement="Bottom" IsOpen="{Binding IsEditorOpened}" VerticalOffset="1" Height="150" Width="400" PlacementTarget="{Binding ElementName=MessageEditorAnchor}">
|
||||
<Border BorderBrush="{StaticResource Brush.Accent1}" BorderThickness="1" Background="{StaticResource Brush.BG4}">
|
||||
<Grid Margin="8">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Border Grid.Row="0" BorderBrush="{StaticResource Brush.Border1}" BorderThickness="1">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="24"/>
|
||||
<RowDefinition Height="1"/>
|
||||
<RowDefinition Height="79"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBox Grid.Row="0" Text="{Binding EditSubject, Mode=TwoWay}" Height="32" Padding="2,0" helpers:TextBoxHelper.Placeholder="Enter commit subject" BorderThickness="0"/>
|
||||
<Rectangle Grid.Row="1" Height="1" Fill="{StaticResource Brush.FG}" Opacity="0.1"/>
|
||||
<TextBox Grid.Row="2" Text="{Binding EditMessage, Mode=TwoWay}" TextChanged="CommitMessageChanged" Height="79" ScrollViewer.VerticalScrollBarVisibility="Auto" Padding="2" helpers:TextBoxHelper.Placeholder="Enter commit description. Optional" helpers:TextBoxHelper.PlaceholderBaseline="Top" BorderThickness="0" AcceptsReturn="True" AcceptsTab="True" TextWrapping="Wrap"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,8,0,0">
|
||||
<Button Click="ApplyMessageEdit" Height="24" Style="{StaticResource Style.Button.AccentBordered}" BorderThickness="1" Content="APPLY" Margin="8,0"/>
|
||||
<Button Click="HideMessageEditor" Height="24" Style="{StaticResource Style.Button.Bordered}" BorderThickness="1" Content="CANCEL"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Popup>
|
||||
</Grid>
|
||||
|
||||
<TextBlock
|
||||
Grid.Column="2"
|
||||
Foreground="{StaticResource Brush.FG}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Commit.Committer.Name}"/>
|
||||
|
||||
<TextBlock
|
||||
Grid.Column="3"
|
||||
Foreground="{StaticResource Brush.FG}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Commit.Committer.Time}"/>
|
||||
|
||||
<StackPanel
|
||||
Grid.Column="4"
|
||||
Orientation="Horizontal"
|
||||
Margin="4,0">
|
||||
<Button Click="MoveUp" ToolTip="MOVE UP">
|
||||
<Path Width="12" Height="12" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.MoveUp}"/>
|
||||
</Button>
|
||||
|
||||
<Button Click="MoveDown" ToolTip="MOVE DOWN" Margin="4,0,0,0">
|
||||
<Path Width="12" Height="12" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.MoveDown}"/>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
|
||||
<!-- Loading Tip -->
|
||||
<Grid Grid.Row="1" IsHitTestVisible="False">
|
||||
<!-- Loading tip -->
|
||||
<Path x:Name="loading" Grid.ColumnSpan="5" Data="{StaticResource Icon.Loading}" RenderTransformOrigin=".5,.5" Visibility="Hidden">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
</Path.RenderTransform>
|
||||
|
||||
<Path.Style>
|
||||
<Style BasedOn="{StaticResource Style.Icon}" TargetType="{x:Type Path}">
|
||||
<Setter Property="Width" Value="48"/>
|
||||
<Setter Property="Height" Value="48"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Center"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="Fill" Value="{StaticResource Brush.FG2}"/>
|
||||
</Style>
|
||||
</Path.Style>
|
||||
</Path>
|
||||
</Grid>
|
||||
|
||||
<!-- Splitter -->
|
||||
<GridSplitter Grid.Row="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Transparent"/>
|
||||
|
||||
<!-- Commit Detail -->
|
||||
<local:CommitViewer x:Name="commitViewer" Grid.Row="3" Background="{StaticResource Brush.BG4}"/>
|
||||
|
||||
<!-- Options Bar -->
|
||||
<Grid Grid.Row="5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="4"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal" Margin="4,0,24,0">
|
||||
<Label Grid.Column="0" Content="Rebase :"/>
|
||||
<Path Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Branch}"/>
|
||||
<Label x:Name="branch"/>
|
||||
|
||||
<Label Grid.Column="2" Content="On :" Margin="8,0,0,0"/>
|
||||
<Path Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Commit}"/>
|
||||
<Label x:Name="on"/>
|
||||
</StackPanel>
|
||||
|
||||
<Button Grid.Column="1" Height="26" Click="Start" Style="{StaticResource Style.Button.AccentBordered}" Content="REBASE"/>
|
||||
<Button Grid.Column="3" Height="26" Click="Cancel" Style="{StaticResource Style.Button.Bordered}" Content="CANCEL"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Window>
|
299
SourceGit/UI/InteractiveRebase.xaml.cs
Normal file
299
SourceGit/UI/InteractiveRebase.xaml.cs
Normal file
|
@ -0,0 +1,299 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Rebase mode.
|
||||
/// </summary>
|
||||
public enum InteractiveRebaseMode {
|
||||
Pick,
|
||||
Reword,
|
||||
Squash,
|
||||
Fixup,
|
||||
Drop,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rebase mode information to display in UI.
|
||||
/// </summary>
|
||||
public class InteractiveRebaseModeInfo {
|
||||
public InteractiveRebaseMode Mode { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Desc { get; set; }
|
||||
public Brush Theme { get; set; }
|
||||
|
||||
public InteractiveRebaseModeInfo(InteractiveRebaseMode mode, string title, string desc, Brush brush) {
|
||||
Mode = mode;
|
||||
Title = title;
|
||||
Desc = desc;
|
||||
Theme = brush;
|
||||
}
|
||||
|
||||
public static List<InteractiveRebaseModeInfo> Supported = new List<InteractiveRebaseModeInfo>() {
|
||||
new InteractiveRebaseModeInfo(InteractiveRebaseMode.Pick, "Pick", "Use this commit", Brushes.Green),
|
||||
new InteractiveRebaseModeInfo(InteractiveRebaseMode.Reword, "Reword", "Edit the commit message", Brushes.Yellow),
|
||||
new InteractiveRebaseModeInfo(InteractiveRebaseMode.Squash, "Squash", "Meld into previous commit", App.Preference.UIUseLightTheme ? Brushes.Gray : Brushes.White),
|
||||
new InteractiveRebaseModeInfo(InteractiveRebaseMode.Fixup, "Fixup", "Like 'Squash' but discard log message", App.Preference.UIUseLightTheme ? Brushes.Gray : Brushes.White),
|
||||
new InteractiveRebaseModeInfo(InteractiveRebaseMode.Drop, "Drop", "Remove commit", Brushes.Red),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rebase item.
|
||||
/// </summary>
|
||||
public class InteractiveRebaseItem : INotifyPropertyChanged {
|
||||
private InteractiveRebaseMode mode = InteractiveRebaseMode.Pick;
|
||||
private bool isEditorOpened = false;
|
||||
private string editSubject = null;
|
||||
private string editMsg = null;
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public Git.Commit Commit { get; set; }
|
||||
|
||||
public int Mode {
|
||||
get { return (int)mode; }
|
||||
set {
|
||||
if (value != (int)mode) {
|
||||
mode = (InteractiveRebaseMode)value;
|
||||
PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Mode"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEditorOpened {
|
||||
get { return isEditorOpened; }
|
||||
set {
|
||||
if (value != isEditorOpened) {
|
||||
isEditorOpened = value;
|
||||
PropertyChanged.Invoke(this, new PropertyChangedEventArgs("IsEditorOpened"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string Subject {
|
||||
get { return Commit.Subject; }
|
||||
set {
|
||||
if (value != Commit.Subject) {
|
||||
Commit.Subject = value;
|
||||
PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Subject"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string EditSubject {
|
||||
get { return editSubject; }
|
||||
set {
|
||||
if (value != editMsg) {
|
||||
editSubject = value;
|
||||
PropertyChanged.Invoke(this, new PropertyChangedEventArgs("EditSubject"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string EditMessage {
|
||||
get { return editMsg; }
|
||||
set {
|
||||
if (value != editMsg) {
|
||||
editMsg = value;
|
||||
PropertyChanged.Invoke(this, new PropertyChangedEventArgs("EditMessage"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interactive rebase panel.
|
||||
/// </summary>
|
||||
public partial class InteractiveRebase : Window {
|
||||
private Git.Repository repo = null;
|
||||
private string from = null;
|
||||
|
||||
/// <summary>
|
||||
/// Edit commit list.
|
||||
/// </summary>
|
||||
public ObservableCollection<InteractiveRebaseItem> Items {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="opened"></param>
|
||||
/// <param name="start"></param>
|
||||
public InteractiveRebase(Git.Repository opened, Git.Commit start) {
|
||||
repo = opened;
|
||||
Items = new ObservableCollection<InteractiveRebaseItem>();
|
||||
from = start.ShortSHA;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
branch.Content = opened.CurrentBranch().Name;
|
||||
on.Content = $"{start.ShortSHA} {start.Subject}";
|
||||
|
||||
Task.Run(() => {
|
||||
var commits = repo.Commits($"{start.SHA}..HEAD");
|
||||
commits.Add(start);
|
||||
|
||||
Dispatcher.Invoke(() => {
|
||||
Items.Clear();
|
||||
foreach (var c in commits) Items.Add(new InteractiveRebaseItem() { Commit = c });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#region WINDOW_COMMANDS
|
||||
private void LogoMouseButtonDown(object sender, MouseButtonEventArgs e) {
|
||||
var element = e.OriginalSource as FrameworkElement;
|
||||
if (element == null) return;
|
||||
|
||||
var pos = PointToScreen(new Point(0, 33));
|
||||
SystemCommands.ShowSystemMenu(this, pos);
|
||||
}
|
||||
|
||||
private void Minimize(object sender, RoutedEventArgs e) {
|
||||
SystemCommands.MinimizeWindow(this);
|
||||
}
|
||||
|
||||
private void MaximizeOrRestore(object sender, RoutedEventArgs e) {
|
||||
if (WindowState == WindowState.Normal) {
|
||||
SystemCommands.MaximizeWindow(this);
|
||||
} else {
|
||||
SystemCommands.RestoreWindow(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void Quit(object sender, RoutedEventArgs e) {
|
||||
Close();
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void CommitSelectionChanged(object sender, SelectionChangedEventArgs e) {
|
||||
foreach (var obj in e.RemovedItems) {
|
||||
var item = obj as InteractiveRebaseItem;
|
||||
if (item != null) item.IsEditorOpened = false;
|
||||
}
|
||||
|
||||
if (e.AddedItems.Count == 1) {
|
||||
var item = e.AddedItems[0] as InteractiveRebaseItem;
|
||||
if (item != null) commitViewer.SetData(repo, item.Commit);
|
||||
}
|
||||
}
|
||||
|
||||
private void PopupMessageEditor(object sender, MouseButtonEventArgs e) {
|
||||
var item = (sender as Control).DataContext as InteractiveRebaseItem;
|
||||
if (item == null) return;
|
||||
|
||||
item.EditSubject = item.Commit.Subject;
|
||||
item.EditMessage = item.Commit.Message;
|
||||
item.IsEditorOpened = true;
|
||||
}
|
||||
|
||||
private void HideMessageEditor(object sender, RoutedEventArgs e) {
|
||||
var item = (sender as Button).DataContext as InteractiveRebaseItem;
|
||||
if (item == null) return;
|
||||
item.IsEditorOpened = false;
|
||||
}
|
||||
|
||||
private void ApplyMessageEdit(object sender, RoutedEventArgs e) {
|
||||
var item = (sender as Button).DataContext as InteractiveRebaseItem;
|
||||
if (item == null) return;
|
||||
|
||||
item.Subject = item.EditSubject;
|
||||
item.Commit.Message = item.EditMessage;
|
||||
item.Mode = (int)InteractiveRebaseMode.Reword;
|
||||
item.IsEditorOpened = false;
|
||||
}
|
||||
|
||||
private void CommitMessageChanged(object sender, TextChangedEventArgs e) {
|
||||
(sender as TextBox).ScrollToEnd();
|
||||
}
|
||||
|
||||
private void MoveUp(object sender, RoutedEventArgs e) {
|
||||
var item = (sender as Button).DataContext as InteractiveRebaseItem;
|
||||
if (item == null) return;
|
||||
|
||||
var idx = -1;
|
||||
for (int i = 0; i < Items.Count; i++) {
|
||||
if (Items[i].Commit.SHA == item.Commit.SHA) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (idx > 0) {
|
||||
Items.RemoveAt(idx);
|
||||
Items.Insert(idx - 1, item);
|
||||
}
|
||||
}
|
||||
|
||||
private void MoveDown(object sender, RoutedEventArgs e) {
|
||||
var item = (sender as Button).DataContext as InteractiveRebaseItem;
|
||||
if (item == null) return;
|
||||
|
||||
var idx = -1;
|
||||
for (int i = 0; i < Items.Count; i++) {
|
||||
if (Items[i].Commit.SHA == item.Commit.SHA) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (idx < Items.Count - 1) {
|
||||
Items.RemoveAt(idx);
|
||||
Items.Insert(idx + 1, item);
|
||||
}
|
||||
}
|
||||
|
||||
private void Start(object sender, RoutedEventArgs e) {
|
||||
var stream = new FileStream(App.InteractiveRebaseTodo, FileMode.Create);
|
||||
var writer = new StreamWriter(stream);
|
||||
|
||||
for (int i = Items.Count - 1; i >= 0; i--) {
|
||||
var item = Items[i];
|
||||
|
||||
switch ((InteractiveRebaseMode)item.Mode) {
|
||||
case InteractiveRebaseMode.Pick:
|
||||
writer.WriteLine($"pick {item.Commit.ShortSHA} {item.Subject}");
|
||||
break;
|
||||
case InteractiveRebaseMode.Reword:
|
||||
writer.WriteLine($"reword {item.Commit.ShortSHA} {item.Subject}");
|
||||
break;
|
||||
case InteractiveRebaseMode.Squash:
|
||||
writer.WriteLine($"squash {item.Commit.ShortSHA} {item.Subject}");
|
||||
break;
|
||||
case InteractiveRebaseMode.Fixup:
|
||||
writer.WriteLine($"fixup {item.Commit.ShortSHA} {item.Subject}");
|
||||
break;
|
||||
case InteractiveRebaseMode.Drop:
|
||||
writer.WriteLine($"drop {item.Commit.ShortSHA} {item.Subject}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
writer.Flush();
|
||||
stream.Flush();
|
||||
writer.Close();
|
||||
stream.Close();
|
||||
|
||||
repo.SetWatcherEnabled(false);
|
||||
var errs = repo.RunCommand($"-c sequence.editor=\"\\\"{App.InteractiveRebaseScript}\\\"\" rebase -i {from}^", null);
|
||||
repo.AssertCommand(errs);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
private void Cancel(object sender, RoutedEventArgs e) {
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
149
SourceGit/UI/Launcher.xaml
Normal file
149
SourceGit/UI/Launcher.xaml
Normal file
|
@ -0,0 +1,149 @@
|
|||
<Window x:Class="SourceGit.UI.Launcher"
|
||||
x:Name="me"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:source="clr-namespace:SourceGit"
|
||||
mc:Ignorable="d"
|
||||
MinWidth="800" MinHeight="600"
|
||||
Title="Source Git"
|
||||
Width="{Binding Source={x:Static source:App.Preference}, Path=UIMainWindowWidth, Mode=TwoWay}"
|
||||
Height="{Binding Source={x:Static source:App.Preference}, Path=UIMainWindowHeight, Mode=TwoWay}"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
|
||||
<!-- Enable WindowChrome Feature -->
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome UseAeroCaptionButtons="False" CornerRadius="0" CaptionHeight="32"/>
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
<!-- Window Layout -->
|
||||
<Border Background="{StaticResource Brush.BG1}">
|
||||
|
||||
<!-- Fix Maximize BUG -->
|
||||
<Border.Style>
|
||||
<Style TargetType="{x:Type Border}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding WindowState, ElementName=me}" Value="Maximized">
|
||||
<Setter Property="Margin" Value="6"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding WindowState, ElementName=me}" Value="Normal">
|
||||
<Setter Property="Margin" Value="0"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Border.Style>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Titlebar -->
|
||||
<Grid Grid.Row="0" Background="{StaticResource Brush.BG4}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Logo & Title -->
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal">
|
||||
<Path
|
||||
Width="20" Height="20" Margin="6,-1,2,0"
|
||||
Style="{StaticResource Style.Icon}"
|
||||
Data="{StaticResource Icon.Git}"
|
||||
Fill="#FFF05133"
|
||||
WindowChrome.IsHitTestVisibleInChrome="True"
|
||||
MouseLeftButtonDown="LogoMouseButtonDown"/>
|
||||
<Label Content="SOURCE GIT" FontWeight="Light"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Commands -->
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" WindowChrome.IsHitTestVisibleInChrome="True">
|
||||
<Button Click="ShowPreference" Width="24" ToolTip="PREFERENCE">
|
||||
<Path Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Preference}"/>
|
||||
</Button>
|
||||
<Rectangle Width="1" Height="18" Margin="6,0" VerticalAlignment="Center" Fill="{StaticResource Brush.Border1}"/>
|
||||
<Button Click="ShowAbout" Width="24" ToolTip="ABOUT">
|
||||
<Path Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Info}"/>
|
||||
</Button>
|
||||
<Rectangle Width="1" Height="18" Margin="6,0" VerticalAlignment="Center" Fill="{StaticResource Brush.Border1}"/>
|
||||
<Button Click="Minimize" Width="32" Style="{StaticResource Style.Button.HighlightHover}">
|
||||
<Path Style="{StaticResource Style.WindowControlIcon}" Data="{StaticResource Icon.Minimize}"/>
|
||||
</Button>
|
||||
<Button Click="MaximizeOrRestore" Width="32" Style="{StaticResource Style.Button.HighlightHover}">
|
||||
<Path>
|
||||
<Path.Style>
|
||||
<Style TargetType="{x:Type Path}" BasedOn="{StaticResource Style.WindowControlIcon}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding WindowState, ElementName=me}" Value="Maximized">
|
||||
<Setter Property="Data" Value="{StaticResource Icon.Restore}"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding WindowState, ElementName=me}" Value="Normal">
|
||||
<Setter Property="Data" Value="{StaticResource Icon.Maximize}"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Path.Style>
|
||||
</Path>
|
||||
</Button>
|
||||
<Button Click="Quit" Width="32">
|
||||
<Button.Style>
|
||||
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource Style.Button.HighlightHover}">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="Red"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
|
||||
<Path Width="10" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Close}"/>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Body -->
|
||||
<ContentControl Grid.Row="1" x:Name="body"/>
|
||||
|
||||
<!-- Alerts -->
|
||||
<ScrollViewer Grid.Row="1" HorizontalAlignment="Right" VerticalAlignment="Top" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
|
||||
<ItemsControl Margin="4" Width="300" Height="Auto" ItemsSource="{Binding Alerts, ElementName=me}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid x:Name="item" Height="Auto" Width="300" Margin="0,8,0,0">
|
||||
<Border Background="{StaticResource Brush.BG1}">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="8" ShadowDepth="2" Direction="270"/>
|
||||
</Border.Effect>
|
||||
</Border>
|
||||
|
||||
<Grid Margin="8">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="26"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Content="{Binding Title}" FontWeight="DemiBold"/>
|
||||
<TextBlock Grid.Row="1" Margin="6,8" Text="{Binding Message}" Foreground="{StaticResource Brush.FG}" TextWrapping="Wrap"/>
|
||||
<Button
|
||||
Grid.Row="2"
|
||||
Margin="4,0"
|
||||
Click="RemoveAlert"
|
||||
Content="CLOSE"
|
||||
Style="{StaticResource Style.Button.AccentBordered}"
|
||||
BorderBrush="{StaticResource Brush.FG}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Right"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Window>
|
140
SourceGit/UI/Launcher.xaml.cs
Normal file
140
SourceGit/UI/Launcher.xaml.cs
Normal file
|
@ -0,0 +1,140 @@
|
|||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Main window for this app.
|
||||
/// </summary>
|
||||
public partial class Launcher : Window {
|
||||
|
||||
/// <summary>
|
||||
/// Alert data.
|
||||
/// </summary>
|
||||
public class Alert {
|
||||
public string Title { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Alerts.
|
||||
/// </summary>
|
||||
public ObservableCollection<Alert> Alerts { get; set; } = new ObservableCollection<Alert>();
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public Launcher() {
|
||||
Git.Repository.OnOpen = ShowDashboard;
|
||||
Git.Repository.OnClose = ShowManager;
|
||||
|
||||
App.OnError = msg => ShowAlert(new Alert() { Title = "ERROR", Message = msg });
|
||||
|
||||
InitializeComponent();
|
||||
ShowManager();
|
||||
}
|
||||
|
||||
#region LAYOUT_CONTENT
|
||||
/// <summary>
|
||||
/// Show manager.
|
||||
/// </summary>
|
||||
private void ShowManager() {
|
||||
Dispatcher.Invoke(() => {
|
||||
body.Content = new Manager();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show dashboard.
|
||||
/// </summary>
|
||||
/// <param name="repo"></param>
|
||||
private void ShowDashboard(Git.Repository repo) {
|
||||
Dispatcher.Invoke(() => {
|
||||
if (body.Content is Dashboard) return;
|
||||
body.Content = new Dashboard(repo);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open preference dialog.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void ShowPreference(object sender, RoutedEventArgs e) {
|
||||
Preference.Show();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open about dialog.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void ShowAbout(object sender, RoutedEventArgs e) {
|
||||
var about = new About();
|
||||
about.Owner = this;
|
||||
about.ShowDialog();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show alert.
|
||||
/// </summary>
|
||||
/// <param name="alert"></param>
|
||||
private void ShowAlert(Alert alert) {
|
||||
Dispatcher.Invoke(() => Alerts.Add(alert));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove an alert.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void RemoveAlert(object sender, RoutedEventArgs e) {
|
||||
var alert = (sender as Button).DataContext as Alert;
|
||||
Alerts.Remove(alert);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region WINDOW_COMMANDS
|
||||
/// <summary>
|
||||
/// Minimize
|
||||
/// </summary>
|
||||
private void Minimize(object sender, RoutedEventArgs e) {
|
||||
SystemCommands.MinimizeWindow(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximize/Restore
|
||||
/// </summary>
|
||||
private void MaximizeOrRestore(object sender, RoutedEventArgs e) {
|
||||
if (WindowState == WindowState.Normal) {
|
||||
SystemCommands.MaximizeWindow(this);
|
||||
} else {
|
||||
SystemCommands.RestoreWindow(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quit
|
||||
/// </summary>
|
||||
private void Quit(object sender, RoutedEventArgs e) {
|
||||
App.Current.Shutdown();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show system menu when user click logo.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void LogoMouseButtonDown(object sender, MouseButtonEventArgs e) {
|
||||
var element = e.OriginalSource as FrameworkElement;
|
||||
if (element == null) return;
|
||||
|
||||
var pos = PointToScreen(new Point(0, 33));
|
||||
SystemCommands.ShowSystemMenu(this, pos);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
231
SourceGit/UI/Manager.xaml
Normal file
231
SourceGit/UI/Manager.xaml
Normal file
|
@ -0,0 +1,231 @@
|
|||
<UserControl
|
||||
x:Class="SourceGit.UI.Manager"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:SourceGit.UI"
|
||||
xmlns:git="clr-namespace:SourceGit.Git"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Toolbar -->
|
||||
<Grid Grid.Row="0" Panel.ZIndex="9999">
|
||||
<Border Background="{StaticResource Brush.BG1}">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect ShadowDepth="2" Direction="270" Opacity=".3"/>
|
||||
</Border.Effect>
|
||||
</Border>
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Navigation -->
|
||||
<StackPanel Grid.Column="0" Margin="8,0,0,0" Orientation="Horizontal">
|
||||
<Path Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Manager}"/>
|
||||
<Label Content="Repositories"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Options -->
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal">
|
||||
<Button Click="OpenOrAddRepo" Style="{StaticResource Style.Button}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Path Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Folder.Open}"/>
|
||||
<Label Content="Open Folder"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Click="CloneRepo" Style="{StaticResource Style.Button}" Margin="8,0,0,0">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Path Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Clone}"/>
|
||||
<Label Content="Clone"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<!-- Main Body -->
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="200" MinWidth="200" MaxWidth="360"/>
|
||||
<ColumnDefinition Width="2"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Left panel -->
|
||||
<Grid Grid.Column="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Recent Opened Repositories -->
|
||||
<Label Grid.Row="0" Margin="8,8,0,0" Content="RECENTLY OPENED" Style="{StaticResource Style.Label.GroupHeader}"/>
|
||||
<ListView
|
||||
x:Name="recentOpened"
|
||||
Grid.Row="1"
|
||||
Height="Auto"
|
||||
Margin="0,4"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
Style="{StaticResource Style.ListView.Borderless}"
|
||||
ItemContainerStyle="{StaticResource Style.ListViewItem.Borderless}"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
|
||||
GotFocus="RecentsGotFocus"
|
||||
SelectionChanged="RecentsSelectionChanged"
|
||||
MouseDoubleClick="RecentsMouseDoubleClick">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate DataType="{x:Type git:Repository}">
|
||||
<StackPanel Orientation="Horizontal" Height="24">
|
||||
<Path Height="12" Margin="16,0,0,0" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Git}"/>
|
||||
<Label Margin="4,0,0,0" Content="{Binding Name}"/>
|
||||
<Label FontSize="11" Content="{Binding Path}" Opacity=".5"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
|
||||
<!-- Repositories' tree -->
|
||||
<Label Grid.Row="2" Margin="8,8,0,0" Content="REPOSITORIES" Style="{StaticResource Style.Label.GroupHeader}"/>
|
||||
<TreeView
|
||||
x:Name="repositories"
|
||||
Grid.Row="3"
|
||||
Margin="0, 4"
|
||||
AllowDrop="True"
|
||||
ContextMenuOpening="TreeContextMenuOpening"
|
||||
Drop="TreeDrop"
|
||||
GotFocus="TreeGotFocus"
|
||||
MouseMove="TreeMouseMove">
|
||||
|
||||
<TreeView.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource Style.TreeView.ItemContainerStyle}">
|
||||
<Setter Property="IsExpanded" Value="{Binding IsExpended, Mode=TwoWay}"/>
|
||||
|
||||
<EventSetter Event="Selected" Handler="TreeNodeSelected"/>
|
||||
<EventSetter Event="DragOver" Handler="TreeNodeDragOver"/>
|
||||
<EventSetter Event="Drop" Handler="TreeNodeDrop"/>
|
||||
<EventSetter Event="Expanded" Handler="TreeNodeIsExpandedChanged"/>
|
||||
<EventSetter Event="Collapsed" Handler="TreeNodeIsExpandedChanged"/>
|
||||
<EventSetter Event="KeyDown" Handler="TreeNodeKeyDown"/>
|
||||
<EventSetter Event="ContextMenuOpening" Handler="TreeNodeContextMenuOpening"/>
|
||||
<EventSetter Event="MouseDoubleClick" Handler="TreeNodeDoubleClick"/>
|
||||
</Style>
|
||||
</TreeView.ItemContainerStyle>
|
||||
|
||||
<TreeView.ItemTemplate>
|
||||
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="16"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Path x:Name="icon" Grid.Column="0" Width="14" Height="14" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Folder.Fill}"/>
|
||||
<Label x:Name="name" Grid.Column="1" Margin="4,0,0,0" Content="{Binding Name}" />
|
||||
<TextBox x:Name="editName" Grid.Column="1" Margin="4,0,0,0" Text="{Binding Name}" Loaded="TreeNodeRenameStart" KeyDown="TreeNodeRenameKeyDown" LostFocus="TreeNodeRenameEnd"/>
|
||||
</Grid>
|
||||
|
||||
<HierarchicalDataTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding IsExpended}" Value="True">
|
||||
<Setter TargetName="icon" Property="Data" Value="{StaticResource Icon.Folder.Open}"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding IsRepo}" Value="True">
|
||||
<Setter TargetName="icon" Property="Data" Value="{StaticResource Icon.Git}"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding IsEditing}" Value="True">
|
||||
<Setter TargetName="name" Property="Visibility" Value="Hidden"/>
|
||||
<Setter TargetName="editName" Property="Visibility" Value="Visible"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding IsEditing}" Value="False">
|
||||
<Setter TargetName="name" Property="Visibility" Value="Visible"/>
|
||||
<Setter TargetName="editName" Property="Visibility" Value="Hidden"/>
|
||||
</DataTrigger>
|
||||
</HierarchicalDataTemplate.Triggers>
|
||||
</HierarchicalDataTemplate>
|
||||
</TreeView.ItemTemplate>
|
||||
</TreeView>
|
||||
</Grid>
|
||||
|
||||
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" Background="Transparent"/>
|
||||
|
||||
<!-- Right Panel -->
|
||||
<Grid Grid.Column="2" Background="{StaticResource Brush.BG3}">
|
||||
<!-- Brief -->
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Name & Path & OpenButton -->
|
||||
<Grid Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Grid.Column="0" x:Name="repoName" FontSize="20" FontWeight="Bold"/>
|
||||
<Label Grid.Column="1" x:Name="repoPath" FontSize="20" FontWeight="Light" Opacity="0.5" VerticalAlignment="Center"/>
|
||||
<Button Grid.Column="3" Click="OpenRepo" ToolTip="Open">
|
||||
<Path Width="20" Height="20" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Folder}"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<!-- Status of selected repository -->
|
||||
<Label Grid.Row="1" Content="STATUS" FontSize="16" Margin="0,16,0,4" FontWeight="Bold" Opacity=".8"/>
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal">
|
||||
<Label Content="Local Changes :" Opacity=".5" FontWeight="Bold"/>
|
||||
<Label x:Name="localChanges" Margin="2,0,0,0"/>
|
||||
|
||||
<Label Content="Total Commits :" Opacity=".5" FontWeight="Bold"/>
|
||||
<Label x:Name="totalCommits" Margin="2,0,0,0"/>
|
||||
|
||||
<Label Content="Last Commit :" Opacity=".5" FontWeight="Bold"/>
|
||||
<Border Background="{StaticResource Brush.BG4}" Height="18" CornerRadius="4" VerticalAlignment="Center">
|
||||
<Label x:Name="lastCommitId" Foreground="{StaticResource Brush.FG2}" FontFamily="Consolas" Padding="4,0" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<Label x:Name="lastCommit" Margin="2,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- README.md -->
|
||||
<Label Grid.Row="3" Content="README" FontSize="16" Margin="0,16,0,4" FontWeight="Bold" Opacity=".8"/>
|
||||
<Border Grid.Row="4" Margin="6,0,0,0" BorderBrush="{StaticResource Brush.BG4}" BorderThickness="1">
|
||||
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
|
||||
<TextBlock FontSize="10pt"
|
||||
FontFamily="Consolas"
|
||||
Padding="8"
|
||||
Opacity="0.8"
|
||||
Background="{StaticResource Brush.BG2}"
|
||||
Foreground="{StaticResource Brush.FG}"
|
||||
x:Name="readme"/>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<!-- Mask -->
|
||||
<Border x:Name="briefMask" Background="{StaticResource Brush.BG3}" IsHitTestVisible="False">
|
||||
<StackPanel Orientation="Vertical" VerticalAlignment="Center" Opacity=".2">
|
||||
<Path Width="160" Height="160" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Book}"/>
|
||||
<Label Margin="0,8,0,0" Content="DRAG & DROP FOLDER TO LEFT" FontSize="24" FontWeight="UltraBold" HorizontalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<!-- Popups -->
|
||||
<local:PopupManager Grid.Row="1"/>
|
||||
</Grid>
|
||||
</UserControl>
|
495
SourceGit/UI/Manager.xaml.cs
Normal file
495
SourceGit/UI/Manager.xaml.cs
Normal file
|
@ -0,0 +1,495 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Repository manager.
|
||||
/// </summary>
|
||||
public partial class Manager : UserControl {
|
||||
private TreeViewItem selectedTreeViewItem = null;
|
||||
|
||||
/// <summary>
|
||||
/// Used to build tree
|
||||
/// </summary>
|
||||
public class Node {
|
||||
public string Id { get; set; }
|
||||
public string ParentId { get; set; }
|
||||
public string Name { get; set; }
|
||||
public bool IsRepo { get; set; }
|
||||
public bool IsExpended { get; set; }
|
||||
public bool IsEditing { get; set; }
|
||||
public List<Node> Children { get; set; } = new List<Node>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public Manager() {
|
||||
Loaded += async (o, e) => {
|
||||
await Task.Delay(500);
|
||||
GC.Collect();
|
||||
};
|
||||
|
||||
InitializeComponent();
|
||||
UpdateRecentOpened();
|
||||
UpdateTree();
|
||||
}
|
||||
|
||||
#region TOOLBAR
|
||||
/// <summary>
|
||||
/// Open or add local repository.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void OpenOrAddRepo(object sender, RoutedEventArgs e) {
|
||||
var dialog = new System.Windows.Forms.FolderBrowserDialog();
|
||||
dialog.Description = "Open or init local repository";
|
||||
dialog.RootFolder = Environment.SpecialFolder.MyComputer;
|
||||
dialog.ShowNewFolderButton = true;
|
||||
|
||||
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
|
||||
CheckAndOpenRepo(dialog.SelectedPath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clone remote repository.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void CloneRepo(object sender, RoutedEventArgs e) {
|
||||
if (MakeSureReady()) Clone.Show();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region EVENT_RECENT_LISTVIEW
|
||||
private void RecentsGotFocus(object sender, RoutedEventArgs e) {
|
||||
if (selectedTreeViewItem != null) selectedTreeViewItem.IsSelected = false;
|
||||
selectedTreeViewItem = null;
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void RecentsSelectionChanged(object sender, SelectionChangedEventArgs e) {
|
||||
var recent = recentOpened.SelectedItem as Git.Repository;
|
||||
if (recent != null) ShowBrief(recent);
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void RecentsMouseDoubleClick(object sender, MouseButtonEventArgs e) {
|
||||
var list = sender as ListView;
|
||||
var recent = list.SelectedItem as Git.Repository;
|
||||
|
||||
if (recent != null) {
|
||||
CheckAndOpenRepo(recent.Path);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region EVENT_TREEVIEW
|
||||
private void TreeGotFocus(object sender, RoutedEventArgs e) {
|
||||
recentOpened.SelectedItems.Clear();
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void TreeContextMenuOpening(object sender, ContextMenuEventArgs e) {
|
||||
var addFolder = new MenuItem();
|
||||
addFolder.Header = "Add Folder";
|
||||
addFolder.Click += (o, ev) => {
|
||||
var group = App.Preference.AddGroup("New Group", "");
|
||||
UpdateTree(group.Id);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var menu = new ContextMenu();
|
||||
menu.Items.Add(addFolder);
|
||||
menu.IsOpen = true;
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void TreeMouseMove(object sender, MouseEventArgs e) {
|
||||
if (e.LeftButton != MouseButtonState.Pressed) return;
|
||||
|
||||
if (selectedTreeViewItem == null) return;
|
||||
|
||||
var node = selectedTreeViewItem.DataContext as Node;
|
||||
if (node == null || !node.IsRepo) return;
|
||||
|
||||
DragDrop.DoDragDrop(repositories, selectedTreeViewItem, DragDropEffects.Move);
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void TreeDrop(object sender, DragEventArgs e) {
|
||||
bool needRebuild = false;
|
||||
|
||||
if (e.Data.GetDataPresent(DataFormats.FileDrop)) {
|
||||
if (!MakeSureReady()) return;
|
||||
|
||||
string[] paths = e.Data.GetData(DataFormats.FileDrop) as string[];
|
||||
string group = "";
|
||||
|
||||
var node = (sender as TreeViewItem)?.DataContext as Node;
|
||||
if (node != null) group = node.IsRepo ? node.ParentId : node.Id;
|
||||
|
||||
foreach (var path in paths) {
|
||||
FileInfo info = new FileInfo(path);
|
||||
if (info.Attributes == FileAttributes.Directory && Git.Repository.IsValid(path)) {
|
||||
App.Preference.AddRepository(path, group);
|
||||
needRebuild = true;
|
||||
}
|
||||
}
|
||||
} else if (e.Data.GetDataPresent(typeof(TreeViewItem))) {
|
||||
var item = e.Data.GetData(typeof(TreeViewItem)) as TreeViewItem;
|
||||
var node = item.DataContext as Node;
|
||||
if (node == null || !node.IsRepo) return;
|
||||
|
||||
var group = "";
|
||||
var to = (sender as TreeViewItem)?.DataContext as Node;
|
||||
if (to != null) group = to.IsRepo ? to.ParentId : to.Id;
|
||||
App.Preference.FindRepository(node.Id).GroupId = group;
|
||||
needRebuild = true;
|
||||
}
|
||||
|
||||
if (needRebuild) UpdateTree();
|
||||
e.Handled = true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region EVENT_TREEVIEWITEM
|
||||
private void TreeNodeSelected(object sender, RoutedEventArgs e) {
|
||||
selectedTreeViewItem = sender as TreeViewItem;
|
||||
|
||||
var node = selectedTreeViewItem.DataContext as Node;
|
||||
if (node.IsRepo) {
|
||||
ShowBrief(App.Preference.FindRepository(node.Id));
|
||||
} else {
|
||||
HideBrief();
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void TreeNodeDoubleClick(object sender, MouseButtonEventArgs e) {
|
||||
var node = (sender as TreeViewItem).DataContext as Node;
|
||||
if (node != null && node.IsRepo) {
|
||||
CheckAndOpenRepo(node.Id);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void TreeNodeDragOver(object sender, DragEventArgs e) {
|
||||
var item = sender as TreeViewItem;
|
||||
var node = item.DataContext as Node;
|
||||
if (node != null && !node.IsRepo) item.IsExpanded = true;
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void TreeNodeDrop(object sender, DragEventArgs e) {
|
||||
TreeDrop(sender, e);
|
||||
}
|
||||
|
||||
private void TreeNodeIsExpandedChanged(object sender, RoutedEventArgs e) {
|
||||
var item = sender as TreeViewItem;
|
||||
var node = item.DataContext as Node;
|
||||
|
||||
if (node != null && !node.IsRepo) {
|
||||
var group = App.Preference.FindGroup(node.Id);
|
||||
group.IsExpended = item.IsExpanded;
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void TreeNodeKeyDown(object sender, KeyEventArgs e) {
|
||||
if (e.Key != Key.Delete) return;
|
||||
|
||||
var node = (sender as TreeViewItem).DataContext as Node;
|
||||
if (node != null) DeleteNode(node);
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void TreeNodeRenameStart(object sender, RoutedEventArgs e) {
|
||||
var text = sender as TextBox;
|
||||
if (text.IsVisible) {
|
||||
text.SelectAll();
|
||||
text.Focus();
|
||||
}
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void TreeNodeRenameKeyDown(object sender, KeyEventArgs e) {
|
||||
if (e.Key == Key.Escape) {
|
||||
UpdateTree();
|
||||
e.Handled = true;
|
||||
} else if (e.Key == Key.Enter) {
|
||||
TreeNodeRenameEnd(sender, e);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void TreeNodeRenameEnd(object sender, RoutedEventArgs e) {
|
||||
var text = sender as TextBox;
|
||||
if (string.IsNullOrWhiteSpace(text.Text)) {
|
||||
UpdateTree();
|
||||
e.Handled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var node = text.DataContext as Node;
|
||||
if (node != null) {
|
||||
if (node.IsRepo) {
|
||||
App.Preference.RenameRepository(node.Id, text.Text);
|
||||
} else {
|
||||
App.Preference.RenameGroup(node.Id, text.Text);
|
||||
}
|
||||
|
||||
UpdateRecentOpened();
|
||||
UpdateTree();
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void TreeNodeContextMenuOpening(object sender, ContextMenuEventArgs e) {
|
||||
var item = sender as TreeViewItem;
|
||||
var node = item.DataContext as Node;
|
||||
var menu = new ContextMenu();
|
||||
|
||||
if (node.IsRepo) {
|
||||
var open = new MenuItem();
|
||||
open.Header = "Open";
|
||||
open.Click += (o, ev) => {
|
||||
CheckAndOpenRepo(node.Id);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var explore = new MenuItem();
|
||||
explore.Header = "Open Container Folder";
|
||||
explore.Click += (o, ev) => {
|
||||
Process.Start(node.Id);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
menu.Items.Add(open);
|
||||
menu.Items.Add(explore);
|
||||
} else {
|
||||
var addSubFolder = new MenuItem();
|
||||
addSubFolder.Header = "Add Sub-Folder";
|
||||
addSubFolder.Click += (o, ev) => {
|
||||
var parent = App.Preference.FindGroup(node.Id);
|
||||
if (parent != null) parent.IsExpended = true;
|
||||
|
||||
var group = App.Preference.AddGroup("New Group", node.Id);
|
||||
UpdateTree(group.Id);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
menu.Items.Add(addSubFolder);
|
||||
}
|
||||
|
||||
var rename = new MenuItem();
|
||||
rename.Header = "Rename";
|
||||
rename.Click += (o, ev) => {
|
||||
UpdateTree(node.Id);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var delete = new MenuItem();
|
||||
delete.Header = "Delete";
|
||||
delete.Click += (o, ev) => {
|
||||
DeleteNode(node);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
menu.Items.Add(rename);
|
||||
menu.Items.Add(delete);
|
||||
menu.IsOpen = true;
|
||||
e.Handled = true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region EVENT_BRIEF
|
||||
private void ShowBrief(Git.Repository repo) {
|
||||
if (repo == null || !Git.Repository.IsValid(repo.Path)) {
|
||||
if (Directory.Exists(repo.Path)) {
|
||||
Init.Show(repo.Path);
|
||||
} else {
|
||||
App.RaiseError("Path is NOT valid git repository or has been removed.");
|
||||
App.Preference.RemoveRepository(repo.Path);
|
||||
UpdateRecentOpened();
|
||||
UpdateTree();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
briefMask.Visibility = Visibility.Hidden;
|
||||
|
||||
repoName.Content = repo.Name;
|
||||
repoPath.Content = repo.Path;
|
||||
|
||||
Task.Run(() => {
|
||||
var changes = repo.LocalChanges();
|
||||
var count = changes.Count;
|
||||
Dispatcher.Invoke(() => localChanges.Content = count);
|
||||
});
|
||||
|
||||
Task.Run(() => {
|
||||
var count = repo.TotalCommits();
|
||||
Dispatcher.Invoke(() => totalCommits.Content = count);
|
||||
});
|
||||
|
||||
Task.Run(() => {
|
||||
var commits = repo.Commits("-n 1");
|
||||
Dispatcher.Invoke(() => {
|
||||
if (commits.Count > 0) {
|
||||
var c = commits[0];
|
||||
lastCommitId.Content = c.ShortSHA;
|
||||
lastCommit.Content = c.Subject;
|
||||
} else {
|
||||
lastCommitId.Content = "---";
|
||||
lastCommit.Content = "";
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (File.Exists(repo.Path + "/README.md")) {
|
||||
readme.Text = File.ReadAllText(repo.Path + "/README.md");
|
||||
} else {
|
||||
readme.Text = "";
|
||||
}
|
||||
}
|
||||
|
||||
private void HideBrief() {
|
||||
briefMask.Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
private void OpenRepo(object sender, RoutedEventArgs e) {
|
||||
CheckAndOpenRepo(repoPath.Content as string);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region PRIVATES
|
||||
/// <summary>
|
||||
/// Make sure git is configured.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private bool MakeSureReady() {
|
||||
if (!App.IsGitConfigured) {
|
||||
App.RaiseError("Git has NOT been configured.\nPlease to go [Preference] and configure it first.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check and open repository
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
private void CheckAndOpenRepo(string path) {
|
||||
if (!MakeSureReady()) return;
|
||||
|
||||
if (!Git.Repository.IsValid(path)) {
|
||||
if (Directory.Exists(path)) {
|
||||
Init.Show(path);
|
||||
return;
|
||||
}
|
||||
|
||||
App.RaiseError($"Path[{path}] not exists!");
|
||||
return;
|
||||
}
|
||||
|
||||
var repo = App.Preference.AddRepository(path, "");
|
||||
repo.Open();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update recent opened repositories.
|
||||
/// </summary>
|
||||
private void UpdateRecentOpened() {
|
||||
var sorted = App.Preference.Repositories.OrderByDescending(a => a.LastOpenTime).ToList();
|
||||
var top5 = new List<Git.Repository>();
|
||||
|
||||
for (int i = 0; i < sorted.Count && i < 5; i++) {
|
||||
if (sorted[i].LastOpenTime <= 0) break;
|
||||
top5.Add(sorted[i]);
|
||||
}
|
||||
|
||||
recentOpened.ItemsSource = top5;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update tree items.
|
||||
/// </summary>
|
||||
/// <param name="editingNodeId"></param>
|
||||
private void UpdateTree(string editingNodeId = null) {
|
||||
var groupNodes = new Dictionary<string, Node>();
|
||||
var nodes = new List<Node>();
|
||||
|
||||
foreach (var group in App.Preference.Groups) {
|
||||
Node node = new Node() {
|
||||
Id = group.Id,
|
||||
ParentId = group.ParentId,
|
||||
Name = group.Name,
|
||||
IsRepo = false,
|
||||
IsExpended = group.IsExpended,
|
||||
IsEditing = group.Id == editingNodeId,
|
||||
};
|
||||
|
||||
groupNodes.Add(node.Id, node);
|
||||
}
|
||||
|
||||
nodes.Clear();
|
||||
|
||||
foreach (var kv in groupNodes) {
|
||||
if (groupNodes.ContainsKey(kv.Value.ParentId)) {
|
||||
groupNodes[kv.Value.ParentId].Children.Add(kv.Value);
|
||||
} else {
|
||||
nodes.Add(kv.Value);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var repo in App.Preference.Repositories) {
|
||||
Node node = new Node() {
|
||||
Id = repo.Path,
|
||||
ParentId = repo.GroupId,
|
||||
Name = repo.Name,
|
||||
IsRepo = true,
|
||||
IsExpended = false,
|
||||
IsEditing = repo.Path == editingNodeId,
|
||||
};
|
||||
|
||||
if (groupNodes.ContainsKey(repo.GroupId)) {
|
||||
groupNodes[repo.GroupId].Children.Add(node);
|
||||
} else {
|
||||
nodes.Add(node);
|
||||
}
|
||||
}
|
||||
|
||||
repositories.ItemsSource = nodes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete tree node.
|
||||
/// </summary>
|
||||
/// <param name="node"></param>
|
||||
private void DeleteNode(Node node) {
|
||||
if (node.IsRepo) {
|
||||
App.Preference.RemoveRepository(node.Id);
|
||||
UpdateRecentOpened();
|
||||
} else {
|
||||
App.Preference.RemoveGroup(node.Id);
|
||||
}
|
||||
|
||||
UpdateTree();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
71
SourceGit/UI/Merge.xaml
Normal file
71
SourceGit/UI/Merge.xaml
Normal file
|
@ -0,0 +1,71 @@
|
|||
<UserControl x:Class="SourceGit.UI.Merge"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:SourceGit.UI"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="192" d:DesignWidth="500" Height="192" Width="500">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.ColumnSpan="2" FontWeight="DemiBold" FontSize="18" Content="Merge Branch"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right" Content="Source Branch :"/>
|
||||
<StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal">
|
||||
<Path Width="12" Height="12" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Branch}"/>
|
||||
<Label x:Name="sourceBranch"/>
|
||||
</StackPanel>
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="0" HorizontalAlignment="Right" Content="Into :"/>
|
||||
<StackPanel Grid.Row="3" Grid.Column="1" Orientation="Horizontal">
|
||||
<Path Width="12" Height="12" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Branch}"/>
|
||||
<Label x:Name="targetBranch"/>
|
||||
</StackPanel>
|
||||
|
||||
<Label Grid.Row="4" Grid.Column="0" HorizontalAlignment="Right" Content="Merge Option :"/>
|
||||
<ComboBox x:Name="combOptions" Grid.Row="4" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Height="20">
|
||||
<Label Content="{Binding Name}" Padding="4,0"/>
|
||||
<Label Content="{Binding Desc}" Foreground="{StaticResource Brush.FG2}" FontSize="11" Padding="4,0"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<Grid Grid.Row="6" Grid.ColumnSpan="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Column="1" Click="Start" Content="SURE" Style="{StaticResource Style.Button.AccentBordered}"/>
|
||||
<Button Grid.Column="3" Click="Cancel" Content="CANCEL" Style="{StaticResource Style.Button.Bordered}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="0" Grid.RowSpan="7" Grid.ColumnSpan="2" x:Name="status" Visibility="Collapsed" Background="{StaticResource Brush.BG1}" Opacity=".9">
|
||||
<Path x:Name="statusIcon" Width="48" Height="48" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Loading}" RenderTransformOrigin=".5,.5">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
120
SourceGit/UI/Merge.xaml.cs
Normal file
120
SourceGit/UI/Merge.xaml.cs
Normal file
|
@ -0,0 +1,120 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Merge branch dialog.
|
||||
/// </summary>
|
||||
public partial class Merge : UserControl {
|
||||
private Git.Repository repo = null;
|
||||
|
||||
/// <summary>
|
||||
/// Merge option.
|
||||
/// </summary>
|
||||
public class Option {
|
||||
public string Name { get; set; }
|
||||
public string Desc { get; set; }
|
||||
public string Arg { get; set; }
|
||||
|
||||
public Option(string n, string d, string a) {
|
||||
Name = n;
|
||||
Desc = d;
|
||||
Arg = a;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="opened">Opened repository</param>
|
||||
/// <param name="source">Source branch to merge data from.</param>
|
||||
/// <param name="dest">Target branch to merge into</param>
|
||||
public Merge(Git.Repository opened, string source, string dest) {
|
||||
InitializeComponent();
|
||||
|
||||
repo = opened;
|
||||
sourceBranch.Content = source;
|
||||
targetBranch.Content = dest;
|
||||
combOptions.ItemsSource = new Option[] {
|
||||
new Option("Default", "Fast-forward if possible", ""),
|
||||
new Option("No Fast-forward", "Always create a merge commit", "--no-ff"),
|
||||
new Option("Squash", "Use '--squash'", "--squash"),
|
||||
new Option("Don't commit", "Merge without commit", "--no-commit"),
|
||||
};
|
||||
combOptions.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display this dialog.
|
||||
/// </summary>
|
||||
/// <param name="opened"></param>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="dest"></param>
|
||||
public static void Show(Git.Repository opened, string source, string dest) {
|
||||
PopupManager.Show(new Merge(opened, source, dest));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start merge directly(Fast-forward).
|
||||
/// </summary>
|
||||
/// <param name="opened"></param>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="dest"></param>
|
||||
public static void StartDirectly(Git.Repository opened, string source, string dest) {
|
||||
var merge = new Merge(opened, source, dest);
|
||||
PopupManager.Show(merge);
|
||||
PopupManager.Lock();
|
||||
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
merge.statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
merge.status.Visibility = Visibility.Visible;
|
||||
|
||||
Task.Run(() => {
|
||||
opened.Merge(source, "");
|
||||
|
||||
merge.Dispatcher.Invoke(() => {
|
||||
merge.status.Visibility = Visibility.Collapsed;
|
||||
merge.statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
PopupManager.Close(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start merge
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void Start(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Lock();
|
||||
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
status.Visibility = Visibility.Visible;
|
||||
|
||||
var branch = sourceBranch.Content as string;
|
||||
var opt = combOptions.SelectedItem as Option;
|
||||
await Task.Run(() => repo.Merge(branch, opt.Arg));
|
||||
|
||||
status.Visibility = Visibility.Collapsed;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
PopupManager.Close(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel merge.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Cancel(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Close();
|
||||
}
|
||||
}
|
||||
}
|
21
SourceGit/UI/PopupManager.xaml
Normal file
21
SourceGit/UI/PopupManager.xaml
Normal file
|
@ -0,0 +1,21 @@
|
|||
<UserControl x:Class="SourceGit.UI.PopupManager"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
Visibility="Collapsed">
|
||||
<Grid ClipToBounds="True">
|
||||
<Border Background="Transparent" MouseLeftButtonDown="Close"/>
|
||||
|
||||
<Grid HorizontalAlignment="Center" VerticalAlignment="Top" Width="Auto" Height="Auto">
|
||||
<Border Background="{StaticResource Brush.BG1}">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect ShadowDepth="1" Opacity=".5" Direction="270" BlurRadius="16"/>
|
||||
</Border.Effect>
|
||||
</Border>
|
||||
<Border x:Name="popupContent" Padding="8" Width="Auto" Height="Auto"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
95
SourceGit/UI/PopupManager.xaml.cs
Normal file
95
SourceGit/UI/PopupManager.xaml.cs
Normal file
|
@ -0,0 +1,95 @@
|
|||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Common popup manager.
|
||||
/// </summary>
|
||||
public partial class PopupManager : UserControl {
|
||||
private static PopupManager instance = null;
|
||||
private static bool locked = false;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public PopupManager() {
|
||||
instance = this;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show content as popup.
|
||||
/// </summary>
|
||||
/// <param name="elem"></param>
|
||||
public static void Show(UIElement elem) {
|
||||
if (instance == null || locked) return;
|
||||
|
||||
var gone = new Thickness(0, -(double)elem.GetValue(HeightProperty) - 16, 0, 0);
|
||||
|
||||
ThicknessAnimation anim = new ThicknessAnimation();
|
||||
anim.Duration = TimeSpan.FromMilliseconds(150);
|
||||
anim.From = gone;
|
||||
anim.To = new Thickness(0);
|
||||
|
||||
instance.popupContent.Child = elem;
|
||||
instance.popupContent.Margin = gone;
|
||||
instance.Visibility = Visibility.Visible;
|
||||
instance.popupContent.BeginAnimation(MarginProperty, anim);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is current locked.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static bool IsLocked() {
|
||||
return locked;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lock
|
||||
/// </summary>
|
||||
public static void Lock() {
|
||||
locked = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unlock
|
||||
/// </summary>
|
||||
public static void Unlock() {
|
||||
locked = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close current popup.
|
||||
/// </summary>
|
||||
/// <param name="unlockFirst"></param>
|
||||
public static void Close(bool unlockFirst = false) {
|
||||
if (instance == null) return;
|
||||
if (instance.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.Completed += (obj, ev) => {
|
||||
instance.Visibility = Visibility.Collapsed;
|
||||
instance.popupContent.Child = null;
|
||||
};
|
||||
instance.popupContent.BeginAnimation(MarginProperty, anim);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close by click blank area.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Close(object sender, RoutedEventArgs e) {
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
131
SourceGit/UI/Preference.xaml
Normal file
131
SourceGit/UI/Preference.xaml
Normal file
|
@ -0,0 +1,131 @@
|
|||
<UserControl x:Class="SourceGit.UI.Preference"
|
||||
x:Name="me"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:helpers="clr-namespace:SourceGit.Helpers"
|
||||
xmlns:app="clr-namespace:SourceGit"
|
||||
xmlns:git="clr-namespace:SourceGit.Git"
|
||||
mc:Ignorable="d"
|
||||
Height="472" Width="500">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="36"/>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="18"/>
|
||||
<RowDefinition Height="36"/>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="18"/>
|
||||
<RowDefinition Height="36"/>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="18"/>
|
||||
<RowDefinition Height="36"/>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="18"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="120"/>
|
||||
<ColumnDefinition MinWidth="200" Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- 显示 -->
|
||||
<Label Grid.Row="0" Grid.ColumnSpan="2" Content="Appearance" FontSize="18"/>
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="Light Theme :" HorizontalAlignment="Right"/>
|
||||
<CheckBox
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
IsChecked="{Binding Source={x:Static app:App.Preference}, Path=UIUseLightTheme, Mode=TwoWay}"
|
||||
Content="Restart required"
|
||||
TextElement.FontStyle="Italic"/>
|
||||
|
||||
<!-- GIT相关配置 -->
|
||||
<Label Grid.Row="3" Grid.ColumnSpan="2" Content="Git Instance" FontSize="18"/>
|
||||
<Label Grid.Row="4" Grid.Column="0" Content="Install Path :" HorizontalAlignment="Right"/>
|
||||
<Grid Grid.Row="4" Grid.Column="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="28"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox Grid.Column="0"
|
||||
x:Name="txtGitPath"
|
||||
Height="24"
|
||||
Text="{Binding Source={x:Static app:App.Preference}, Path=GitExecutable, Mode=TwoWay}"
|
||||
helpers:TextBoxHelper.Placeholder="Input path for git.exe"/>
|
||||
<Button Grid.Column="1" Width="24" Height="24" Click="SelectGitPath" Padding="0" BorderThickness="1" Style="{StaticResource Style.Button.Bordered}">
|
||||
<Path Width="14" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Folder}"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
<Label Grid.Row="5" Grid.Column="0" Content="Default Clone Dir :" HorizontalAlignment="Right"/>
|
||||
<Grid Grid.Row="5" Grid.Column="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="28"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox Grid.Column="0"
|
||||
x:Name="txtGitCloneDir"
|
||||
Height="24"
|
||||
Text="{Binding Source={x:Static app:App.Preference}, Path=GitDefaultCloneDir, Mode=TwoWay}"
|
||||
helpers:TextBoxHelper.Placeholder="Default path to clone repo into"/>
|
||||
<Button Grid.Column="1" Width="24" Height="24" Click="SelectDefaultClonePath" Padding="0" BorderThickness="1" Style="{StaticResource Style.Button.Bordered}">
|
||||
<Path Width="14" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Folder}"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<!-- Global User -->
|
||||
<Label Grid.Row="7" Grid.ColumnSpan="2" Content="Global User" FontSize="18"/>
|
||||
<Label Grid.Row="8" Grid.Column="0" Content="Name :" HorizontalAlignment="Right"/>
|
||||
<TextBox Grid.Row="8" Grid.Column="1" Height="24" helpers:TextBoxHelper.Placeholder="Global git user name" Text="{Binding ElementName=me, Path=GlobalUser, Mode=TwoWay}"/>
|
||||
<Label Grid.Row="9" Grid.Column="0" Content="Email :" HorizontalAlignment="Right"/>
|
||||
<TextBox Grid.Row="9" Grid.Column="1" Height="24" helpers:TextBoxHelper.Placeholder="Global git user email" Text="{Binding ElementName=me, Path=GlobalUserEmail, Mode=TwoWay}"/>
|
||||
|
||||
<!-- 合并工具配置 -->
|
||||
<Label Grid.Row="11" Grid.ColumnSpan="2" Content="Merge Tool" FontSize="18"/>
|
||||
<Label Grid.Row="12" Grid.Column="0" Content="Choose Merger :" HorizontalAlignment="Right"/>
|
||||
<ComboBox Grid.Row="12" Grid.Column="1"
|
||||
Height="24"
|
||||
Padding="2,0,0,0"
|
||||
HorizontalContentAlignment="Left"
|
||||
VerticalContentAlignment="Center"
|
||||
SelectedIndex="{Binding Source={x:Static app:App.Preference}, Path=MergeTool, Mode=TwoWay}"
|
||||
ItemsSource="{Binding Source={x:Static git:MergeTool.Supported}}"
|
||||
DisplayMemberPath="Name"
|
||||
SelectionChanged="ChangeMergeTool"/>
|
||||
<Label Grid.Row="13" Grid.Column="0" Content="Install Path :" HorizontalAlignment="Right"/>
|
||||
<Grid Grid.Row="13" Grid.Column="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="28"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox Grid.Column="0"
|
||||
x:Name="txtMergePath"
|
||||
Height="24"
|
||||
Text="{Binding Source={x:Static app:App.Preference}, Path=MergeExecutable, Mode=TwoWay}"
|
||||
helpers:TextBoxHelper.Placeholder="Input path for merge tool"/>
|
||||
<Button Grid.Column="1" Width="24" Height="24" Click="SelectMergeToolPath" Padding="0" BorderThickness="1" Style="{StaticResource Style.Button.Bordered}">
|
||||
<Path Width="14" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Folder}"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
<Label Grid.Row="14" Grid.Column="0" Content="Command :" HorizontalAlignment="Right"/>
|
||||
<TextBlock Grid.Row="14" Grid.Column="1"
|
||||
x:Name="txtMergeParam"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{StaticResource Brush.FG2}"/>
|
||||
|
||||
<Button Grid.Row="16" Grid.Column="1"
|
||||
Content="CLOSE"
|
||||
Click="Close"
|
||||
Width="80"
|
||||
Style="{StaticResource Style.Button.AccentBordered}"
|
||||
HorizontalAlignment="Right"/>
|
||||
</Grid>
|
||||
</UserControl>
|
178
SourceGit/UI/Preference.xaml.cs
Normal file
178
SourceGit/UI/Preference.xaml.cs
Normal file
|
@ -0,0 +1,178 @@
|
|||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Preference window.
|
||||
/// </summary>
|
||||
public partial class Preference : UserControl {
|
||||
|
||||
/// <summary>
|
||||
/// Git global user name.
|
||||
/// </summary>
|
||||
public string GlobalUser {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Git global user email.
|
||||
/// </summary>
|
||||
public string GlobalUserEmail {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public Preference() {
|
||||
GlobalUser = GetConfig("user.name");
|
||||
GlobalUserEmail = GetConfig("user.email");
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
int mergeType = App.Preference.MergeTool;
|
||||
var merger = Git.MergeTool.Supported[mergeType];
|
||||
txtMergePath.IsReadOnly = !merger.IsConfigured;
|
||||
txtMergeParam.Text = merger.Parameter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show preference.
|
||||
/// </summary>
|
||||
public static void Show() {
|
||||
PopupManager.Show(new Preference());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close this dialog
|
||||
/// </summary>
|
||||
private void Close(object sender, RoutedEventArgs e) {
|
||||
var oldUser = GetConfig("user.name");
|
||||
if (oldUser != GlobalUser) SetConfig("user.name", GlobalUser);
|
||||
|
||||
var oldEmail = GetConfig("user.email");
|
||||
if (oldEmail != GlobalUserEmail) SetConfig("user.email", GlobalUserEmail);
|
||||
|
||||
PopupManager.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Select git executable file path.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void SelectGitPath(object sender, RoutedEventArgs e) {
|
||||
var dialog = new OpenFileDialog();
|
||||
dialog.Filter = "Git Executable|git.exe";
|
||||
dialog.FileName = "git.exe";
|
||||
dialog.Title = "Select Git Executable File";
|
||||
dialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
|
||||
dialog.CheckFileExists = true;
|
||||
|
||||
if (dialog.ShowDialog() == true) {
|
||||
txtGitPath.Text = dialog.FileName;
|
||||
App.Preference.GitExecutable = dialog.FileName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set default clone path.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void SelectDefaultClonePath(object sender, RoutedEventArgs e) {
|
||||
var dialog = new System.Windows.Forms.FolderBrowserDialog();
|
||||
dialog.Description = "Select Folder To Clone Repository Into As Default";
|
||||
dialog.RootFolder = Environment.SpecialFolder.MyComputer;
|
||||
dialog.ShowNewFolderButton = true;
|
||||
|
||||
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
|
||||
txtGitCloneDir.Text = dialog.SelectedPath;
|
||||
App.Preference.GitDefaultCloneDir = dialog.SelectedPath;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Choose external merge tool.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void ChangeMergeTool(object sender, SelectionChangedEventArgs e) {
|
||||
if (IsLoaded) {
|
||||
var t = Git.MergeTool.Supported[App.Preference.MergeTool];
|
||||
|
||||
App.Preference.MergeExecutable = t.Finder();
|
||||
|
||||
txtMergePath.Text = App.Preference.MergeExecutable;
|
||||
txtMergeParam.Text = t.Parameter;
|
||||
txtMergePath.IsReadOnly = !t.IsConfigured;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set merge tool executable file path.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void SelectMergeToolPath(object sender, RoutedEventArgs e) {
|
||||
int mergeType = App.Preference.MergeTool;
|
||||
if (mergeType == 0) return;
|
||||
|
||||
var merger = Git.MergeTool.Supported[mergeType];
|
||||
var dialog = new OpenFileDialog();
|
||||
dialog.Filter = $"{merger.Name} Executable|{merger.ExecutableName}";
|
||||
dialog.Title = $"Select {merger.Name} Install Path";
|
||||
dialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
|
||||
dialog.CheckFileExists = true;
|
||||
|
||||
if (dialog.ShowDialog() == true) {
|
||||
txtMergePath.Text = dialog.FileName;
|
||||
App.Preference.MergeExecutable = dialog.FileName;
|
||||
}
|
||||
}
|
||||
|
||||
#region CONFIG
|
||||
private string GetConfig(string key) {
|
||||
if (!App.IsGitConfigured) return "";
|
||||
|
||||
var startInfo = new ProcessStartInfo();
|
||||
startInfo.FileName = App.Preference.GitExecutable;
|
||||
startInfo.Arguments = $"config --global {key}";
|
||||
startInfo.UseShellExecute = false;
|
||||
startInfo.CreateNoWindow = true;
|
||||
startInfo.RedirectStandardOutput = true;
|
||||
startInfo.StandardOutputEncoding = Encoding.UTF8;
|
||||
|
||||
var proc = new Process() { StartInfo = startInfo };
|
||||
proc.Start();
|
||||
var output = proc.StandardOutput.ReadToEnd();
|
||||
proc.WaitForExit();
|
||||
proc.Close();
|
||||
|
||||
return output.Trim();
|
||||
}
|
||||
|
||||
private void SetConfig(string key, string val) {
|
||||
if (!App.IsGitConfigured) return;
|
||||
|
||||
var startInfo = new ProcessStartInfo();
|
||||
startInfo.FileName = App.Preference.GitExecutable;
|
||||
startInfo.Arguments = $"config --global {key} \"{val}\"";
|
||||
startInfo.UseShellExecute = false;
|
||||
startInfo.CreateNoWindow = true;
|
||||
|
||||
var proc = new Process() { StartInfo = startInfo };
|
||||
proc.Start();
|
||||
proc.WaitForExit();
|
||||
proc.Close();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
100
SourceGit/UI/Pull.xaml
Normal file
100
SourceGit/UI/Pull.xaml
Normal file
|
@ -0,0 +1,100 @@
|
|||
<UserControl x:Class="SourceGit.UI.Pull"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="248" d:DesignWidth="500" Height="248" Width="500">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.ColumnSpan="2" FontWeight="DemiBold" FontSize="18" Content="Pull (Fetch & Merge)"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right" Content="Remote :"/>
|
||||
<ComboBox Grid.Row="2" Grid.Column="1"
|
||||
x:Name="combRemotes"
|
||||
VerticalAlignment="Center"
|
||||
SelectionChanged="RemotesSelectionChanged">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Height="20">
|
||||
<Path Margin="4,0,0,0" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Remote}"/>
|
||||
<Label Content="{Binding}" Padding="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="0" HorizontalAlignment="Right" Content="Branch :"/>
|
||||
<ComboBox Grid.Row="3" Grid.Column="1"
|
||||
x:Name="combBranches"
|
||||
VerticalAlignment="Center">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Height="20">
|
||||
<Path Margin="4,0,0,0" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Branch}"/>
|
||||
<Label Content="{Binding}" Padding="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<Label Grid.Row="4" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center" Content="Into :"/>
|
||||
<StackPanel Grid.Row="4" Grid.Column="1" Orientation="Horizontal">
|
||||
<Path Width="12" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Branch}"/>
|
||||
<Label x:Name="txtInto" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox Grid.Row="5" Grid.Column="1"
|
||||
x:Name="chkRebase"
|
||||
IsChecked="True"
|
||||
VerticalAlignment="Center"
|
||||
Content="Use rebase instead of merge"/>
|
||||
|
||||
<CheckBox Grid.Row="6" Grid.Column="1"
|
||||
x:Name="chkAutoStash"
|
||||
IsChecked="True"
|
||||
VerticalAlignment="Center"
|
||||
Content="Stash & reapply local changes"/>
|
||||
|
||||
<Grid Grid.Row="8" Grid.ColumnSpan="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Column="1" Click="Start" Content="SURE" Style="{StaticResource Style.Button.AccentBordered}"/>
|
||||
<Button Grid.Column="3" Click="Cancel" Content="CANCEL" Style="{StaticResource Style.Button.Bordered}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="0" Grid.RowSpan="9" Grid.ColumnSpan="2" x:Name="status" Visibility="Collapsed" Background="{StaticResource Brush.BG1}" Opacity=".9">
|
||||
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Path x:Name="statusIcon" Width="48" Height="48" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Loading}" RenderTransformOrigin=".5,.5">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
|
||||
<Label x:Name="statusMsg" Margin="0,8,0,0"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
131
SourceGit/UI/Pull.xaml.cs
Normal file
131
SourceGit/UI/Pull.xaml.cs
Normal file
|
@ -0,0 +1,131 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Git pull
|
||||
/// </summary>
|
||||
public partial class Pull : UserControl {
|
||||
private Git.Repository repo = null;
|
||||
private string preferRemote = null;
|
||||
private string preferBranch = null;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="opened">Opened repository</param>
|
||||
/// <param name="preferRemoteBranch">Prefered remote branch.</param>
|
||||
public Pull(Git.Repository opened, string preferRemoteBranch) {
|
||||
repo = opened;
|
||||
InitializeComponent();
|
||||
SetContent(preferRemoteBranch);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display git pull dialog.
|
||||
/// </summary>
|
||||
/// <param name="opened">Opened repository</param>
|
||||
/// <param name="preferRemoteBranch">Prefered remote branch</param>
|
||||
public static void Show(Git.Repository opened, string preferRemoteBranch = null) {
|
||||
PopupManager.Show(new Pull(opened, preferRemoteBranch));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set content.
|
||||
/// </summary>
|
||||
private void SetContent(string prefered) {
|
||||
var branches = repo.Branches();
|
||||
var remotes = new List<string>();
|
||||
var current = null as Git.Branch;
|
||||
|
||||
foreach (var b in branches) {
|
||||
if (b.IsLocal) {
|
||||
if (b.IsCurrent) current = b;
|
||||
} else {
|
||||
if (!remotes.Contains(b.Remote)) remotes.Add(b.Remote);
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(prefered)) {
|
||||
preferRemote = prefered.Substring(0, prefered.IndexOf('/'));
|
||||
preferBranch = prefered;
|
||||
} else if (current != null && !string.IsNullOrEmpty(current.Upstream)) {
|
||||
var upstream = current.Upstream.Substring("refs/remotes/".Length);
|
||||
preferRemote = upstream.Substring(0, upstream.IndexOf('/'));
|
||||
preferBranch = upstream;
|
||||
}
|
||||
|
||||
txtInto.Content = current.Name;
|
||||
combRemotes.ItemsSource = remotes;
|
||||
combRemotes.SelectedItem = preferRemote;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start pull
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void Start(object sender, RoutedEventArgs e) {
|
||||
var remote = combRemotes.SelectedItem as string;
|
||||
var branch = combBranches.SelectedItem as string;
|
||||
var rebase = chkRebase.IsChecked == true;
|
||||
var autoStash = chkAutoStash.IsChecked == true;
|
||||
|
||||
if (remote == null || branch == null) return;
|
||||
|
||||
PopupManager.Lock();
|
||||
|
||||
status.Visibility = Visibility.Visible;
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
|
||||
await Task.Run(() => repo.Pull(remote, branch.Substring(branch.IndexOf('/')+1), msg => Dispatcher.Invoke(() => statusMsg.Content = msg), rebase, autoStash));
|
||||
|
||||
status.Visibility = Visibility.Collapsed;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
PopupManager.Close(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Cancel(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remote selection changed event.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void RemotesSelectionChanged(object sender, SelectionChangedEventArgs e) {
|
||||
if (e.AddedItems.Count != 1) return;
|
||||
|
||||
var remote = e.AddedItems[0] as string;
|
||||
var allBranches = repo.Branches();
|
||||
var branches = new List<string>();
|
||||
|
||||
foreach (var b in allBranches) {
|
||||
if (!b.IsLocal && b.Remote == remote) {
|
||||
branches.Add(b.Name);
|
||||
}
|
||||
}
|
||||
|
||||
combBranches.ItemsSource = branches;
|
||||
if (remote == preferRemote && preferBranch != null) {
|
||||
combBranches.SelectedItem = preferBranch;
|
||||
} else {
|
||||
combBranches.SelectedIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
108
SourceGit/UI/Push.xaml
Normal file
108
SourceGit/UI/Push.xaml
Normal file
|
@ -0,0 +1,108 @@
|
|||
<UserControl x:Class="SourceGit.UI.Push"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:git="clr-namespace:SourceGit.Git"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="248" d:DesignWidth="500" Height="248" Width="500">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.ColumnSpan="2" FontWeight="DemiBold" FontSize="18" Content="Push Changes To Remote"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center" Content="Local Branch :"/>
|
||||
<ComboBox Grid.Row="2" Grid.Column="1"
|
||||
x:Name="combLocalBranches"
|
||||
VerticalAlignment="Center"
|
||||
SelectionChanged="LocalBranchesSelectionChanged">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate DataType="{x:Type git:Branch}">
|
||||
<StackPanel Orientation="Horizontal" Height="20">
|
||||
<Path Margin="4,0,0,0" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Branch}"/>
|
||||
<Label Content="{Binding Name}" Padding="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center" Content="Remote :"/>
|
||||
<ComboBox Grid.Row="3" Grid.Column="1"
|
||||
x:Name="combRemotes"
|
||||
VerticalAlignment="Center"
|
||||
SelectionChanged="RemotesSelectionChanged">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Height="20">
|
||||
<Path Margin="4,0,0,0" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Remote}"/>
|
||||
<Label Content="{Binding}" Padding="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<Label Grid.Row="4" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center" Content="To :"/>
|
||||
<ComboBox Grid.Row="4" Grid.Column="1"
|
||||
x:Name="combRemoteBranches"
|
||||
VerticalAlignment="Center">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Height="20">
|
||||
<Path Margin="4,0,0,0" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Branch}"/>
|
||||
<Label Content="{Binding}" Padding="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<CheckBox Grid.Row="5" Grid.Column="1"
|
||||
x:Name="chkTags"
|
||||
VerticalAlignment="Center"
|
||||
Content="Push all tags"/>
|
||||
|
||||
<CheckBox Grid.Row="6" Grid.Column="1"
|
||||
x:Name="chkForce"
|
||||
VerticalAlignment="Center"
|
||||
Content="Force push"/>
|
||||
|
||||
<Grid Grid.Row="8" Grid.ColumnSpan="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Column="1" Click="Start" Content="SURE" Style="{StaticResource Style.Button.AccentBordered}"/>
|
||||
<Button Grid.Column="3" Click="Cancel" Content="CANCEL" Style="{StaticResource Style.Button.Bordered}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="0" Grid.RowSpan="9" Grid.ColumnSpan="2" x:Name="status" Visibility="Collapsed" Background="{StaticResource Brush.BG1}" Opacity=".9">
|
||||
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Path x:Name="statusIcon" Width="48" Height="48" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Loading}" RenderTransformOrigin=".5,.5">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
|
||||
<Label x:Name="statusMsg" Margin="0,8,0,0"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
202
SourceGit/UI/Push.xaml.cs
Normal file
202
SourceGit/UI/Push.xaml.cs
Normal file
|
@ -0,0 +1,202 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Git push dialog
|
||||
/// </summary>
|
||||
public partial class Push : UserControl {
|
||||
private Git.Repository repo = null;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="opened">Opened repository.</param>
|
||||
/// <param name="prefer">Prefered push branch.</param>
|
||||
public Push(Git.Repository opened, Git.Branch prefer) {
|
||||
repo = opened;
|
||||
InitializeComponent();
|
||||
SetContent(prefer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show push dialog.
|
||||
/// </summary>
|
||||
/// <param name="repo"></param>
|
||||
/// <param name="prefer"></param>
|
||||
public static void Show(Git.Repository repo, Git.Branch prefer = null) {
|
||||
PopupManager.Show(new Push(repo, prefer));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show push and start directly.
|
||||
/// </summary>
|
||||
/// <param name="repo"></param>
|
||||
public static void StartDirectly(Git.Repository repo) {
|
||||
var current = repo.CurrentBranch();
|
||||
if (current == null || string.IsNullOrEmpty(current.Upstream)) {
|
||||
App.RaiseError("Current branch has no tracked upstream");
|
||||
return;
|
||||
}
|
||||
|
||||
var push = new Push(repo, current);
|
||||
PopupManager.Show(push);
|
||||
PopupManager.Lock();
|
||||
|
||||
var upstream = current.Upstream.Substring(13);
|
||||
var remoteIdx = upstream.IndexOf('/');
|
||||
var remote = upstream.Substring(0, remoteIdx);
|
||||
var remoteBranch = upstream.Substring(remoteIdx + 1);
|
||||
|
||||
push.status.Visibility = Visibility.Visible;
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
push.statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
|
||||
Task.Run(() => {
|
||||
repo.Push(remote, current.Name, remoteBranch, msg => push.Dispatcher.Invoke(() => push.statusMsg.Content = msg));
|
||||
|
||||
push.Dispatcher.Invoke(() => {
|
||||
push.status.Visibility = Visibility.Collapsed;
|
||||
push.statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
PopupManager.Close(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set content.
|
||||
/// </summary>
|
||||
private void SetContent(Git.Branch prefer) {
|
||||
var allBranches = repo.Branches();
|
||||
var localBranches = new List<Git.Branch>();
|
||||
|
||||
foreach (var b in allBranches) {
|
||||
if (b.IsLocal) {
|
||||
localBranches.Add(b);
|
||||
if (b.IsCurrent && prefer == null) prefer = b;
|
||||
}
|
||||
}
|
||||
|
||||
combLocalBranches.ItemsSource = localBranches;
|
||||
combLocalBranches.SelectedItem = prefer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start push.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void Start(object sender, RoutedEventArgs e) {
|
||||
var localBranch = combLocalBranches.SelectedItem as Git.Branch;
|
||||
var remote = combRemotes.SelectedItem as string;
|
||||
var remoteBranch = combRemoteBranches.SelectedItem as string;
|
||||
var track = string.IsNullOrEmpty(localBranch.Upstream);
|
||||
var tags = chkTags.IsChecked == true;
|
||||
var force = chkForce.IsChecked == true;
|
||||
|
||||
remoteBranch = remoteBranch.Substring($"{remote}/".Length);
|
||||
if (remoteBranch.Contains(" (new)")) {
|
||||
remoteBranch = remoteBranch.Substring(0, remoteBranch.Length - 6);
|
||||
}
|
||||
|
||||
PopupManager.Lock();
|
||||
|
||||
status.Visibility = Visibility.Visible;
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
|
||||
await Task.Run(() => repo.Push(remote, localBranch.Name, remoteBranch, msg => Dispatcher.Invoke(() => statusMsg.Content = msg), tags, track, force));
|
||||
|
||||
status.Visibility = Visibility.Collapsed;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
PopupManager.Close(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Cancel(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Local branch selection changed.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void LocalBranchesSelectionChanged(object sender, SelectionChangedEventArgs e) {
|
||||
if (e.AddedItems.Count != 1) return;
|
||||
|
||||
var current = e.AddedItems[0] as Git.Branch;
|
||||
var allRemotes = repo.Remotes();
|
||||
var remoteNames = new List<string>();
|
||||
foreach (var r in allRemotes) remoteNames.Add(r.Name);
|
||||
combRemotes.ItemsSource = null;
|
||||
combRemotes.ItemsSource = remoteNames;
|
||||
|
||||
if (!string.IsNullOrEmpty(current.Upstream)) {
|
||||
var upstream = current.Upstream.Substring("refs/remotes/".Length);
|
||||
combRemotes.SelectedItem = upstream.Substring(0, upstream.IndexOf('/'));
|
||||
} else {
|
||||
combRemotes.SelectedIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remote selection changed.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void RemotesSelectionChanged(object sender, SelectionChangedEventArgs e) {
|
||||
if (e.AddedItems.Count != 1) return;
|
||||
|
||||
var remote = e.AddedItems[0] as string;
|
||||
var allBranches = repo.Branches();
|
||||
var branches = new List<string>();
|
||||
|
||||
combRemoteBranches.ItemsSource = null;
|
||||
|
||||
foreach (var b in allBranches) {
|
||||
if (!b.IsLocal && b.Remote == remote) {
|
||||
branches.Add(b.Name);
|
||||
}
|
||||
}
|
||||
|
||||
var current = combLocalBranches.SelectedItem as Git.Branch;
|
||||
if (string.IsNullOrEmpty(current.Upstream)) {
|
||||
var newBranch = $"{remote}/{current.Name} (new)";
|
||||
branches.Add(newBranch);
|
||||
combRemoteBranches.ItemsSource = branches;
|
||||
combRemoteBranches.SelectedItem = newBranch;
|
||||
} else if (current.Upstream.StartsWith($"refs/remotes/{remote}", StringComparison.Ordinal)) {
|
||||
combRemoteBranches.ItemsSource = branches;
|
||||
combRemoteBranches.SelectedItem = current.Upstream.Substring("refs/remotes/".Length);
|
||||
} else {
|
||||
var match = $"{remote}/{current.Name}";
|
||||
foreach (var b in branches) {
|
||||
if (b == match) {
|
||||
combRemoteBranches.ItemsSource = branches;
|
||||
combRemoteBranches.SelectedItem = b;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var newBranch = $"{remote}/{current.Name} (new)";
|
||||
branches.Add(newBranch);
|
||||
combRemoteBranches.ItemsSource = branches;
|
||||
combRemoteBranches.SelectedItem = newBranch;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
65
SourceGit/UI/PushTag.xaml
Normal file
65
SourceGit/UI/PushTag.xaml
Normal file
|
@ -0,0 +1,65 @@
|
|||
<UserControl x:Class="SourceGit.UI.PushTag"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="160" d:DesignWidth="500" Height="160" Width="500">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.ColumnSpan="2" FontWeight="DemiBold" FontSize="18" Content="Push Tag To Remote"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right" Content="Tag :"/>
|
||||
<StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal">
|
||||
<Path Width="12" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Tag}" Margin="4,0"/>
|
||||
<Label x:Name="tagName"/>
|
||||
</StackPanel>
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="0" HorizontalAlignment="Right" Content="Remote :"/>
|
||||
<ComboBox Grid.Row="3" Grid.Column="1"
|
||||
x:Name="combRemotes"
|
||||
VerticalAlignment="Center">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Height="20">
|
||||
<Path Margin="4,0,0,0" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Remote}"/>
|
||||
<Label Content="{Binding Name}" Padding="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<Grid Grid.Row="5" Grid.ColumnSpan="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Column="1" Click="Start" Content="SURE" Style="{StaticResource Style.Button.AccentBordered}"/>
|
||||
<Button Grid.Column="3" Click="Cancel" Content="CANCEL" Style="{StaticResource Style.Button.Bordered}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="0" Grid.RowSpan="6" Grid.ColumnSpan="2" x:Name="status" Visibility="Collapsed" Background="{StaticResource Brush.BG1}" Opacity=".9">
|
||||
<Path x:Name="statusIcon" Width="48" Height="48" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Loading}" RenderTransformOrigin=".5,.5">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
73
SourceGit/UI/PushTag.xaml.cs
Normal file
73
SourceGit/UI/PushTag.xaml.cs
Normal file
|
@ -0,0 +1,73 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Push tag to remote dialog
|
||||
/// </summary>
|
||||
public partial class PushTag : UserControl {
|
||||
private Git.Repository repo = null;
|
||||
private Git.Tag tag = null;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="repo">Opened repo</param>
|
||||
/// <param name="tag">Delete tag</param>
|
||||
public PushTag(Git.Repository repo, Git.Tag tag) {
|
||||
this.repo = repo;
|
||||
this.tag = tag;
|
||||
|
||||
InitializeComponent();
|
||||
tagName.Content = tag.Name;
|
||||
combRemotes.ItemsSource = repo.Remotes();
|
||||
combRemotes.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display this dialog.
|
||||
/// </summary>
|
||||
/// <param name="repo"></param>
|
||||
/// <param name="tag"></param>
|
||||
public static void Show(Git.Repository repo, Git.Tag tag) {
|
||||
PopupManager.Show(new PushTag(repo, tag));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start request.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void Start(object sender, RoutedEventArgs e) {
|
||||
var remote = combRemotes.SelectedItem as Git.Remote;
|
||||
if (remote == null) return;
|
||||
|
||||
PopupManager.Lock();
|
||||
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
status.Visibility = Visibility.Visible;
|
||||
|
||||
await Task.Run(() => Git.Tag.Push(repo, tag.Name, remote.Name));
|
||||
|
||||
status.Visibility = Visibility.Collapsed;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
PopupManager.Close(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Cancel(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Close();
|
||||
}
|
||||
}
|
||||
}
|
63
SourceGit/UI/Rebase.xaml
Normal file
63
SourceGit/UI/Rebase.xaml
Normal file
|
@ -0,0 +1,63 @@
|
|||
<UserControl x:Class="SourceGit.UI.Rebase"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="192" d:DesignWidth="500" Height="192" Width="500">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.ColumnSpan="2" FontWeight="DemiBold" FontSize="18" Content="Rebase Current Branch"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right" Content="Rebase :"/>
|
||||
<StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal">
|
||||
<Path Width="12" Height="12" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Branch}"/>
|
||||
<Label x:Name="branch"/>
|
||||
</StackPanel>
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="0" HorizontalAlignment="Right" Content="On :"/>
|
||||
<StackPanel Grid.Row="3" Grid.Column="1" Orientation="Horizontal">
|
||||
<Path x:Name="type" Width="12" Height="12" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Branch}"/>
|
||||
<Label x:Name="desc"/>
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox x:Name="chkAutoStash"
|
||||
Grid.Row="4" Grid.Column="1"
|
||||
IsChecked="True"
|
||||
Content="Stash & reapply local changes"/>
|
||||
|
||||
<Grid Grid.Row="6" Grid.ColumnSpan="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Column="1" Click="Start" Content="SURE" Style="{StaticResource Style.Button.AccentBordered}"/>
|
||||
<Button Grid.Column="3" Click="Cancel" Content="CANCEL" Style="{StaticResource Style.Button.Bordered}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="0" Grid.RowSpan="7" Grid.ColumnSpan="2" x:Name="status" Visibility="Collapsed" Background="{StaticResource Brush.BG1}" Opacity=".9">
|
||||
<Path x:Name="statusIcon" Width="48" Height="48" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Loading}" RenderTransformOrigin=".5,.5">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
87
SourceGit/UI/Rebase.xaml.cs
Normal file
87
SourceGit/UI/Rebase.xaml.cs
Normal file
|
@ -0,0 +1,87 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Rebase current branch on selected commit/branch
|
||||
/// </summary>
|
||||
public partial class Rebase : UserControl {
|
||||
private Git.Repository repo = null;
|
||||
private string based = null;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="opened">Opened repository</param>
|
||||
public Rebase(Git.Repository opened) {
|
||||
repo = opened;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rebase current branch on selected branch
|
||||
/// </summary>
|
||||
/// <param name="opened"></param>
|
||||
/// <param name="branch"></param>
|
||||
public static void Show(Git.Repository opened, Git.Branch branch) {
|
||||
if (branch == null) return;
|
||||
|
||||
var current = opened.CurrentBranch();
|
||||
if (current == null) return;
|
||||
|
||||
var dialog = new Rebase(opened);
|
||||
dialog.based = branch.Head;
|
||||
dialog.branch.Content = current.Name;
|
||||
dialog.type.Data = dialog.FindResource("Icon.Branch") as Geometry;
|
||||
dialog.desc.Content = branch.Name;
|
||||
PopupManager.Show(dialog);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rebase current branch on selected commit.
|
||||
/// </summary>
|
||||
/// <param name="opened"></param>
|
||||
/// <param name="commit"></param>
|
||||
public static void Show(Git.Repository opened, Git.Commit commit) {
|
||||
var current = opened.CurrentBranch();
|
||||
if (current == null) return;
|
||||
|
||||
var dialog = new Rebase(opened);
|
||||
dialog.based = commit.SHA;
|
||||
dialog.branch.Content = current.Name;
|
||||
dialog.type.Data = dialog.FindResource("Icon.Commit") as Geometry;
|
||||
dialog.desc.Content = $"{commit.ShortSHA} {commit.Subject}";
|
||||
PopupManager.Show(dialog);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start rebase.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void Start(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Lock();
|
||||
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
status.Visibility = Visibility.Visible;
|
||||
|
||||
var autoStash = chkAutoStash.IsChecked == true;
|
||||
await Task.Run(() => repo.Rebase(based, autoStash));
|
||||
|
||||
status.Visibility = Visibility.Collapsed;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
PopupManager.Close(true);
|
||||
}
|
||||
|
||||
private void Cancel(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Close();
|
||||
}
|
||||
}
|
||||
}
|
77
SourceGit/UI/Remote.xaml
Normal file
77
SourceGit/UI/Remote.xaml
Normal file
|
@ -0,0 +1,77 @@
|
|||
<UserControl x:Class="SourceGit.UI.Remote"
|
||||
x:Name="me"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:helpers="clr-namespace:SourceGit.Helpers"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="160" d:DesignWidth="500" Height="160" Width="500">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Row="0" x:Name="title" Grid.ColumnSpan="2" FontWeight="DemiBold" FontSize="18" Content="Add Remote"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right" Content="Name :"/>
|
||||
<TextBox Grid.Row="2" Grid.Column="1"
|
||||
x:Name="txtName"
|
||||
VerticalContentAlignment="Center"
|
||||
Height="24"
|
||||
helpers:TextBoxHelper.Placeholder="Remote name">
|
||||
<TextBox.Text>
|
||||
<Binding Path="RemoteName" ElementName="me" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay">
|
||||
<Binding.ValidationRules>
|
||||
<helpers:RemoteNameRule x:Name="nameValidator"/>
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="0" HorizontalAlignment="Right" Content="Repository URL :"/>
|
||||
<TextBox Grid.Row="3" Grid.Column="1"
|
||||
x:Name="txtUrl"
|
||||
VerticalContentAlignment="Center"
|
||||
Height="24"
|
||||
helpers:TextBoxHelper.Placeholder="Remote git repository URL">
|
||||
<TextBox.Text>
|
||||
<Binding Path="RemoteUri" ElementName="me" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay">
|
||||
<Binding.ValidationRules>
|
||||
<helpers:RemoteUriRule/>
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
|
||||
<Grid Grid.Row="5" Grid.ColumnSpan="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Column="1" Click="Sure" Content="SURE" Style="{StaticResource Style.Button.AccentBordered}"/>
|
||||
<Button Grid.Column="3" Click="Cancel" Content="CANCEL" Style="{StaticResource Style.Button.Bordered}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="0" Grid.RowSpan="6" Grid.ColumnSpan="2" x:Name="status" Visibility="Collapsed" Background="{StaticResource Brush.BG1}" Opacity=".9">
|
||||
<Path x:Name="statusIcon" Width="48" Height="48" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Loading}" RenderTransformOrigin=".5,.5">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
94
SourceGit/UI/Remote.xaml.cs
Normal file
94
SourceGit/UI/Remote.xaml.cs
Normal file
|
@ -0,0 +1,94 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Create or edit remote dialog.
|
||||
/// </summary>
|
||||
public partial class Remote : UserControl {
|
||||
private Git.Repository repo = null;
|
||||
private Git.Remote remote = null;
|
||||
|
||||
public string RemoteName { get; set; }
|
||||
public string RemoteUri { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="opened">Opened repository</param>
|
||||
/// <param name="editing">Editing remote</param>
|
||||
public Remote(Git.Repository opened, Git.Remote editing) {
|
||||
repo = opened;
|
||||
remote = editing;
|
||||
|
||||
if (remote != null) {
|
||||
RemoteName = remote.Name;
|
||||
RemoteUri = remote.URL;
|
||||
}
|
||||
|
||||
InitializeComponent();
|
||||
nameValidator.Repo = repo;
|
||||
|
||||
if (remote != null) {
|
||||
title.Content = "Edit Remote";
|
||||
} else {
|
||||
title.Content = "Add New Remote";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display this dialog.
|
||||
/// </summary>
|
||||
/// <param name="opened"></param>
|
||||
/// <param name="editing"></param>
|
||||
public static void Show(Git.Repository opened, Git.Remote editing = null) {
|
||||
PopupManager.Show(new Remote(opened, editing));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Commit request.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void Sure(object sender, RoutedEventArgs e) {
|
||||
txtName.GetBindingExpression(TextBox.TextProperty).UpdateSource();
|
||||
if (Validation.GetHasError(txtName)) return;
|
||||
|
||||
txtUrl.GetBindingExpression(TextBox.TextProperty).UpdateSource();
|
||||
if (Validation.GetHasError(txtUrl)) return;
|
||||
|
||||
PopupManager.Lock();
|
||||
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
status.Visibility = Visibility.Visible;
|
||||
|
||||
await Task.Run(() => {
|
||||
if (remote != null) {
|
||||
remote.Edit(repo, RemoteName, RemoteUri);
|
||||
} else {
|
||||
Git.Remote.Add(repo, RemoteName, RemoteUri);
|
||||
}
|
||||
});
|
||||
|
||||
status.Visibility = Visibility.Collapsed;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
PopupManager.Close(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Cancel(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Close();
|
||||
}
|
||||
}
|
||||
}
|
67
SourceGit/UI/RenameBranch.xaml
Normal file
67
SourceGit/UI/RenameBranch.xaml
Normal file
|
@ -0,0 +1,67 @@
|
|||
<UserControl x:Class="SourceGit.UI.RenameBranch"
|
||||
x:Name="me"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:helpers="clr-namespace:SourceGit.Helpers"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="160" d:DesignWidth="500" Height="160" Width="500">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.ColumnSpan="2" FontWeight="DemiBold" FontSize="18" Content="Rename Branch"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right" Content="Branch :"/>
|
||||
<StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal">
|
||||
<Path Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Branch}"/>
|
||||
<Label x:Name="txtOldName"/>
|
||||
</StackPanel>
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="0" HorizontalAlignment="Right" Content="New Name :"/>
|
||||
<TextBox Grid.Row="3" Grid.Column="1"
|
||||
x:Name="txtNewName"
|
||||
Height="24"
|
||||
helpers:TextBoxHelper.Placeholder="Unique name for this branch">
|
||||
<TextBox.Text>
|
||||
<Binding Path="NewName" ElementName="me" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay">
|
||||
<Binding.ValidationRules>
|
||||
<helpers:BranchNameRule x:Name="nameValidator"/>
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
|
||||
<Grid Grid.Row="5" Grid.ColumnSpan="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Column="1" Click="Sure" Content="SURE" Style="{StaticResource Style.Button.AccentBordered}"/>
|
||||
<Button Grid.Column="3" Click="Cancel" Content="CANCEL" Style="{StaticResource Style.Button.Bordered}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="0" Grid.RowSpan="6" Grid.ColumnSpan="2" x:Name="status" Visibility="Collapsed" Background="{StaticResource Brush.BG1}" Opacity=".9">
|
||||
<Path x:Name="statusIcon" Width="48" Height="48" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Loading}" RenderTransformOrigin=".5,.5">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
79
SourceGit/UI/RenameBranch.xaml.cs
Normal file
79
SourceGit/UI/RenameBranch.xaml.cs
Normal file
|
@ -0,0 +1,79 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Rename branch dialog.
|
||||
/// </summary>
|
||||
public partial class RenameBranch : UserControl {
|
||||
private Git.Repository repo = null;
|
||||
private Git.Branch branch = null;
|
||||
|
||||
/// <summary>
|
||||
/// New branch name.
|
||||
/// </summary>
|
||||
public string NewName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="opened">Opened repository.</param>
|
||||
/// <param name="target">Branch to rename.</param>
|
||||
public RenameBranch(Git.Repository opened, Git.Branch target) {
|
||||
repo = opened;
|
||||
branch = target;
|
||||
NewName = target.Name;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
nameValidator.Repo = opened;
|
||||
txtOldName.Content = target.Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show this dialog
|
||||
/// </summary>
|
||||
/// <param name="opened"></param>
|
||||
/// <param name="branch"></param>
|
||||
public static void Show(Git.Repository opened, Git.Branch branch) {
|
||||
PopupManager.Show(new RenameBranch(opened, branch));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rename
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void Sure(object sender, RoutedEventArgs e) {
|
||||
txtNewName.GetBindingExpression(TextBox.TextProperty).UpdateSource();
|
||||
if (Validation.GetHasError(txtNewName)) return;
|
||||
|
||||
PopupManager.Lock();
|
||||
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
status.Visibility = Visibility.Visible;
|
||||
|
||||
await Task.Run(() => branch.Rename(repo, NewName));
|
||||
|
||||
status.Visibility = Visibility.Collapsed;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
PopupManager.Close(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel merge.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Cancel(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Close();
|
||||
}
|
||||
}
|
||||
}
|
72
SourceGit/UI/Reset.xaml
Normal file
72
SourceGit/UI/Reset.xaml
Normal file
|
@ -0,0 +1,72 @@
|
|||
<UserControl x:Class="SourceGit.UI.Reset"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="192" d:DesignWidth="500" Height="192" Width="500">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.ColumnSpan="2" FontWeight="DemiBold" FontSize="18" Content="Reset Current Branch To Revision"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right" Content="Current Branch :"/>
|
||||
<StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal">
|
||||
<Path Width="12" Height="12" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Branch}"/>
|
||||
<Label x:Name="branch"/>
|
||||
</StackPanel>
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="0" HorizontalAlignment="Right" Content="Move To :"/>
|
||||
<StackPanel Grid.Row="3" Grid.Column="1" Orientation="Horizontal">
|
||||
<Path Width="12" Height="12" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Commit}"/>
|
||||
<Label x:Name="desc"/>
|
||||
</StackPanel>
|
||||
|
||||
<Label Grid.Row="4" Grid.Column="0" HorizontalAlignment="Right" Content="Reset Mode :"/>
|
||||
<ComboBox x:Name="combMode" Grid.Row="4" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Height="20">
|
||||
<Path Width="12" Height="12" Margin="4,0,0,0" Fill="{Binding Color}" Style="{StaticResource Style.Icon}" Data="M 0,0 A 180,180 180 1 1 1,1 Z"/>
|
||||
<Label Content="{Binding Name}" Padding="4,0"/>
|
||||
<Label Content="{Binding Desc}" Foreground="{StaticResource Brush.FG2}" FontSize="11" Padding="4,0"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<Grid Grid.Row="6" Grid.ColumnSpan="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Column="1" Click="Start" Content="SURE" Style="{StaticResource Style.Button.AccentBordered}"/>
|
||||
<Button Grid.Column="3" Click="Cancel" Content="CANCEL" Style="{StaticResource Style.Button.Bordered}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="0" Grid.RowSpan="7" Grid.ColumnSpan="2" x:Name="status" Visibility="Collapsed" Background="{StaticResource Brush.BG1}" Opacity=".9">
|
||||
<Path x:Name="statusIcon" Width="48" Height="48" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Loading}" RenderTransformOrigin=".5,.5">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
99
SourceGit/UI/Reset.xaml.cs
Normal file
99
SourceGit/UI/Reset.xaml.cs
Normal file
|
@ -0,0 +1,99 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Reset branch to revision dialog.
|
||||
/// </summary>
|
||||
public partial class Reset : UserControl {
|
||||
private Git.Repository repo = null;
|
||||
private string revision = null;
|
||||
|
||||
/// <summary>
|
||||
/// Reset mode.
|
||||
/// </summary>
|
||||
public class Mode {
|
||||
public Brush Color { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Desc { get; set; }
|
||||
public string Arg { get; set; }
|
||||
public Mode(Brush b, string n, string d, string a) {
|
||||
Color = b;
|
||||
Name = n;
|
||||
Desc = d;
|
||||
Arg = a;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="opened"></param>
|
||||
/// <param name="current"></param>
|
||||
/// <param name="commit"></param>
|
||||
public Reset(Git.Repository opened, Git.Branch current, Git.Commit commit) {
|
||||
InitializeComponent();
|
||||
|
||||
repo = opened;
|
||||
revision = commit.SHA;
|
||||
|
||||
branch.Content = current.Name;
|
||||
desc.Content = $"{commit.ShortSHA} {commit.Subject}";
|
||||
combMode.ItemsSource = new Mode[] {
|
||||
new Mode(Brushes.Green, "Soft", "Keep all changes. Stage differences", "--soft"),
|
||||
new Mode(Brushes.Yellow, "Mixed", "Keep all changes. Unstage differences", "--mixed"),
|
||||
new Mode(Brushes.Red, "Hard", "Discard all changes", "--hard"),
|
||||
};
|
||||
combMode.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show dialog.
|
||||
/// </summary>
|
||||
/// <param name="repo"></param>
|
||||
/// <param name="commit"></param>
|
||||
public static void Show(Git.Repository repo, Git.Commit commit) {
|
||||
var current = repo.CurrentBranch();
|
||||
if (current == null) return;
|
||||
|
||||
PopupManager.Show(new Reset(repo, current, commit));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start reset.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void Start(object sender, RoutedEventArgs e) {
|
||||
var mode = combMode.SelectedItem as Mode;
|
||||
if (mode == null) return;
|
||||
|
||||
PopupManager.Lock();
|
||||
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
status.Visibility = Visibility.Visible;
|
||||
|
||||
await Task.Run(() => repo.Reset(revision, mode.Arg));
|
||||
|
||||
status.Visibility = Visibility.Collapsed;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
PopupManager.Close(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Cancel(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Close();
|
||||
}
|
||||
}
|
||||
}
|
53
SourceGit/UI/Revert.xaml
Normal file
53
SourceGit/UI/Revert.xaml
Normal file
|
@ -0,0 +1,53 @@
|
|||
<UserControl x:Class="SourceGit.UI.Revert"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="160" d:DesignWidth="500" Height="160" Width="500">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.ColumnSpan="2" FontWeight="DemiBold" FontSize="18" Content="Confirm To Revert Commit"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right" Content="Commit :"/>
|
||||
<StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal">
|
||||
<Path x:Name="icon" Width="12" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Commit}" Margin="4,0"/>
|
||||
<Label x:Name="txtDesc"/>
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox Grid.Row="3" Grid.Column="1" x:Name="chkCommit" Content="Commit revert changes" IsChecked="True"/>
|
||||
|
||||
<Grid Grid.Row="5" Grid.ColumnSpan="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Column="1" Click="Sure" Content="SURE" Style="{StaticResource Style.Button.AccentBordered}"/>
|
||||
<Button Grid.Column="3" Click="Cancel" Content="CANCEL" Style="{StaticResource Style.Button.Bordered}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="0" Grid.RowSpan="6" Grid.ColumnSpan="2" x:Name="status" Visibility="Collapsed" Background="{StaticResource Brush.BG1}" Opacity=".9">
|
||||
<Path x:Name="statusIcon" Width="48" Height="48" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Loading}" RenderTransformOrigin=".5,.5">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="0"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
79
SourceGit/UI/Revert.xaml.cs
Normal file
79
SourceGit/UI/Revert.xaml.cs
Normal file
|
@ -0,0 +1,79 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Confirm to revert selected commit.
|
||||
/// </summary>
|
||||
public partial class Revert : UserControl {
|
||||
private Git.Repository repo = null;
|
||||
private string sha = null;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="opened">Opened repository</param>
|
||||
/// <param name="commit">Commit to be reverted</param>
|
||||
public Revert(Git.Repository opened, Git.Commit commit) {
|
||||
repo = opened;
|
||||
sha = commit.SHA;
|
||||
|
||||
InitializeComponent();
|
||||
txtDesc.Content = $"{commit.ShortSHA} {commit.Subject}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open this dialog.
|
||||
/// </summary>
|
||||
/// <param name="repo"></param>
|
||||
/// <param name="commit"></param>
|
||||
public static void Show(Git.Repository repo, Git.Commit commit) {
|
||||
PopupManager.Show(new Revert(repo, commit));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start revert.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void Sure(object sender, RoutedEventArgs e) {
|
||||
bool autoCommit = chkCommit.IsChecked == true;
|
||||
|
||||
PopupManager.Lock();
|
||||
|
||||
status.Visibility = Visibility.Visible;
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
|
||||
await Task.Run(() => repo.Revert(sha, autoCommit));
|
||||
|
||||
status.Visibility = Visibility.Collapsed;
|
||||
statusIcon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
PopupManager.Close(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Cancel(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Close();
|
||||
}
|
||||
}
|
||||
}
|
46
SourceGit/UI/Stash.xaml
Normal file
46
SourceGit/UI/Stash.xaml
Normal file
|
@ -0,0 +1,46 @@
|
|||
<UserControl x:Class="SourceGit.UI.Stash"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:helpers="clr-namespace:SourceGit.Helpers"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="160" d:DesignWidth="500" Height="160" Width="500">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.ColumnSpan="2" FontWeight="DemiBold" FontSize="18" Content="Stash Local Changes"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right" Content="Message :"/>
|
||||
<TextBox Grid.Row="2" Grid.Column="1"
|
||||
x:Name="txtName"
|
||||
Height="24"
|
||||
helpers:TextBoxHelper.Placeholder="Optional. Name of this stash"/>
|
||||
|
||||
<CheckBox Grid.Row="3" Grid.Column="1" x:Name="chkIncludeUntracked" IsChecked="True" Content="Include untracked files"/>
|
||||
|
||||
<Grid Grid.Row="5" Grid.ColumnSpan="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Column="1" Click="Start" Content="SURE" Style="{StaticResource Style.Button.AccentBordered}"/>
|
||||
<Button Grid.Column="3" Click="Cancel" Content="CANCEL" Style="{StaticResource Style.Button.Bordered}"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
56
SourceGit/UI/Stash.xaml.cs
Normal file
56
SourceGit/UI/Stash.xaml.cs
Normal file
|
@ -0,0 +1,56 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Git save stash panel.
|
||||
/// </summary>
|
||||
public partial class Stash : UserControl {
|
||||
private Git.Repository repo = null;
|
||||
private List<string> files = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="repo">Opened repsitory</param>
|
||||
public Stash(Git.Repository repo, List<string> files) {
|
||||
this.repo = repo;
|
||||
this.files = files;
|
||||
InitializeComponent();
|
||||
chkIncludeUntracked.IsEnabled = files.Count == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open this dialog.
|
||||
/// </summary>
|
||||
/// <param name="repo">Opened repository</param>
|
||||
/// <param name="files">Special files to stash</param>
|
||||
public static void Show(Git.Repository repo, List<string> files) {
|
||||
PopupManager.Show(new Stash(repo, files));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start saving stash.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Start(object sender, RoutedEventArgs e) {
|
||||
bool includeUntracked = chkIncludeUntracked.IsChecked == true;
|
||||
string message = txtName.Text;
|
||||
|
||||
Git.Stash.Push(repo, includeUntracked, message, files);
|
||||
PopupManager.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Cancel(object sender, RoutedEventArgs e) {
|
||||
PopupManager.Close();
|
||||
}
|
||||
}
|
||||
}
|
134
SourceGit/UI/Stashes.xaml
Normal file
134
SourceGit/UI/Stashes.xaml
Normal file
|
@ -0,0 +1,134 @@
|
|||
<UserControl x:Class="SourceGit.UI.Stashes"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:SourceGit.UI"
|
||||
xmlns:git="clr-namespace:SourceGit.Git"
|
||||
xmlns:converters="clr-namespace:SourceGit.Converters"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
Unloaded="Cleanup">
|
||||
<Grid Background="{StaticResource Brush.BG3}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="300"/>
|
||||
<ColumnDefinition Width="1"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Left panel -->
|
||||
<Grid Grid.Column="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="1"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Stash list -->
|
||||
<Grid Grid.Row="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="24"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Label Content="STASHES" Foreground="{StaticResource Brush.FG2}" FontWeight="Bold"/>
|
||||
<ListView
|
||||
Grid.Row="1"
|
||||
x:Name="stashList"
|
||||
Background="{StaticResource Brush.BG2}" BorderThickness="0"
|
||||
SelectedIndex="-1"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
|
||||
SelectionChanged="StashSelectionChanged">
|
||||
<ListView.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource Style.ListViewItem.Borderless}">
|
||||
<Setter Property="Padding" Value="0"/>
|
||||
<EventSetter Event="ContextMenuOpening" Handler="StashContextMenuOpening"/>
|
||||
</Style>
|
||||
</ListView.ItemContainerStyle>
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate DataType="{x:Type git:Stash}">
|
||||
<Border BorderBrush="{StaticResource Brush.BG4}" BorderThickness="0,0,0,1" Padding="4">
|
||||
<StackPanel Orientation="Vertical" Margin="2" MaxWidth="290">
|
||||
<Grid TextBlock.Foreground="{StaticResource Brush.FG2}" TextBlock.FontSize="11">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Column="0" Text="{Binding SHA}"/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding Author.Time}"/>
|
||||
</Grid>
|
||||
<TextBlock MaxWidth="280" Foreground="{StaticResource Brush.FG}" Text="{Binding Message}" TextAlignment="Left" Padding="0" Margin="0,8,2,0"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Grid>
|
||||
|
||||
<!-- Splitter -->
|
||||
<GridSplitter Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="{StaticResource Brush.BG3}"/>
|
||||
|
||||
<!-- Changed file in this stash -->
|
||||
<Grid Grid.Row="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="24"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Column="0" Content="CHANGES" Foreground="{StaticResource Brush.FG2}" FontWeight="Bold"/>
|
||||
<Label Grid.Column="1" Content="Untracked files not included" Foreground="{StaticResource Brush.FG2}" FontSize="10"/>
|
||||
</Grid>
|
||||
|
||||
|
||||
<ListView
|
||||
x:Name="changeList"
|
||||
Grid.Row="1"
|
||||
Background="{StaticResource Brush.BG2}" BorderThickness="0"
|
||||
SelectedIndex="-1"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
||||
SelectionChanged="FileSelectionChanged">
|
||||
<ListView.Resources>
|
||||
<converters:FileStatusToColor x:Key="StatusColorConverter"/>
|
||||
<converters:FileStatusToIcon x:Key="StatusIconConverter"/>
|
||||
</ListView.Resources>
|
||||
<ListView.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource Style.ListViewItem.Borderless}">
|
||||
<Setter Property="Padding" Value="0"/>
|
||||
</Style>
|
||||
</ListView.ItemContainerStyle>
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate DataType="{x:Type git:Change}">
|
||||
<Grid Height="24">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="24"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Border Grid.Column="0" Width="14" Height="14" Background="{Binding ., Converter={StaticResource StatusColorConverter}}" CornerRadius="2" Margin="0,0,4,0">
|
||||
<TextBlock Text="{Binding ., Converter={StaticResource StatusIconConverter}}" Foreground="{StaticResource Brush.FG}" TextAlignment="Center" VerticalAlignment="Center" FontSize="10"/>
|
||||
</Border>
|
||||
|
||||
<TextBlock Grid.Column="1" Text="{Binding Path}" Foreground="{StaticResource Brush.FG}" VerticalAlignment="Center" FontSize="11"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<!-- Splitter -->
|
||||
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="{StaticResource Brush.BG3}"/>
|
||||
|
||||
<!-- Right Panel -->
|
||||
<local:DiffViewer Grid.Column="2" x:Name="diff"/>
|
||||
</Grid>
|
||||
</UserControl>
|
118
SourceGit/UI/Stashes.xaml.cs
Normal file
118
SourceGit/UI/Stashes.xaml.cs
Normal file
|
@ -0,0 +1,118 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SourceGit.UI {
|
||||
|
||||
/// <summary>
|
||||
/// Stashes viewer.
|
||||
/// </summary>
|
||||
public partial class Stashes : UserControl {
|
||||
private Git.Repository repo = null;
|
||||
private string selectedStash = null;
|
||||
|
||||
/// <summary>
|
||||
/// File tree node.
|
||||
/// </summary>
|
||||
public class Node {
|
||||
public string FilePath { get; set; } = "";
|
||||
public string OriginalPath { get; set; } = "";
|
||||
public string Name { get; set; } = "";
|
||||
public bool IsFile { get; set; } = false;
|
||||
public bool IsNodeExpanded { get; set; } = true;
|
||||
public Git.Change.Status Status { get; set; } = Git.Change.Status.None;
|
||||
public List<Node> Children { get; set; } = new List<Node>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public Stashes() {
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleanup
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Cleanup(object sender, RoutedEventArgs e) {
|
||||
stashList.ItemsSource = null;
|
||||
changeList.ItemsSource = null;
|
||||
diff.Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set data.
|
||||
/// </summary>
|
||||
/// <param name="opened"></param>
|
||||
/// <param name="stashes"></param>
|
||||
public void SetData(Git.Repository opened, List<Git.Stash> stashes) {
|
||||
repo = opened;
|
||||
selectedStash = null;
|
||||
stashList.ItemsSource = stashes;
|
||||
changeList.ItemsSource = null;
|
||||
diff.Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stash list selection changed event.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void StashSelectionChanged(object sender, SelectionChangedEventArgs e) {
|
||||
if (e.AddedItems.Count != 1) return;
|
||||
|
||||
var stash = e.AddedItems[0] as Git.Stash;
|
||||
if (stash == null) return;
|
||||
|
||||
selectedStash = stash.SHA;
|
||||
diff.Reset();
|
||||
changeList.ItemsSource = stash.GetChanges(repo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// File selection changed in TreeView.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void FileSelectionChanged(object sender, SelectionChangedEventArgs e) {
|
||||
if (e.AddedItems.Count != 1) return;
|
||||
|
||||
var change = e.AddedItems[0] as Git.Change;
|
||||
if (change == null) return;
|
||||
|
||||
var data = repo.Diff($"{selectedStash}^", selectedStash, change.Path, change.OriginalPath);
|
||||
diff.SetData(data, change.Path, change.OriginalPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stash context menu.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="ev"></param>
|
||||
private void StashContextMenuOpening(object sender, ContextMenuEventArgs ev) {
|
||||
var stash = (sender as ListViewItem).DataContext as Git.Stash;
|
||||
if (stash == null) return;
|
||||
|
||||
var apply = new MenuItem();
|
||||
apply.Header = "Apply";
|
||||
apply.Click += (o, e) => stash.Apply(repo);
|
||||
|
||||
var pop = new MenuItem();
|
||||
pop.Header = "Pop";
|
||||
pop.Click += (o, e) => stash.Pop(repo);
|
||||
|
||||
var delete = new MenuItem();
|
||||
delete.Header = "Drop";
|
||||
delete.Click += (o, e) => stash.Drop(repo);
|
||||
|
||||
var menu = new ContextMenu();
|
||||
menu.Items.Add(apply);
|
||||
menu.Items.Add(pop);
|
||||
menu.Items.Add(delete);
|
||||
menu.IsOpen = true;
|
||||
ev.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
379
SourceGit/UI/WorkingCopy.xaml
Normal file
379
SourceGit/UI/WorkingCopy.xaml
Normal file
|
@ -0,0 +1,379 @@
|
|||
<UserControl x:Class="SourceGit.UI.WorkingCopy"
|
||||
x:Name="me"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:source="clr-namespace:SourceGit"
|
||||
xmlns:local="clr-namespace:SourceGit.UI"
|
||||
xmlns:helpers="clr-namespace:SourceGit.Helpers"
|
||||
xmlns:converters="clr-namespace:SourceGit.Converters"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
|
||||
<UserControl.Resources>
|
||||
<converters:BoolToCollapsed x:Key="BoolToCollapsed"/>
|
||||
<converters:InverseBoolToCollapsed x:Key="InverseBoolToCollapsed"/>
|
||||
<converters:FileStatusToColor x:Key="UnstagedStatusConverter" OnlyWorkTree="True"/>
|
||||
<converters:FileStatusToIcon x:Key="UnstagedStatusIconConverter" OnlyWorkTree="True"/>
|
||||
<converters:FileStatusToColor x:Key="StagedStatusConverter"/>
|
||||
<converters:FileStatusToIcon x:Key="StagedStatusIconConverter"/>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid Background="{StaticResource Brush.BG3}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="300"/>
|
||||
<ColumnDefinition Width="1"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Left -->
|
||||
<Grid Grid.Column="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="1"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Unstaged changes -->
|
||||
<Grid Grid.Row="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="24"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0" >
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ToggleButton
|
||||
Grid.Column="0"
|
||||
x:Name="toggleUnstangedMode"
|
||||
Margin="4,0,0,0"
|
||||
ToolTip="SWITCH TO LIST/TREE VIEW"
|
||||
Style="{StaticResource Style.ToggleButton.ListOrTree}"
|
||||
IsChecked="{Binding Source={x:Static source:App.Preference}, Path=UIUseListInUnstaged, Mode=TwoWay}"/>
|
||||
<Label Grid.Column="1" Content="UNSTAGED" FontWeight="Bold" Foreground="{StaticResource Brush.FG2}"/>
|
||||
<Button Grid.Column="3" Click="Stage" Margin="4,0" ToolTip="STAGE" Background="Transparent">
|
||||
<Path Width="14" Height="14" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.StageSelected}" Fill="{StaticResource Brush.FG2}"/>
|
||||
</Button>
|
||||
<Button Grid.Column="4" Click="StageAll" Margin="4,0" ToolTip="STAGE ALL" Background="Transparent">
|
||||
<Path Width="14" Height="14" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.StageAll}" Fill="{StaticResource Brush.FG2}"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<TreeView
|
||||
Grid.Row="1"
|
||||
x:Name="unstagedTree"
|
||||
Background="{StaticResource Brush.BG2}"
|
||||
Visibility="{Binding ElementName=toggleUnstangedMode, Path=IsChecked, Converter={StaticResource InverseBoolToCollapsed}}"
|
||||
FontFamily="Consolas"
|
||||
PreviewMouseWheel="TreeMouseWheel"
|
||||
helpers:TreeViewHelper.EnableMultiSelection="True"
|
||||
helpers:TreeViewHelper.MultiSelectionChanged="UnstagedTreeMultiSelectionChanged">
|
||||
<TreeView.Resources>
|
||||
<RoutedUICommand x:Key="SelectWholeTreeCommand" Text="SelectWholeTree"/>
|
||||
</TreeView.Resources>
|
||||
|
||||
<TreeView.InputBindings>
|
||||
<KeyBinding Key="A" Modifiers="Ctrl" Command="{StaticResource SelectWholeTreeCommand}"/>
|
||||
</TreeView.InputBindings>
|
||||
|
||||
<TreeView.CommandBindings>
|
||||
<CommandBinding Command="{StaticResource SelectWholeTreeCommand}" Executed="SelectWholeTree"/>
|
||||
</TreeView.CommandBindings>
|
||||
|
||||
<TreeView.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource Style.TreeView.MultiSelectionItemContainerStyle}">
|
||||
<Setter Property="IsExpanded" Value="{Binding IsNodeExpanded, Mode=TwoWay}"/>
|
||||
<EventSetter Event="ContextMenuOpening" Handler="UnstagedTreeContextMenuOpening"/>
|
||||
</Style>
|
||||
</TreeView.ItemContainerStyle>
|
||||
<TreeView.ItemTemplate>
|
||||
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
|
||||
<StackPanel Orientation="Horizontal" Height="24">
|
||||
<Border x:Name="status" Width="14" Height="14" Visibility="Collapsed" Background="{Binding Change, Converter={StaticResource UnstagedStatusConverter}}" CornerRadius="2" Margin="0,0,4,0">
|
||||
<TextBlock Text="{Binding Change, Converter={StaticResource UnstagedStatusIconConverter}}" Foreground="{StaticResource Brush.FG}" TextAlignment="Center" VerticalAlignment="Center" FontSize="10" RenderOptions.BitmapScalingMode="HighQuality"/>
|
||||
</Border>
|
||||
<Path x:Name="icon" Width="14" Style="{StaticResource Style.Icon}" Fill="Goldenrod" Data="{StaticResource Icon.Folder.Fill}"/>
|
||||
<TextBlock Text="{Binding Name}" Foreground="{StaticResource Brush.FG}" TextAlignment="Center" VerticalAlignment="Center" Margin="4,0,0,0" FontSize="11"/>
|
||||
</StackPanel>
|
||||
|
||||
<HierarchicalDataTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding IsFile}" Value="True">
|
||||
<Setter TargetName="status" Property="Visibility" Value="Visible"/>
|
||||
<Setter TargetName="icon" Property="Visibility" Value="Collapsed"/>
|
||||
</DataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsFile}" Value="False"/>
|
||||
<Condition Binding="{Binding IsNodeExpanded}" Value="True"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter TargetName="icon" Property="Data" Value="{StaticResource Icon.Folder.Open}"/>
|
||||
</MultiDataTrigger>
|
||||
</HierarchicalDataTemplate.Triggers>
|
||||
</HierarchicalDataTemplate>
|
||||
</TreeView.ItemTemplate>
|
||||
</TreeView>
|
||||
|
||||
<DataGrid
|
||||
Grid.Row="1"
|
||||
x:Name="unstagedList"
|
||||
Visibility="{Binding ElementName=toggleUnstangedMode, Path=IsChecked, Converter={StaticResource BoolToCollapsed}}"
|
||||
RowHeight="24"
|
||||
SelectionChanged="UnstagedListSelectionChanged"
|
||||
SelectionMode="Extended"
|
||||
SelectionUnit="FullRow"
|
||||
Background="{StaticResource Brush.BG2}">
|
||||
<DataGrid.Resources>
|
||||
<Style x:Key="Style.DataGridText.VerticalCenter" TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
|
||||
<RoutedUICommand x:Key="SelectWholeDataGridCommand" Text="SelectWholeDataGrid"/>
|
||||
</DataGrid.Resources>
|
||||
|
||||
<DataGrid.InputBindings>
|
||||
<KeyBinding Key="A" Modifiers="Ctrl" Command="{StaticResource SelectWholeDataGridCommand}"/>
|
||||
</DataGrid.InputBindings>
|
||||
|
||||
<DataGrid.CommandBindings>
|
||||
<CommandBinding Command="{StaticResource SelectWholeDataGridCommand}" Executed="SelectWholeDataGrid"/>
|
||||
</DataGrid.CommandBindings>
|
||||
|
||||
<DataGrid.Columns>
|
||||
<DataGridTemplateColumn Width="22">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<Border Width="14" Height="14" x:Name="status" Background="{Binding ., Converter={StaticResource UnstagedStatusConverter}}" CornerRadius="2" Margin="2,0,4,0">
|
||||
<TextBlock Text="{Binding ., Converter={StaticResource UnstagedStatusIconConverter}}" Foreground="{StaticResource Brush.FG}" TextAlignment="Center" VerticalAlignment="Center" FontSize="8"/>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTextColumn Width="*" Binding="{Binding Path}" Foreground="{StaticResource Brush.FG}" FontFamily="Consolas" ElementStyle="{StaticResource Style.DataGridText.VerticalCenter}"/>
|
||||
</DataGrid.Columns>
|
||||
|
||||
<DataGrid.RowStyle>
|
||||
<Style TargetType="{x:Type DataGridRow}" BasedOn="{StaticResource Style.DataGridRow}">
|
||||
<EventSetter Event="ContextMenuOpening" Handler="UnstagedListContextMenuOpening"/>
|
||||
</Style>
|
||||
</DataGrid.RowStyle>
|
||||
</DataGrid>
|
||||
</Grid>
|
||||
|
||||
<!-- Splitter -->
|
||||
<GridSplitter Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Transparent"/>
|
||||
|
||||
<!-- Staged changes -->
|
||||
<Grid Grid.Row="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="24"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0" >
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ToggleButton
|
||||
Grid.Column="0"
|
||||
x:Name="toggleStagedMode"
|
||||
Margin="4,0,0,0"
|
||||
ToolTip="SWITCH TO LIST/TREE VIEW"
|
||||
Style="{StaticResource Style.ToggleButton.ListOrTree}"
|
||||
IsChecked="{Binding Source={x:Static source:App.Preference}, Path=UIUseListInStaged, Mode=TwoWay}"/>
|
||||
<Label Grid.Column="1" Content="STAGED" FontWeight="Bold" Foreground="{StaticResource Brush.FG2}"/>
|
||||
<Button Grid.Column="3" Click="Unstage" ToolTip="UNSTAGE" Margin="4,0" Background="Transparent">
|
||||
<Path Width="14" Height="14" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.UnstageSelected}" Fill="{StaticResource Brush.FG2}"/>
|
||||
</Button>
|
||||
<Button Grid.Column="4" Click="UnstageAll" ToolTip="UNSTAGE ALL" Margin="4,0" Background="Transparent">
|
||||
<Path Width="14" Height="14" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.UnstageAll}" Fill="{StaticResource Brush.FG2}"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<TreeView
|
||||
Grid.Row="1"
|
||||
x:Name="stageTree"
|
||||
Background="{StaticResource Brush.BG2}"
|
||||
Visibility="{Binding ElementName=toggleStagedMode, Path=IsChecked, Converter={StaticResource InverseBoolToCollapsed}}"
|
||||
FontFamily="Consolas"
|
||||
PreviewMouseWheel="TreeMouseWheel"
|
||||
helpers:TreeViewHelper.EnableMultiSelection="True"
|
||||
helpers:TreeViewHelper.MultiSelectionChanged="StageTreeMultiSelectionChanged">
|
||||
<TreeView.Resources>
|
||||
<RoutedUICommand x:Key="SelectWholeTreeCommand" Text="SelectWholeTree"/>
|
||||
</TreeView.Resources>
|
||||
|
||||
<TreeView.InputBindings>
|
||||
<KeyBinding Key="A" Modifiers="Ctrl" Command="{StaticResource SelectWholeTreeCommand}"/>
|
||||
</TreeView.InputBindings>
|
||||
|
||||
<TreeView.CommandBindings>
|
||||
<CommandBinding Command="{StaticResource SelectWholeTreeCommand}" Executed="SelectWholeTree"/>
|
||||
</TreeView.CommandBindings>
|
||||
|
||||
<TreeView.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource Style.TreeView.MultiSelectionItemContainerStyle}">
|
||||
<Setter Property="IsExpanded" Value="{Binding IsNodeExpanded, Mode=TwoWay}"/>
|
||||
<EventSetter Event="ContextMenuOpening" Handler="StageTreeContextMenuOpening"/>
|
||||
</Style>
|
||||
</TreeView.ItemContainerStyle>
|
||||
<TreeView.ItemTemplate>
|
||||
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
|
||||
<StackPanel Orientation="Horizontal" Height="24">
|
||||
<Border x:Name="status" Width="14" Height="14" Visibility="Collapsed" Background="{Binding Change, Converter={StaticResource StagedStatusConverter}}" CornerRadius="2" Margin="0,0,4,0">
|
||||
<TextBlock Text="{Binding Change, Converter={StaticResource StagedStatusIconConverter}}" Foreground="{StaticResource Brush.FG}" TextAlignment="Center" VerticalAlignment="Center" FontSize="10" RenderOptions.BitmapScalingMode="HighQuality"/>
|
||||
</Border>
|
||||
<Path x:Name="icon" Width="14" Style="{StaticResource Style.Icon}" Fill="Goldenrod" Data="{StaticResource Icon.Folder.Fill}"/>
|
||||
<TextBlock Text="{Binding Name}" Foreground="{StaticResource Brush.FG}" TextAlignment="Center" VerticalAlignment="Center" Margin="4,0,0,0" FontSize="11"/>
|
||||
</StackPanel>
|
||||
|
||||
<HierarchicalDataTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding IsFile}" Value="True">
|
||||
<Setter TargetName="status" Property="Visibility" Value="Visible"/>
|
||||
<Setter TargetName="icon" Property="Visibility" Value="Collapsed"/>
|
||||
</DataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsFile}" Value="False"/>
|
||||
<Condition Binding="{Binding IsNodeExpanded}" Value="True"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter TargetName="icon" Property="Data" Value="{StaticResource Icon.Folder.Open}"/>
|
||||
</MultiDataTrigger>
|
||||
</HierarchicalDataTemplate.Triggers>
|
||||
</HierarchicalDataTemplate>
|
||||
</TreeView.ItemTemplate>
|
||||
</TreeView>
|
||||
|
||||
<DataGrid
|
||||
Grid.Row="1"
|
||||
x:Name="stageList"
|
||||
Visibility="{Binding ElementName=toggleStagedMode, Path=IsChecked, Converter={StaticResource BoolToCollapsed}}"
|
||||
RowHeight="24"
|
||||
SelectionChanged="StagedListSelectionChanged"
|
||||
SelectionMode="Extended"
|
||||
SelectionUnit="FullRow"
|
||||
Background="{StaticResource Brush.BG2}">
|
||||
<DataGrid.Resources>
|
||||
<Style x:Key="Style.DataGridText.VerticalCenter" TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
|
||||
<RoutedUICommand x:Key="SelectWholeDataGridCommand" Text="SelectWholeDataGrid"/>
|
||||
</DataGrid.Resources>
|
||||
|
||||
<DataGrid.InputBindings>
|
||||
<KeyBinding Key="A" Modifiers="Ctrl" Command="{StaticResource SelectWholeDataGridCommand}"/>
|
||||
</DataGrid.InputBindings>
|
||||
|
||||
<DataGrid.CommandBindings>
|
||||
<CommandBinding Command="{StaticResource SelectWholeDataGridCommand}" Executed="SelectWholeDataGrid"/>
|
||||
</DataGrid.CommandBindings>
|
||||
|
||||
<DataGrid.Columns>
|
||||
<DataGridTemplateColumn Width="22">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<Border Width="14" Height="14" x:Name="status" Background="{Binding ., Converter={StaticResource StagedStatusConverter}}" CornerRadius="2" Margin="2,0,4,0">
|
||||
<TextBlock Text="{Binding ., Converter={StaticResource StagedStatusIconConverter}}" Foreground="{StaticResource Brush.FG}" TextAlignment="Center" VerticalAlignment="Center" FontSize="8"/>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTextColumn Width="*" Binding="{Binding Path}" Foreground="{StaticResource Brush.FG}" FontFamily="Consolas" ElementStyle="{StaticResource Style.DataGridText.VerticalCenter}"/>
|
||||
</DataGrid.Columns>
|
||||
|
||||
<DataGrid.RowStyle>
|
||||
<Style TargetType="{x:Type DataGridRow}" BasedOn="{StaticResource Style.DataGridRow}">
|
||||
<EventSetter Event="ContextMenuOpening" Handler="StagedListContextMenuOpening"/>
|
||||
</Style>
|
||||
</DataGrid.RowStyle>
|
||||
</DataGrid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<!-- Splitter -->
|
||||
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="{StaticResource Brush.BG4}"/>
|
||||
|
||||
<!-- Right -->
|
||||
<Grid Grid.Column="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="66"/>
|
||||
<RowDefinition Height="36"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Diff -->
|
||||
<local:DiffViewer Grid.Row="0" x:Name="diffViewer" Margin="8,8,8,4"/>
|
||||
|
||||
<!-- Merge -->
|
||||
<Grid Grid.Row="0" x:Name="mergePanel" Background="{StaticResource Brush.BG3}" Visibility="Collapsed">
|
||||
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Path Width="64" Height="64" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Conflict}" Margin="0,0,0,16"/>
|
||||
<Label Content="CONFLICTS DETECTED" FontSize="20" HorizontalAlignment="Center" FontWeight="DemiBold" Foreground="{StaticResource Brush.FG2}" Margin="0,0,0,24"/>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,4">
|
||||
<Button Click="UseTheirs" Style="{StaticResource Style.Button.Bordered}" Content="USE THEIRS" Height="24"/>
|
||||
<Button Click="UseMine" Style="{StaticResource Style.Button.Bordered}" Content="USE MINE" Height="24" Margin="8,0"/>
|
||||
<Button Click="OpenMergeTool" Style="{StaticResource Style.Button.Bordered}" Content="OPEN MERGE" Height="24"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Commit Message -->
|
||||
<TextBox
|
||||
x:Name="txtCommitMsg"
|
||||
Grid.Row="1"
|
||||
Height="64"
|
||||
Margin="8,0"
|
||||
Padding="2"
|
||||
AcceptsReturn="True"
|
||||
AcceptsTab="True"
|
||||
TextWrapping="Wrap"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||
helpers:TextBoxHelper.Placeholder="Enter commit message"
|
||||
helpers:TextBoxHelper.PlaceholderBaseline="Top"
|
||||
TextChanged="CommitMessageChanged">
|
||||
<TextBox.Text>
|
||||
<Binding ElementName="me" Path="CommitMessage" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
|
||||
<Binding.ValidationRules>
|
||||
<helpers:CommitSubjectRequiredRule/>
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
|
||||
<!-- Commit Options -->
|
||||
<Grid Grid.Row="2" Margin="8,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Column="0" Width="16" Height="16" Margin="0,0,8,0" Click="OpenCommitMessageSelector" ToolTip="MESSAGE HISTORIES">
|
||||
<Path Width="14" Height="14" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.List}"/>
|
||||
</Button>
|
||||
|
||||
<CheckBox Grid.Column="1" x:Name="chkAmend" HorizontalAlignment="Left" Content="Amend" Checked="StartAmend"/>
|
||||
|
||||
<Button Grid.Column="3" Height="26" Click="Commit" Style="{StaticResource Style.Button.AccentBordered}" Content="COMMIT"/>
|
||||
<Button Grid.Column="4" x:Name="btnCommitAndPush" Height="26" Click="CommitAndPush" Style="{StaticResource Style.Button.Bordered}" Content="COMMIT & PUSH" Margin="8,0,0,0"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
1027
SourceGit/UI/WorkingCopy.xaml.cs
Normal file
1027
SourceGit/UI/WorkingCopy.xaml.cs
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue