mirror of
https://github.com/sourcegit-scm/sourcegit
synced 2025-05-28 15:45:00 +00:00
refactor<*>: rewrite all codes...
This commit is contained in:
parent
89ff8aa744
commit
30ab8ae954
342 changed files with 17208 additions and 19633 deletions
88
src/Views/About.xaml
Normal file
88
src/Views/About.xaml
Normal file
|
@ -0,0 +1,88 @@
|
|||
<Window x:Class="SourceGit.Views.About"
|
||||
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:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
mc:Ignorable="d"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
Title="{StaticResource Text.About}"
|
||||
Height="280" Width="400"
|
||||
ResizeMode="NoResize">
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome UseAeroCaptionButtons="False" CornerRadius="0" CaptionHeight="28" ResizeBorderThickness="1"/>
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
<controls:WindowBorder>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Title bar -->
|
||||
<Grid Grid.Row="0" Background="{StaticResource Brush.TitleBar}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Icon -->
|
||||
<Path Grid.Column="0" Margin="6,0" Width="16" Height="16" Data="{StaticResource Icon.Help}"/>
|
||||
|
||||
<!-- Title -->
|
||||
<TextBlock Grid.Column="1" Text="{StaticResource Text.About}"/>
|
||||
|
||||
<!-- Close -->
|
||||
<controls:IconButton
|
||||
Grid.Column="3"
|
||||
Click="Quit"
|
||||
Padding="9"
|
||||
Icon="{StaticResource Icon.Close}"
|
||||
HoverBackground="Red"
|
||||
WindowChrome.IsHitTestVisibleInChrome="True"/>
|
||||
</Grid>
|
||||
|
||||
<!-- Content -->
|
||||
<StackPanel Grid.Row="1" Orientation="Vertical">
|
||||
|
||||
<!-- LOGO -->
|
||||
<Path
|
||||
Margin="0,16,0,0"
|
||||
Width="64" Height="64"
|
||||
Data="{StaticResource Icon.Git}"
|
||||
Fill="{StaticResource Brush.Logo}"/>
|
||||
|
||||
<!-- Title -->
|
||||
<TextBlock
|
||||
Margin="0,24,0,8"
|
||||
Text="{StaticResource Text.About.Title}"
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="18" FontWeight="Bold"/>
|
||||
|
||||
<!-- Version -->
|
||||
<TextBlock
|
||||
x:Name="version"
|
||||
Margin="0,0,0,8"
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="11"
|
||||
Text="VERSION: v1.0 .NET: v5.0"/>
|
||||
|
||||
<!-- Official site -->
|
||||
<TextBlock HorizontalAlignment="Center" Margin="0,16,0,4">
|
||||
<Hyperlink NavigateUri="https://gitee.com/sourcegit/SourceGit.git" RequestNavigate="OnRequestNavigate">
|
||||
<Run Text="https://gitee.com/sourcegit/SourceGit.git"/>
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
|
||||
<!-- Copyrights -->
|
||||
<TextBlock
|
||||
Margin="0,4"
|
||||
Text="Copyright © sourcegit 2021. All rights reserved."
|
||||
HorizontalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</controls:WindowBorder>
|
||||
</Window>
|
33
src/Views/About.xaml.cs
Normal file
33
src/Views/About.xaml.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
|
||||
namespace SourceGit.Views {
|
||||
|
||||
/// <summary>
|
||||
/// 关于对话框
|
||||
/// </summary>
|
||||
public partial class About : Window {
|
||||
|
||||
public About() {
|
||||
InitializeComponent();
|
||||
|
||||
var asm = Assembly.GetExecutingAssembly().GetName();
|
||||
var framework = AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName;
|
||||
var dotnetVer = framework.Substring(framework.IndexOf("=") + 1);
|
||||
|
||||
version.Text = $"VERSION : v{asm.Version.Major}.{asm.Version.Minor} .NET : {dotnetVer}";
|
||||
}
|
||||
|
||||
private void OnRequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e) {
|
||||
var info = new ProcessStartInfo("cmd", $"/c start {e.Uri.AbsoluteUri}");
|
||||
info.CreateNoWindow = true;
|
||||
Process.Start(info);
|
||||
}
|
||||
|
||||
private void Quit(object sender, RoutedEventArgs e) {
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
136
src/Views/Blame.xaml
Normal file
136
src/Views/Blame.xaml
Normal file
|
@ -0,0 +1,136 @@
|
|||
<Window x:Class="SourceGit.Views.Blame"
|
||||
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:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
mc:Ignorable="d"
|
||||
Title="{StaticResource Text.Blame}"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
Height="600" Width="800">
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome UseAeroCaptionButtons="False" CornerRadius="0" CaptionHeight="28" ResizeBorderThickness="1"/>
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
<controls:WindowBorder>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="24"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Title Bar -->
|
||||
<Grid Grid.Row="0" Background="{StaticResource Brush.TitleBar}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Icon -->
|
||||
<Path Grid.Column="0" Margin="6,0" Width="16" Height="16" Data="{StaticResource Icon.Commit}"/>
|
||||
|
||||
<!-- Title -->
|
||||
<TextBlock Grid.Column="1" Text="{StaticResource Text.Blame}"/>
|
||||
|
||||
<!-- Window Commands -->
|
||||
<StackPanel Grid.Column="3" Orientation="Horizontal" WindowChrome.IsHitTestVisibleInChrome="True">
|
||||
<controls:IconButton Click="Minimize" Width="28" Padding="8" Icon="{StaticResource Icon.Minimize}" HoverBackground="#40000000" Opacity="1"/>
|
||||
<controls:IconButton Click="MaximizeOrRestore" Width="28" Padding="8" Icon="{StaticResource Icon.Maximize}" HoverBackground="#40000000" Opacity="1"/>
|
||||
<controls:IconButton Click="Quit" Width="28" Padding="8" Icon="{StaticResource Icon.Close}" HoverBackground="Red" Opacity="1"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Body -->
|
||||
<Border Grid.Row="1">
|
||||
<Grid Margin="4,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Column="0" x:Name="txtFile" FontSize="11" Foreground="{StaticResource Brush.FG2}" FontFamily="Consolas"/>
|
||||
<TextBlock Grid.Column="1" HorizontalAlignment="Right" Foreground="{StaticResource Brush.FG2}" FontFamily="11" Text="{StaticResource Text.Blame.Tip}"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- Viewer -->
|
||||
<DataGrid
|
||||
Grid.Row="2"
|
||||
x:Name="blame"
|
||||
GridLinesVisibility="Vertical"
|
||||
VerticalGridLinesBrush="{StaticResource Brush.Border2}"
|
||||
BorderBrush="{StaticResource Brush.Border2}"
|
||||
BorderThickness="1"
|
||||
FrozenColumnCount="1"
|
||||
RowHeight="16"
|
||||
SelectionUnit="FullRow"
|
||||
SelectionMode="Single"
|
||||
FontFamily="Consolas"
|
||||
SizeChanged="OnViewerSizeChanged">
|
||||
|
||||
<DataGrid.RowStyle>
|
||||
<Style TargetType="{x:Type DataGridRow}" BasedOn="{StaticResource Style.DataGridRow}">
|
||||
<EventSetter Event="RequestBringIntoView" Handler="OnViewerRequestBringIntoView"/>
|
||||
<EventSetter Event="ContextMenuOpening" Handler="OnViewerContextMenuOpening"/>
|
||||
</Style>
|
||||
</DataGrid.RowStyle>
|
||||
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Width="Auto" IsReadOnly="True" Binding="{Binding Line.LineNumber}" ElementStyle="{StaticResource Style.TextBlock.LineNumber}"/>
|
||||
|
||||
<DataGridTemplateColumn Width="SizeToCells" MinWidth="1" IsReadOnly="True">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<Border Background="{Binding BG}" BorderThickness="0">
|
||||
<TextBlock Text="{Binding Line.Content}" Background="Transparent" Foreground="{StaticResource Brush.FG1}" FontFamily="Consolas" FontSize="12" Margin="0" Padding="2,0,0,0"/>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
<StackPanel
|
||||
x:Name="notSupport"
|
||||
Grid.Row="2"
|
||||
Orientation="Vertical"
|
||||
VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||
Background="{StaticResource Brush.Window}"
|
||||
Visibility="Collapsed">
|
||||
<Path Width="64" Height="64" Data="{StaticResource Icon.Error}" Fill="{StaticResource Brush.FG2}"/>
|
||||
<TextBlock Text="{StaticResource Text.BlameTypeNotSupported}" Margin="0,16,0,0" FontFamily="Consolas" FontSize="18" FontWeight="UltraBold" HorizontalAlignment="Center" Foreground="{StaticResource Brush.FG2}"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Loading -->
|
||||
<controls:Loading x:Name="loading" Grid.Row="2" Width="48" Height="48" IsAnimating="True"/>
|
||||
|
||||
<!-- Popup to show commit info -->
|
||||
<Popup x:Name="popup" Grid.Row="2" Placement="MousePoint" IsOpen="False" StaysOpen="False" Focusable="True">
|
||||
<Border BorderBrush="{StaticResource Brush.Accent1}" BorderThickness="1" Background="{StaticResource Brush.Popup}">
|
||||
<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>
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="{StaticResource Text.Blame.SHA}" Foreground="{StaticResource Brush.FG2}" Margin="4,0"/>
|
||||
<Label Grid.Row="0" Grid.Column="1" x:Name="commitID" Margin="8,0,4,0" Padding="0" VerticalAlignment="Center"/>
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="{StaticResource Text.Blame.Author}" Foreground="{StaticResource Brush.FG2}" Margin="4,0"/>
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" x:Name="authorName" Margin="8,0,4,0"/>
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="{StaticResource Text.Blame.ModifyTime}" Foreground="{StaticResource Brush.FG2}" Margin="4,0"/>
|
||||
<TextBlock Grid.Row="2" Grid.Column="1" x:Name="authorTime" Margin="8,0,4,0"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Popup>
|
||||
</Grid>
|
||||
</controls:WindowBorder>
|
||||
</Window>
|
195
src/Views/Blame.xaml.cs
Normal file
195
src/Views/Blame.xaml.cs
Normal file
|
@ -0,0 +1,195 @@
|
|||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace SourceGit.Views {
|
||||
/// <summary>
|
||||
/// 逐行追溯
|
||||
/// </summary>
|
||||
public partial class Blame : Window {
|
||||
private static readonly Brush[] BG = new Brush[] {
|
||||
Brushes.Transparent,
|
||||
new SolidColorBrush(Color.FromArgb(128, 0, 0, 0))
|
||||
};
|
||||
|
||||
private string repo = null;
|
||||
private string lastSHA = null;
|
||||
private int lastBG = 1;
|
||||
|
||||
public class Record : INotifyPropertyChanged {
|
||||
private Brush bg = null;
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public Models.BlameLine Line { get; set; }
|
||||
public Brush OrgBG { get; set; }
|
||||
public Brush BG {
|
||||
get { return bg; }
|
||||
set {
|
||||
if (value != bg) {
|
||||
bg = value;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("BG"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<Record> Records { get; set; }
|
||||
|
||||
public Blame(string repo, string file, string revision) {
|
||||
InitializeComponent();
|
||||
|
||||
this.repo = repo;
|
||||
Records = new ObservableCollection<Record>();
|
||||
txtFile.Text = $"{file}@{revision.Substring(0, 8)}";
|
||||
|
||||
Task.Run(() => {
|
||||
var lfs = new Commands.IsLFSFiltered(repo, file).Result();
|
||||
if (lfs) {
|
||||
Dispatcher.Invoke(() => {
|
||||
loading.IsAnimating = false;
|
||||
loading.Visibility = Visibility.Collapsed;
|
||||
notSupport.Visibility = Visibility.Visible;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var rs = new Commands.Blame(repo, file, revision).Result();
|
||||
if (rs.IsBinary) {
|
||||
Dispatcher.Invoke(() => {
|
||||
loading.IsAnimating = false;
|
||||
loading.Visibility = Visibility.Collapsed;
|
||||
notSupport.Visibility = Visibility.Visible;
|
||||
});
|
||||
} else {
|
||||
foreach (var line in rs.Lines) {
|
||||
var r = new Record();
|
||||
r.Line = line;
|
||||
r.BG = GetBG(line.CommitSHA);
|
||||
r.OrgBG = r.BG;
|
||||
Records.Add(r);
|
||||
}
|
||||
|
||||
Dispatcher.Invoke(() => {
|
||||
loading.IsAnimating = false;
|
||||
loading.Visibility = Visibility.Collapsed;
|
||||
|
||||
var formatted = new FormattedText(
|
||||
$"{Records.Count}",
|
||||
CultureInfo.CurrentCulture,
|
||||
FlowDirection.LeftToRight,
|
||||
new Typeface(blame.FontFamily, FontStyles.Normal, FontWeights.Normal, FontStretches.Normal),
|
||||
12.0,
|
||||
Brushes.Black,
|
||||
VisualTreeHelper.GetDpi(this).PixelsPerDip);
|
||||
|
||||
var lineNumberWidth = formatted.Width + 16;
|
||||
var minWidth = blame.ActualWidth - lineNumberWidth;
|
||||
if (Records.Count * 16 > blame.ActualHeight) minWidth -= 8;
|
||||
blame.Columns[0].Width = lineNumberWidth;
|
||||
blame.Columns[1].MinWidth = minWidth;
|
||||
blame.ItemsSource = Records;
|
||||
blame.UpdateLayout();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Brush GetBG(string sha) {
|
||||
if (lastSHA != sha) {
|
||||
lastSHA = sha;
|
||||
lastBG = 1 - lastBG;
|
||||
}
|
||||
|
||||
return BG[lastBG];
|
||||
}
|
||||
|
||||
#region WINDOW_COMMANDS
|
||||
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
|
||||
|
||||
#region EVENTS
|
||||
private T GetVisualChild<T>(DependencyObject parent) where T : Visual {
|
||||
T child = null;
|
||||
|
||||
int count = VisualTreeHelper.GetChildrenCount(parent);
|
||||
for (int i = 0; i < count; i++) {
|
||||
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
|
||||
child = v as T;
|
||||
|
||||
if (child == null) {
|
||||
child = GetVisualChild<T>(v);
|
||||
}
|
||||
|
||||
if (child != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
private void OnViewerSizeChanged(object sender, SizeChangedEventArgs e) {
|
||||
var total = blame.ActualWidth;
|
||||
var offset = blame.NonFrozenColumnsViewportHorizontalOffset;
|
||||
var minWidth = total - offset;
|
||||
|
||||
var scroller = GetVisualChild<ScrollViewer>(blame);
|
||||
if (scroller != null && scroller.ComputedVerticalScrollBarVisibility == Visibility.Visible) minWidth -= 8;
|
||||
|
||||
blame.Columns[1].MinWidth = minWidth;
|
||||
blame.Columns[1].Width = DataGridLength.SizeToCells;
|
||||
blame.UpdateLayout();
|
||||
}
|
||||
|
||||
private void OnViewerRequestBringIntoView(object sender, RequestBringIntoViewEventArgs e) {
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void OnViewerContextMenuOpening(object sender, ContextMenuEventArgs ev) {
|
||||
var record = (sender as DataGridRow).DataContext as Record;
|
||||
if (record == null) return;
|
||||
|
||||
foreach (var r in Records) {
|
||||
if (r.Line.CommitSHA == record.Line.CommitSHA) {
|
||||
r.BG = new SolidColorBrush(Color.FromArgb(60, 0, 255, 0));
|
||||
} else {
|
||||
r.BG = r.OrgBG;
|
||||
}
|
||||
}
|
||||
|
||||
Hyperlink link = new Hyperlink(new Run(record.Line.CommitSHA));
|
||||
link.ToolTip = App.Text("Goto");
|
||||
link.Click += (o, e) => {
|
||||
Models.Watcher.Get(repo).NavigateTo(record.Line.CommitSHA);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
commitID.Content = link;
|
||||
authorName.Text = record.Line.Author;
|
||||
authorTime.Text = record.Line.Time;
|
||||
popup.IsOpen = true;
|
||||
ev.Handled = true;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
202
src/Views/Controls/Avatar.cs
Normal file
202
src/Views/Controls/Avatar.cs
Normal file
|
@ -0,0 +1,202 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace SourceGit.Views.Controls {
|
||||
|
||||
/// <summary>
|
||||
/// 头像控件
|
||||
/// </summary>
|
||||
public class Avatar : Image {
|
||||
|
||||
/// <summary>
|
||||
/// 显示FallbackLabel时的背景色
|
||||
/// </summary>
|
||||
private static readonly Brush[] BACKGROUND_BRUSHES = new Brush[] {
|
||||
new LinearGradientBrush(Colors.Orange, Color.FromRgb(255, 213, 134), 90),
|
||||
new LinearGradientBrush(Colors.DodgerBlue, Colors.LightSkyBlue, 90),
|
||||
new LinearGradientBrush(Colors.LimeGreen, Color.FromRgb(124, 241, 124), 90),
|
||||
new LinearGradientBrush(Colors.Orchid, Color.FromRgb(248, 161, 245), 90),
|
||||
new LinearGradientBrush(Colors.Tomato, Color.FromRgb(252, 165, 150), 90),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// 头像资源本地缓存路径
|
||||
/// </summary>
|
||||
public static readonly string CACHE_PATH = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"SourceGit",
|
||||
"avatars");
|
||||
|
||||
/// <summary>
|
||||
/// 邮件属性定义
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty EmailProperty = DependencyProperty.Register(
|
||||
"Email",
|
||||
typeof(string),
|
||||
typeof(Avatar),
|
||||
new PropertyMetadata(null, OnEmailChanged));
|
||||
|
||||
/// <summary>
|
||||
/// 邮件属性
|
||||
/// </summary>
|
||||
public string Email {
|
||||
get { return (string)GetValue(EmailProperty); }
|
||||
set { SetValue(EmailProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 下载头像失败时显示的Label属性定义
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty FallbackLabelProperty = DependencyProperty.Register(
|
||||
"FallbackLabel",
|
||||
typeof(string),
|
||||
typeof(Avatar),
|
||||
new PropertyMetadata("?"));
|
||||
|
||||
/// <summary>
|
||||
/// 下载头像失败时显示的Label属性
|
||||
/// </summary>
|
||||
public string FallbackLabel {
|
||||
get { return (string)GetValue(FallbackLabelProperty); }
|
||||
set { SetValue(FallbackLabelProperty, value); }
|
||||
}
|
||||
|
||||
private static Dictionary<string, List<Avatar>> requesting = new Dictionary<string, List<Avatar>>();
|
||||
private static Dictionary<string, BitmapImage> loaded = new Dictionary<string, BitmapImage>();
|
||||
private static Task loader = null;
|
||||
|
||||
/// <summary>
|
||||
/// 渲染实现
|
||||
/// </summary>
|
||||
/// <param name="dc"></param>
|
||||
protected override void OnRender(DrawingContext dc) {
|
||||
base.OnRender(dc);
|
||||
|
||||
if (Source == null) {
|
||||
var placeholder = FallbackLabel.Length > 0 ? FallbackLabel.Substring(0, 1) : "?";
|
||||
var formatted = new FormattedText(
|
||||
placeholder,
|
||||
CultureInfo.CurrentCulture,
|
||||
FlowDirection.LeftToRight,
|
||||
new Typeface(new FontFamily("Consolas"), FontStyles.Normal, FontWeights.Normal, FontStretches.Normal),
|
||||
Width * 0.65,
|
||||
Brushes.White,
|
||||
VisualTreeHelper.GetDpi(this).PixelsPerDip);
|
||||
|
||||
var corner = Math.Max(2, Width / 16);
|
||||
var offsetX = (double)0;
|
||||
if (HorizontalAlignment == HorizontalAlignment.Right) {
|
||||
offsetX = -Width * 0.5;
|
||||
} else if (HorizontalAlignment == HorizontalAlignment.Left) {
|
||||
offsetX = Width * 0.5;
|
||||
}
|
||||
|
||||
var chars = placeholder.ToCharArray();
|
||||
var sum = 0;
|
||||
foreach (var ch in chars) sum += Math.Abs(ch);
|
||||
|
||||
Brush brush = BACKGROUND_BRUSHES[sum % BACKGROUND_BRUSHES.Length];
|
||||
dc.DrawRoundedRectangle(brush, null, new Rect(-Width * 0.5 + offsetX, -Height * 0.5, Width, Height), corner, corner);
|
||||
dc.DrawText(formatted, new Point(formatted.Width * -0.5 + offsetX, formatted.Height * -0.5));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 邮件变化时触发
|
||||
/// </summary>
|
||||
/// <param name="d"></param>
|
||||
/// <param name="e"></param>
|
||||
private static void OnEmailChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
Avatar a = d as Avatar;
|
||||
if (a == null) return;
|
||||
|
||||
var oldEmail = e.OldValue as string;
|
||||
if (!string.IsNullOrEmpty(oldEmail) && requesting.ContainsKey(oldEmail)) {
|
||||
if (requesting[oldEmail].Count <= 1) {
|
||||
requesting.Remove(oldEmail);
|
||||
} else {
|
||||
requesting[oldEmail].Remove(a);
|
||||
}
|
||||
}
|
||||
|
||||
a.Source = null;
|
||||
a.InvalidateVisual();
|
||||
|
||||
var email = e.NewValue as string;
|
||||
if (string.IsNullOrEmpty(email)) return;
|
||||
|
||||
if (loaded.ContainsKey(email)) {
|
||||
a.Source = loaded[email];
|
||||
return;
|
||||
}
|
||||
|
||||
if (requesting.ContainsKey(email)) {
|
||||
requesting[email].Add(a);
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] hash = MD5.Create().ComputeHash(Encoding.Default.GetBytes(email.ToLower().Trim()));
|
||||
string md5 = "";
|
||||
for (int i = 0; i < hash.Length; i++) md5 += hash[i].ToString("x2");
|
||||
md5 = md5.ToLower();
|
||||
|
||||
string filePath = Path.Combine(CACHE_PATH, md5);
|
||||
if (File.Exists(filePath)) {
|
||||
var img = new BitmapImage(new Uri(filePath));
|
||||
loaded.Add(email, img);
|
||||
a.Source = img;
|
||||
return;
|
||||
}
|
||||
|
||||
requesting.Add(email, new List<Avatar>());
|
||||
requesting[email].Add(a);
|
||||
|
||||
Action job = () => {
|
||||
try {
|
||||
HttpWebRequest req = WebRequest.CreateHttp(Models.Preference.Instance.General.AvatarServer + md5 + "?d=404");
|
||||
req.Timeout = 2000;
|
||||
req.Method = "GET";
|
||||
|
||||
HttpWebResponse rsp = req.GetResponse() as HttpWebResponse;
|
||||
if (rsp.StatusCode == HttpStatusCode.OK) {
|
||||
using (Stream reader = rsp.GetResponseStream())
|
||||
using (FileStream writer = File.OpenWrite(filePath)) {
|
||||
reader.CopyTo(writer);
|
||||
}
|
||||
|
||||
a.Dispatcher.Invoke(() => {
|
||||
var img = new BitmapImage(new Uri(filePath));
|
||||
loaded.Add(email, img);
|
||||
|
||||
if (requesting.ContainsKey(email)) {
|
||||
foreach (var one in requesting[email]) one.Source = img;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (!loaded.ContainsKey(email)) loaded.Add(email, null);
|
||||
}
|
||||
} catch {
|
||||
if (!loaded.ContainsKey(email)) loaded.Add(email, null);
|
||||
}
|
||||
|
||||
requesting.Remove(email);
|
||||
};
|
||||
|
||||
if (loader != null && !loader.IsCompleted) {
|
||||
loader = loader.ContinueWith(t => { job(); });
|
||||
} else {
|
||||
loader = Task.Run(job);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
52
src/Views/Controls/Badge.cs
Normal file
52
src/Views/Controls/Badge.cs
Normal file
|
@ -0,0 +1,52 @@
|
|||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace SourceGit.Views.Controls {
|
||||
|
||||
/// <summary>
|
||||
/// 徽章
|
||||
/// </summary>
|
||||
public class Badge : Border {
|
||||
private TextBlock label = null;
|
||||
|
||||
public static readonly DependencyProperty LabelProperty = DependencyProperty.Register(
|
||||
"Label",
|
||||
typeof(string),
|
||||
typeof(Border),
|
||||
new PropertyMetadata("", OnLabelChanged));
|
||||
|
||||
public string Label {
|
||||
get { return (string)GetValue(LabelProperty); }
|
||||
set { SetValue(LabelProperty, value); }
|
||||
}
|
||||
|
||||
public Badge() {
|
||||
Width = double.NaN;
|
||||
Height = 18;
|
||||
CornerRadius = new CornerRadius(9);
|
||||
VerticalAlignment = VerticalAlignment.Center;
|
||||
Background = FindResource("Brush.Badge") as Brush;
|
||||
Visibility = Visibility.Collapsed;
|
||||
|
||||
label = new TextBlock();
|
||||
label.FontSize = 10;
|
||||
label.HorizontalAlignment = HorizontalAlignment.Center;
|
||||
label.Margin = new Thickness(9, 0, 9, 0);
|
||||
Child = label;
|
||||
}
|
||||
|
||||
private static void OnLabelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
Badge badge = d as Badge;
|
||||
if (badge != null) {
|
||||
var text = e.NewValue as string;
|
||||
if (string.IsNullOrEmpty(text) || text == "0") {
|
||||
badge.Visibility = Visibility.Collapsed;
|
||||
} else {
|
||||
badge.label.Text = text;
|
||||
badge.Visibility = Visibility.Visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
86
src/Views/Controls/Bookmark.cs
Normal file
86
src/Views/Controls/Bookmark.cs
Normal file
|
@ -0,0 +1,86 @@
|
|||
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.Media;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace SourceGit.Views.Controls {
|
||||
|
||||
/// <summary>
|
||||
/// 标签页图标
|
||||
/// </summary>
|
||||
public class Bookmark : Border {
|
||||
private Path icon = null;
|
||||
|
||||
public static readonly Brush[] COLORS = new Brush[] {
|
||||
Brushes.Transparent,
|
||||
Brushes.White,
|
||||
Brushes.Red,
|
||||
Brushes.Orange,
|
||||
Brushes.Yellow,
|
||||
Brushes.ForestGreen,
|
||||
Brushes.Purple,
|
||||
Brushes.DeepSkyBlue,
|
||||
Brushes.Magenta,
|
||||
};
|
||||
|
||||
public static readonly DependencyProperty ColorProperty =
|
||||
DependencyProperty.Register("Color", typeof(int), typeof(Bookmark), new PropertyMetadata(0, UpdateBookmark));
|
||||
|
||||
public int Color {
|
||||
get { return (int)GetValue(ColorProperty); }
|
||||
set { SetValue(ColorProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsNewPageProperty =
|
||||
DependencyProperty.Register("IsNewPage", typeof(bool), typeof(Bookmark), new PropertyMetadata(false, UpdateBookmark));
|
||||
|
||||
public bool IsNewPage {
|
||||
get { return (bool)GetValue(IsNewPageProperty); }
|
||||
set { SetValue(IsNewPageProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty HideOnZeroProperty =
|
||||
DependencyProperty.Register("HideOnZero", typeof(bool), typeof(Bookmark), new PropertyMetadata(false, UpdateBookmark));
|
||||
|
||||
public bool HideOnZero {
|
||||
get { return (bool)GetValue(HideOnZeroProperty); }
|
||||
set { SetValue(HideOnZeroProperty, value); }
|
||||
}
|
||||
|
||||
public Bookmark() {
|
||||
icon = new Path();
|
||||
Child = icon;
|
||||
UpdateBookmark(this, new DependencyPropertyChangedEventArgs());
|
||||
}
|
||||
|
||||
private static void UpdateBookmark(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
var mark = d as Bookmark;
|
||||
if (mark == null) return;
|
||||
|
||||
if (mark.HideOnZero && mark.Color == 0) {
|
||||
mark.Visibility = Visibility.Collapsed;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mark.IsNewPage) {
|
||||
if (mark.Color == 0) {
|
||||
mark.icon.Fill = mark.FindResource("Brush.FG1") as Brush;
|
||||
mark.icon.Data = mark.FindResource("Icon.Git") as Geometry;
|
||||
} else {
|
||||
mark.icon.Fill = COLORS[mark.Color % COLORS.Length];
|
||||
mark.icon.Data = mark.FindResource("Icon.Bookmark") as Geometry;
|
||||
}
|
||||
} else {
|
||||
mark.icon.Fill = mark.FindResource("Brush.FG1") as Brush;
|
||||
mark.icon.Data = mark.FindResource("Icon.NewPage") as Geometry;
|
||||
}
|
||||
|
||||
mark.Visibility = Visibility.Visible;
|
||||
}
|
||||
}
|
||||
}
|
105
src/Views/Controls/ChangeDisplaySwitcher.cs
Normal file
105
src/Views/Controls/ChangeDisplaySwitcher.cs
Normal file
|
@ -0,0 +1,105 @@
|
|||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace SourceGit.Views.Controls {
|
||||
/// <summary>
|
||||
/// 用于切换变更显示模式的按钮
|
||||
/// </summary>
|
||||
public class ChangeDisplaySwitcher : Button {
|
||||
|
||||
public static readonly DependencyProperty ModeProperty = DependencyProperty.Register(
|
||||
"Mode",
|
||||
typeof(Models.Change.DisplayMode),
|
||||
typeof(ChangeDisplaySwitcher),
|
||||
new PropertyMetadata(Models.Change.DisplayMode.Tree, OnModeChanged));
|
||||
|
||||
public Models.Change.DisplayMode Mode {
|
||||
get { return (Models.Change.DisplayMode)GetValue(ModeProperty); }
|
||||
set { SetValue(ModeProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly RoutedEvent ModeChangedEvent = EventManager.RegisterRoutedEvent(
|
||||
"ModeChanged",
|
||||
RoutingStrategy.Bubble,
|
||||
typeof(RoutedEventHandler),
|
||||
typeof(ChangeDisplaySwitcher));
|
||||
|
||||
public event RoutedEventHandler ModeChanged {
|
||||
add { AddHandler(ModeChangedEvent, value); }
|
||||
remove { RemoveHandler(ModeChangedEvent, value); }
|
||||
}
|
||||
|
||||
private Path icon = null;
|
||||
|
||||
public ChangeDisplaySwitcher() {
|
||||
icon = new Path();
|
||||
icon.Fill = FindResource("Brush.FG2") as Brush;
|
||||
icon.Data = FindResource("Icon.Tree") as Geometry;
|
||||
|
||||
Content = icon;
|
||||
Style = FindResource("Style.Button") as Style;
|
||||
BorderThickness = new Thickness(0);
|
||||
ToolTip = App.Text("ChangeDisplayMode");
|
||||
|
||||
Click += OnClicked;
|
||||
}
|
||||
|
||||
private void OnClicked(object sender, RoutedEventArgs e) {
|
||||
if (ContextMenu != null) {
|
||||
ContextMenu.IsOpen = true;
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var menu = new ContextMenu();
|
||||
menu.Placement = PlacementMode.Bottom;
|
||||
menu.PlacementTarget = this;
|
||||
menu.StaysOpen = false;
|
||||
menu.Focusable = true;
|
||||
|
||||
FillMenu(menu, "ChangeDisplayMode.Tree", "Icon.Tree", Models.Change.DisplayMode.Tree);
|
||||
FillMenu(menu, "ChangeDisplayMode.List", "Icon.List", Models.Change.DisplayMode.List);
|
||||
FillMenu(menu, "ChangeDisplayMode.Grid", "Icon.Grid", Models.Change.DisplayMode.Grid);
|
||||
|
||||
ContextMenu = menu;
|
||||
ContextMenu.IsOpen = true;
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void FillMenu(ContextMenu menu, string header, string icon, Models.Change.DisplayMode useMode) {
|
||||
var iconMode = new Path();
|
||||
iconMode.Width = 12;
|
||||
iconMode.Height = 12;
|
||||
iconMode.Fill = FindResource("Brush.FG2") as Brush;
|
||||
iconMode.Data = FindResource(icon) as Geometry;
|
||||
|
||||
var item = new MenuItem();
|
||||
item.Icon = iconMode;
|
||||
item.Header = App.Text(header);
|
||||
item.Click += (o, e) => Mode = useMode;
|
||||
|
||||
menu.Items.Add(item);
|
||||
}
|
||||
|
||||
private static void OnModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
var elem = d as ChangeDisplaySwitcher;
|
||||
if (elem != null) {
|
||||
switch (elem.Mode) {
|
||||
case Models.Change.DisplayMode.Tree:
|
||||
elem.icon.Data = elem.FindResource("Icon.Tree") as Geometry;
|
||||
break;
|
||||
case Models.Change.DisplayMode.List:
|
||||
elem.icon.Data = elem.FindResource("Icon.List") as Geometry;
|
||||
break;
|
||||
case Models.Change.DisplayMode.Grid:
|
||||
elem.icon.Data = elem.FindResource("Icon.Grid") as Geometry;
|
||||
break;
|
||||
}
|
||||
elem.RaiseEvent(new RoutedEventArgs(ModeChangedEvent));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
111
src/Views/Controls/ChangeStatusIcon.cs
Normal file
111
src/Views/Controls/ChangeStatusIcon.cs
Normal file
|
@ -0,0 +1,111 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace SourceGit.Views.Controls {
|
||||
/// <summary>
|
||||
/// 变更状态图标
|
||||
/// </summary>
|
||||
class ChangeStatusIcon : FrameworkElement {
|
||||
|
||||
public static readonly DependencyProperty ChangeProperty = DependencyProperty.Register(
|
||||
"Change",
|
||||
typeof(Models.Change),
|
||||
typeof(ChangeStatusIcon),
|
||||
new PropertyMetadata(null, ForceDirty));
|
||||
|
||||
public Models.Change Change {
|
||||
get { return (Models.Change)GetValue(ChangeProperty); }
|
||||
set { SetValue(ChangeProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsLocalChangeProperty = DependencyProperty.Register(
|
||||
"IsLocalChange",
|
||||
typeof(bool),
|
||||
typeof(ChangeStatusIcon),
|
||||
new PropertyMetadata(false, ForceDirty));
|
||||
|
||||
public bool IsLocalChange {
|
||||
get { return (bool)GetValue(IsLocalChangeProperty); }
|
||||
set { SetValue(IsLocalChangeProperty, value); }
|
||||
}
|
||||
|
||||
private Brush background;
|
||||
private FormattedText label;
|
||||
|
||||
public ChangeStatusIcon() {
|
||||
HorizontalAlignment = HorizontalAlignment.Center;
|
||||
VerticalAlignment = VerticalAlignment.Center;
|
||||
}
|
||||
|
||||
protected override void OnRender(DrawingContext dc) {
|
||||
if (background == null || label == null) return;
|
||||
var corner = Math.Max(2, Width / 16);
|
||||
dc.DrawRoundedRectangle(background, null, new Rect(0, 0, Width, Height), corner, corner);
|
||||
dc.DrawText(label, new Point((Width - label.Width) * 0.5, 0));
|
||||
}
|
||||
|
||||
private static void ForceDirty(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
var icon = d as ChangeStatusIcon;
|
||||
if (icon == null) return;
|
||||
|
||||
if (icon.Change == null) {
|
||||
icon.background = null;
|
||||
icon.label = null;
|
||||
return;
|
||||
}
|
||||
|
||||
string txt;
|
||||
if (icon.IsLocalChange) {
|
||||
if (icon.Change.IsConflit) {
|
||||
icon.background = Brushes.OrangeRed;
|
||||
txt = "!";
|
||||
} else {
|
||||
icon.background = GetBackground(icon.Change.WorkTree);
|
||||
txt = GetLabel(icon.Change.WorkTree);
|
||||
}
|
||||
} else {
|
||||
icon.background = GetBackground(icon.Change.Index);
|
||||
txt = GetLabel(icon.Change.Index);
|
||||
}
|
||||
|
||||
icon.label = new FormattedText(
|
||||
txt,
|
||||
CultureInfo.CurrentCulture,
|
||||
FlowDirection.LeftToRight,
|
||||
new Typeface(new FontFamily("Consolas"), FontStyles.Normal, FontWeights.Normal, FontStretches.Normal),
|
||||
icon.Width * 0.8,
|
||||
new SolidColorBrush(Color.FromRgb(241,241,241)),
|
||||
VisualTreeHelper.GetDpi(icon).PixelsPerDip);
|
||||
|
||||
icon.InvalidateVisual();
|
||||
}
|
||||
|
||||
private static Brush GetBackground(Models.Change.Status status) {
|
||||
switch (status) {
|
||||
case Models.Change.Status.Modified: return new LinearGradientBrush(Colors.Orange, Color.FromRgb(255, 213, 134), 90);
|
||||
case Models.Change.Status.Added: return new LinearGradientBrush(Colors.LimeGreen, Color.FromRgb(124, 241, 124), 90);
|
||||
case Models.Change.Status.Deleted: return new LinearGradientBrush(Colors.Tomato, Color.FromRgb(252, 165, 150), 90);
|
||||
case Models.Change.Status.Renamed: return new LinearGradientBrush(Colors.Orchid, Color.FromRgb(248, 161, 245), 90);
|
||||
case Models.Change.Status.Copied: return new LinearGradientBrush(Colors.Orange, Color.FromRgb(255, 213, 134), 90);
|
||||
case Models.Change.Status.Unmerged: return new LinearGradientBrush(Colors.Orange, Color.FromRgb(255, 213, 134), 90);
|
||||
case Models.Change.Status.Untracked: return new LinearGradientBrush(Colors.LimeGreen, Color.FromRgb(124, 241, 124), 90);
|
||||
default: return Brushes.Transparent;
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetLabel(Models.Change.Status status) {
|
||||
switch (status) {
|
||||
case Models.Change.Status.Modified: return "±";
|
||||
case Models.Change.Status.Added: return "✚";
|
||||
case Models.Change.Status.Deleted: return "▬";
|
||||
case Models.Change.Status.Renamed: return "➔";
|
||||
case Models.Change.Status.Copied: return "❏";
|
||||
case Models.Change.Status.Unmerged: return "U";
|
||||
case Models.Change.Status.Untracked: return "?";
|
||||
default: return "?";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
312
src/Views/Controls/CommitGraph.cs
Normal file
312
src/Views/Controls/CommitGraph.cs
Normal file
|
@ -0,0 +1,312 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace SourceGit.Views.Controls {
|
||||
|
||||
/// <summary>
|
||||
/// 提交线路图
|
||||
/// </summary>
|
||||
public class CommitGraph : FrameworkElement {
|
||||
public static readonly Brush[] COLORS = new Brush[] {
|
||||
Brushes.Orange,
|
||||
Brushes.ForestGreen,
|
||||
Brushes.Gold,
|
||||
Brushes.Magenta,
|
||||
Brushes.Red,
|
||||
Brushes.Gray,
|
||||
Brushes.Turquoise,
|
||||
Brushes.Olive,
|
||||
};
|
||||
|
||||
public static readonly double UNIT_WIDTH = 12;
|
||||
public static readonly double HALF_WIDTH = 6;
|
||||
public static readonly double UNIT_HEIGHT = 24;
|
||||
public static readonly double HALF_HEIGHT = 12;
|
||||
|
||||
public class Line {
|
||||
public List<Point> Points = new List<Point>();
|
||||
public int Color = 0;
|
||||
}
|
||||
|
||||
public class LineHelper {
|
||||
public string Next;
|
||||
public bool IsMerged;
|
||||
public double LastX;
|
||||
public double LastY;
|
||||
public Line Line;
|
||||
|
||||
public LineHelper(string next, bool isMerged, int color, Point start) {
|
||||
Next = next;
|
||||
IsMerged = isMerged;
|
||||
LastX = start.X;
|
||||
LastY = start.Y;
|
||||
|
||||
Line = new Line();
|
||||
Line.Color = color % COLORS.Length;
|
||||
Line.Points.Add(start);
|
||||
}
|
||||
|
||||
public void Add(double x, double y, bool isEnd = false) {
|
||||
if (x > LastX) {
|
||||
Line.Points.Add(new Point(LastX, LastY));
|
||||
Line.Points.Add(new Point(x, y - HALF_HEIGHT));
|
||||
} else if (x < LastX) {
|
||||
Line.Points.Add(new Point(LastX, LastY + HALF_HEIGHT));
|
||||
Line.Points.Add(new Point(x, y));
|
||||
}
|
||||
|
||||
LastX = x;
|
||||
LastY = y;
|
||||
|
||||
if (isEnd) {
|
||||
var last = Line.Points.Last();
|
||||
if (LastX != last.X || LastY != last.Y) Line.Points.Add(new Point(LastX, LastY));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Link {
|
||||
public Point Start;
|
||||
public Point Control;
|
||||
public Point End;
|
||||
public int Color;
|
||||
}
|
||||
|
||||
public class Dot {
|
||||
public Point Center;
|
||||
public int Color;
|
||||
}
|
||||
|
||||
public class Data {
|
||||
public List<Line> Lines = new List<Line>();
|
||||
public List<Link> Links = new List<Link>();
|
||||
public List<Dot> Dots = new List<Dot>();
|
||||
}
|
||||
|
||||
private Data data = null;
|
||||
private double startY = 0;
|
||||
|
||||
public CommitGraph() {
|
||||
IsHitTestVisible = false;
|
||||
ClipToBounds = true;
|
||||
}
|
||||
|
||||
public void SetOffset(double offset) {
|
||||
startY = offset;
|
||||
InvalidateVisual();
|
||||
}
|
||||
|
||||
public void SetData(List<Models.Commit> commits, bool isSearchResult = false) {
|
||||
if (isSearchResult) {
|
||||
foreach (var c in commits) c.Margin = new Thickness(0);
|
||||
data = null;
|
||||
return;
|
||||
}
|
||||
|
||||
var temp = new Data();
|
||||
var unsolved = new List<LineHelper>();
|
||||
var mapUnsolved = new Dictionary<string, LineHelper>();
|
||||
var ended = new List<LineHelper>();
|
||||
var offsetY = -HALF_HEIGHT;
|
||||
var colorIdx = 0;
|
||||
|
||||
foreach (var commit in commits) {
|
||||
var major = null as LineHelper;
|
||||
var isMerged = commit.IsMerged;
|
||||
var oldCount = unsolved.Count;
|
||||
|
||||
// 更新Y坐标
|
||||
offsetY += UNIT_HEIGHT;
|
||||
|
||||
// 找到第一个依赖于本提交的树,将其他依赖于本提交的树标记为终止,并对已存在的线路调整(防止线重合)
|
||||
double offsetX = -HALF_WIDTH;
|
||||
foreach (var l in unsolved) {
|
||||
if (l.Next == commit.SHA) {
|
||||
if (major == null) {
|
||||
offsetX += UNIT_WIDTH;
|
||||
major = l;
|
||||
|
||||
if (commit.Parents.Count > 0) {
|
||||
major.Next = commit.Parents[0];
|
||||
if (!mapUnsolved.ContainsKey(major.Next)) mapUnsolved.Add(major.Next, major);
|
||||
} else {
|
||||
major.Next = "ENDED";
|
||||
ended.Add(l);
|
||||
}
|
||||
|
||||
major.Add(offsetX, offsetY);
|
||||
} else {
|
||||
ended.Add(l);
|
||||
}
|
||||
|
||||
isMerged = isMerged || l.IsMerged;
|
||||
} else {
|
||||
if (!mapUnsolved.ContainsKey(l.Next)) mapUnsolved.Add(l.Next, l);
|
||||
offsetX += UNIT_WIDTH;
|
||||
l.Add(offsetX, offsetY);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理本提交为非当前分支HEAD的情况(创建新依赖线路)
|
||||
if (major == null && commit.Parents.Count > 0) {
|
||||
offsetX += UNIT_WIDTH;
|
||||
major = new LineHelper(commit.Parents[0], isMerged, colorIdx, new Point(offsetX, offsetY));
|
||||
unsolved.Add(major);
|
||||
colorIdx++;
|
||||
}
|
||||
|
||||
// 确定本提交的点的位置
|
||||
Point position = new Point(offsetX, offsetY);
|
||||
if (major != null) {
|
||||
major.IsMerged = isMerged;
|
||||
position.X = major.LastX;
|
||||
position.Y = offsetY;
|
||||
temp.Dots.Add(new Dot() { Center = position, Color = major.Line.Color });
|
||||
} else {
|
||||
temp.Dots.Add(new Dot() { Center = position, Color = 0 });
|
||||
}
|
||||
|
||||
// 处理本提交的其他依赖
|
||||
for (int j = 1; j < commit.Parents.Count; j++) {
|
||||
var parent = commit.Parents[j];
|
||||
if (mapUnsolved.ContainsKey(parent)) {
|
||||
var l = mapUnsolved[parent];
|
||||
var link = new Link();
|
||||
|
||||
link.Start = position;
|
||||
link.End = new Point(l.LastX, offsetY + HALF_HEIGHT);
|
||||
link.Control = new Point(link.End.X, link.Start.Y);
|
||||
link.Color = l.Line.Color;
|
||||
temp.Links.Add(link);
|
||||
} else {
|
||||
offsetX += UNIT_WIDTH;
|
||||
unsolved.Add(new LineHelper(commit.Parents[j], isMerged, colorIdx, position));
|
||||
colorIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理已终止的线
|
||||
foreach (var l in ended) {
|
||||
l.Add(position.X, position.Y, true);
|
||||
temp.Lines.Add(l.Line);
|
||||
unsolved.Remove(l);
|
||||
}
|
||||
|
||||
// 加入本次提交
|
||||
commit.IsMerged = isMerged;
|
||||
commit.Margin = new Thickness(Math.Max(offsetX + HALF_WIDTH, oldCount * UNIT_WIDTH), 0, 0, 0);
|
||||
|
||||
// 清理
|
||||
ended.Clear();
|
||||
mapUnsolved.Clear();
|
||||
}
|
||||
|
||||
// 处理尚未终结的线
|
||||
for (int i = 0; i < unsolved.Count; i++) {
|
||||
var path = unsolved[i];
|
||||
var endY = (commits.Count - 0.5) * UNIT_HEIGHT;
|
||||
|
||||
if (path.Line.Points.Count == 1 && path.Line.Points[0].Y == endY) continue;
|
||||
|
||||
path.Add((i + 0.5) * UNIT_WIDTH, endY, true);
|
||||
temp.Lines.Add(path.Line);
|
||||
}
|
||||
unsolved.Clear();
|
||||
|
||||
// 排序
|
||||
temp.Lines.Sort((l, h) => l.Points[0].Y.CompareTo(h.Points[0].Y));
|
||||
|
||||
Dispatcher.Invoke(() => {
|
||||
data = temp;
|
||||
InvalidateVisual();
|
||||
});
|
||||
}
|
||||
|
||||
protected override void OnRender(DrawingContext dc) {
|
||||
if (data == null) return;
|
||||
|
||||
var top = startY;
|
||||
var bottom = startY + ActualHeight;
|
||||
|
||||
dc.PushTransform(new TranslateTransform(0, -startY));
|
||||
|
||||
// 绘制曲线
|
||||
foreach (var line in data.Lines) {
|
||||
var last = line.Points[0];
|
||||
var size = line.Points.Count;
|
||||
|
||||
if (line.Points[size - 1].Y < top) continue;
|
||||
if (last.Y > bottom) continue;
|
||||
|
||||
var geo = new StreamGeometry();
|
||||
var pen = new Pen(COLORS[line.Color], 2);
|
||||
using (var ctx = geo.Open()) {
|
||||
ctx.BeginFigure(last, false, false);
|
||||
|
||||
var ended = false;
|
||||
for (int i = 1; i < size; i++) {
|
||||
var cur = line.Points[i];
|
||||
if (cur.Y > bottom) {
|
||||
cur.Y = bottom;
|
||||
ended = true;
|
||||
}
|
||||
|
||||
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 += 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);
|
||||
}
|
||||
|
||||
if (ended) break;
|
||||
last = cur;
|
||||
}
|
||||
}
|
||||
|
||||
geo.Freeze();
|
||||
dc.DrawGeometry(null, pen, geo);
|
||||
}
|
||||
|
||||
// 绘制合并线
|
||||
foreach (var link in data.Links) {
|
||||
if (link.End.Y < top) continue;
|
||||
if (link.Start.Y > bottom) break;
|
||||
|
||||
var geo = new StreamGeometry();
|
||||
var pen = new Pen(COLORS[link.Color], 2);
|
||||
|
||||
using (var ctx = geo.Open()) {
|
||||
ctx.BeginFigure(link.Start, false, false);
|
||||
ctx.QuadraticBezierTo(link.Control, link.End, true, false);
|
||||
}
|
||||
|
||||
geo.Freeze();
|
||||
dc.DrawGeometry(null, pen, geo);
|
||||
}
|
||||
|
||||
// 绘制点
|
||||
foreach (var dot in data.Dots) {
|
||||
if (dot.Center.Y < top) continue;
|
||||
if (dot.Center.Y > bottom) break;
|
||||
|
||||
dc.DrawEllipse(COLORS[dot.Color], null, dot.Center, 3, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
80
src/Views/Controls/DragDropAdorner.cs
Normal file
80
src/Views/Controls/DragDropAdorner.cs
Normal file
|
@ -0,0 +1,80 @@
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace SourceGit.Views.Controls {
|
||||
|
||||
/// <summary>
|
||||
/// DragDropAdorner容器
|
||||
/// </summary>
|
||||
public class DragDropAdornerLayer : Grid {
|
||||
private static AdornerLayer host = null;
|
||||
private static bool enableFeedback = false;
|
||||
|
||||
public DragDropAdornerLayer() {
|
||||
Loaded += (o, e) => host = AdornerLayer.GetAdornerLayer(this);
|
||||
PreviewGiveFeedback += OnPreviewGiveFeedback;
|
||||
}
|
||||
|
||||
public static void Add(Adorner adorner) {
|
||||
host.Add(adorner);
|
||||
enableFeedback = true;
|
||||
}
|
||||
|
||||
public static void Remove(Adorner adorner) {
|
||||
host.Remove(adorner);
|
||||
enableFeedback = false;
|
||||
}
|
||||
|
||||
private static void OnPreviewGiveFeedback(object sender, GiveFeedbackEventArgs e) {
|
||||
if (enableFeedback) host.Update();
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 展示正在拖拽的视图
|
||||
/// </summary>
|
||||
public class DragDropAdorner : Adorner {
|
||||
private Size renderSize;
|
||||
private Brush renderBrush;
|
||||
|
||||
public struct PInPoint {
|
||||
public int X;
|
||||
public int Y;
|
||||
|
||||
public PInPoint(int x, int y) { X = x; Y = y; }
|
||||
public PInPoint(double x, double y) { X = (int)x; Y = (int)y; }
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
static extern void GetCursorPos(ref PInPoint p);
|
||||
|
||||
public DragDropAdorner(FrameworkElement elem) : base(elem) {
|
||||
renderSize = elem.RenderSize;
|
||||
renderBrush = new VisualBrush(elem);
|
||||
IsHitTestVisible = false;
|
||||
DragDropAdornerLayer.Add(this);
|
||||
}
|
||||
|
||||
public void Remove() {
|
||||
DragDropAdornerLayer.Remove(this);
|
||||
}
|
||||
|
||||
protected override void OnRender(DrawingContext dc) {
|
||||
base.OnRender(dc);
|
||||
|
||||
PInPoint p = new PInPoint();
|
||||
GetCursorPos(ref p);
|
||||
|
||||
Point pos = PointFromScreen(new Point(p.X, p.Y));
|
||||
Rect rect = new Rect(pos.X, pos.Y, renderSize.Width, renderSize.Height);
|
||||
|
||||
dc.PushOpacity(1);
|
||||
dc.DrawRectangle(renderBrush, null, rect);
|
||||
dc.DrawRectangle(null, new Pen(Brushes.DeepSkyBlue, 2), rect);
|
||||
}
|
||||
}
|
||||
}
|
34
src/Views/Controls/IconButton.cs
Normal file
34
src/Views/Controls/IconButton.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace SourceGit.Views.Controls {
|
||||
|
||||
/// <summary>
|
||||
/// 简化只有一个Icon的Button
|
||||
/// </summary>
|
||||
public class IconButton : Button {
|
||||
|
||||
public static readonly DependencyProperty IconProperty = DependencyProperty.Register(
|
||||
"Icon",
|
||||
typeof(Geometry),
|
||||
typeof(IconButton),
|
||||
new PropertyMetadata(null));
|
||||
|
||||
public Geometry Icon {
|
||||
get { return (Geometry)GetValue(IconProperty); }
|
||||
set { SetValue(IconProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty HoverBackgroundProperty = DependencyProperty.Register(
|
||||
"HoverBackground",
|
||||
typeof(Brush),
|
||||
typeof(IconButton),
|
||||
new PropertyMetadata(Brushes.Transparent));
|
||||
|
||||
public Brush HoverBackground {
|
||||
get { return (Brush)GetValue(HoverBackgroundProperty); }
|
||||
set { SetValue(HoverBackgroundProperty, value); }
|
||||
}
|
||||
}
|
||||
}
|
51
src/Views/Controls/Loading.cs
Normal file
51
src/Views/Controls/Loading.cs
Normal file
|
@ -0,0 +1,51 @@
|
|||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace SourceGit.Views.Controls {
|
||||
|
||||
/// <summary>
|
||||
/// 加载中图标
|
||||
/// </summary>
|
||||
public class Loading : UserControl {
|
||||
private Path icon = null;
|
||||
|
||||
public static readonly DependencyProperty IsAnimatingProperty = DependencyProperty.Register(
|
||||
"IsAnimating",
|
||||
typeof(bool),
|
||||
typeof(Loading),
|
||||
new PropertyMetadata(false, OnIsAnimatingChanged));
|
||||
|
||||
public bool IsAnimating {
|
||||
get { return (bool)GetValue(IsAnimatingProperty); }
|
||||
set { SetValue(IsAnimatingProperty, value); }
|
||||
}
|
||||
|
||||
public Loading() {
|
||||
icon = new Path();
|
||||
icon.Data = FindResource("Icon.Loading") as Geometry;
|
||||
icon.RenderTransformOrigin = new Point(.5, .5);
|
||||
icon.RenderTransform = new RotateTransform(0);
|
||||
icon.Width = double.NaN;
|
||||
icon.Height = double.NaN;
|
||||
|
||||
AddChild(icon);
|
||||
}
|
||||
|
||||
private static void OnIsAnimatingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
var loading = d as Loading;
|
||||
if (loading == null) return;
|
||||
|
||||
if (loading.IsAnimating) {
|
||||
DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
|
||||
anim.RepeatBehavior = RepeatBehavior.Forever;
|
||||
loading.icon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
|
||||
} else {
|
||||
loading.icon.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
66
src/Views/Controls/PageContainer.cs
Normal file
66
src/Views/Controls/PageContainer.cs
Normal file
|
@ -0,0 +1,66 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SourceGit.Views.Controls {
|
||||
|
||||
/// <summary>
|
||||
/// 用于方便切换子页面的组件
|
||||
/// </summary>
|
||||
public class PageContainer : Grid {
|
||||
private Dictionary<string, UIElement> pages;
|
||||
private string front;
|
||||
|
||||
public PageContainer() {
|
||||
pages = new Dictionary<string, UIElement>();
|
||||
front = null;
|
||||
|
||||
Loaded += OnLoaded;
|
||||
}
|
||||
|
||||
public void Add(string id, UIElement view) {
|
||||
view.Visibility = Visibility.Collapsed;
|
||||
pages.Add(id, view);
|
||||
Children.Add(view);
|
||||
}
|
||||
|
||||
public UIElement Get(string id) {
|
||||
if (pages.ContainsKey(id)) return pages[id];
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Goto(string id) {
|
||||
if (!pages.ContainsKey(id)) return;
|
||||
|
||||
if (!string.IsNullOrEmpty(front)) {
|
||||
if (front == id) return;
|
||||
pages[front].Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
front = id;
|
||||
pages[front].Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
public void Remove(string id) {
|
||||
if (!pages.ContainsKey(id)) return;
|
||||
if (front == id) front = null;
|
||||
Children.Remove(pages[id]);
|
||||
pages.Remove(id);
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs e) {
|
||||
foreach (var child in Children) {
|
||||
var elem = child as UIElement;
|
||||
var id = elem.Uid;
|
||||
if (string.IsNullOrEmpty(id)) continue;
|
||||
|
||||
pages.Add(id, elem);
|
||||
front = id;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(front)) {
|
||||
pages[front].Visibility = Visibility.Visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
92
src/Views/Controls/PopupWidget.cs
Normal file
92
src/Views/Controls/PopupWidget.cs
Normal file
|
@ -0,0 +1,92 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SourceGit.Views.Controls {
|
||||
|
||||
/// <summary>
|
||||
/// 可显示弹出面板的容器接口
|
||||
/// </summary>
|
||||
public interface IPopupContainer {
|
||||
void Show(PopupWidget widget);
|
||||
void ShowAndStart(PopupWidget widget);
|
||||
void UpdateProgress(string message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 可弹出面板
|
||||
/// </summary>
|
||||
public class PopupWidget : UserControl {
|
||||
private static Dictionary<string, IPopupContainer> containers = new Dictionary<string, IPopupContainer>();
|
||||
private static string currentContainer = null;
|
||||
private IPopupContainer mine = null;
|
||||
|
||||
/// <summary>
|
||||
/// 注册一个弹出容器
|
||||
/// </summary>
|
||||
/// <param name="id">页面ID</param>
|
||||
/// <param name="container">容器实例</param>
|
||||
public static void RegisterContainer(string id, IPopupContainer container) {
|
||||
if (containers.ContainsKey(id)) containers[id] = container;
|
||||
else containers.Add(id, container);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除一个弹出容器
|
||||
/// </summary>
|
||||
/// <param name="id">容器ID</param>
|
||||
public static void UnregisterContainer(string id) {
|
||||
if (containers.ContainsKey(id)) containers.Remove(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置当前的弹出容器
|
||||
/// </summary>
|
||||
/// <param name="id">容器ID</param>
|
||||
public static void SetCurrentContainer(string id) {
|
||||
currentContainer = id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 显示
|
||||
/// </summary>
|
||||
public void Show() {
|
||||
if (string.IsNullOrEmpty(currentContainer) || !containers.ContainsKey(currentContainer)) return;
|
||||
mine = containers[currentContainer];
|
||||
mine.Show(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 显示并直接点击开始
|
||||
/// </summary>
|
||||
public void ShowAndStart() {
|
||||
if (string.IsNullOrEmpty(currentContainer) || !containers.ContainsKey(currentContainer)) return;
|
||||
mine = containers[currentContainer];
|
||||
mine.ShowAndStart(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 窗体标题
|
||||
/// </summary>
|
||||
/// <returns>返回具体的标题</returns>
|
||||
public virtual string GetTitle() {
|
||||
return "TITLE";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 点击确定时的回调,由程序自己
|
||||
/// </summary>
|
||||
/// <returns>返回一个任务,任务预期返回类型为bool,表示是否关闭Popup</returns>
|
||||
public virtual Task<bool> Start() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新进度显示
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
protected void UpdateProgress(string message) {
|
||||
mine?.UpdateProgress(message);
|
||||
}
|
||||
}
|
||||
}
|
109
src/Views/Controls/TextEdit.cs
Normal file
109
src/Views/Controls/TextEdit.cs
Normal file
|
@ -0,0 +1,109 @@
|
|||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace SourceGit.Views.Controls {
|
||||
|
||||
/// <summary>
|
||||
/// 扩展默认TextBox
|
||||
/// </summary>
|
||||
public class TextEdit : TextBox {
|
||||
private bool isPlaceholderShow = false;
|
||||
|
||||
public static readonly DependencyProperty PlaceholderProperty = DependencyProperty.Register(
|
||||
"Placeholder",
|
||||
typeof(string),
|
||||
typeof(TextEdit),
|
||||
new PropertyMetadata(""));
|
||||
|
||||
public string Placeholder {
|
||||
get { return (string)GetValue(PlaceholderProperty); }
|
||||
set { SetValue(PlaceholderProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty PlaceholderBaselineProperty = DependencyProperty.Register(
|
||||
"PlaceholderBaseline",
|
||||
typeof(AlignmentY),
|
||||
typeof(TextEdit),
|
||||
new PropertyMetadata(AlignmentY.Center));
|
||||
|
||||
public AlignmentY PlaceholderBaseline {
|
||||
get { return (AlignmentY)GetValue(PlaceholderBaselineProperty); }
|
||||
set { SetValue(PlaceholderBaselineProperty, value); }
|
||||
}
|
||||
|
||||
public TextEdit() {
|
||||
TextChanged += OnTextChanged;
|
||||
SelectionChanged += OnSelectionChanged;
|
||||
}
|
||||
|
||||
protected override void OnRender(DrawingContext dc) {
|
||||
base.OnRender(dc);
|
||||
|
||||
if (string.IsNullOrEmpty(Text) && !string.IsNullOrEmpty(Placeholder)) {
|
||||
isPlaceholderShow = true;
|
||||
|
||||
var text = new FormattedText(
|
||||
Placeholder,
|
||||
CultureInfo.CurrentCulture,
|
||||
FlowDirection.LeftToRight,
|
||||
new Typeface(FontFamily, FontStyle, FontWeight, FontStretches.Normal),
|
||||
FontSize,
|
||||
FindResource("Brush.FG2") as Brush,
|
||||
VisualTreeHelper.GetDpi(this).PixelsPerDip);
|
||||
|
||||
switch (PlaceholderBaseline) {
|
||||
case AlignmentY.Top:
|
||||
dc.DrawText(text, new Point(4, 4));
|
||||
break;
|
||||
case AlignmentY.Center:
|
||||
dc.DrawText(text, new Point(4, ActualHeight * .5 - text.Height * .5));
|
||||
break;
|
||||
default:
|
||||
dc.DrawText(text, new Point(4, ActualHeight - text.Height - 4));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
isPlaceholderShow = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTextChanged(object sender, TextChangedEventArgs e) {
|
||||
if (string.IsNullOrEmpty(Text) || isPlaceholderShow) InvalidateVisual();
|
||||
}
|
||||
|
||||
private void OnSelectionChanged(object sender, RoutedEventArgs e) {
|
||||
if (!IsFocused) return;
|
||||
|
||||
if (Mouse.LeftButton == MouseButtonState.Pressed && SelectionLength > 0) {
|
||||
var p = Mouse.GetPosition(this);
|
||||
if (p.X <= 8) {
|
||||
LineLeft();
|
||||
} else if (p.X >= ActualWidth - 8) {
|
||||
LineRight();
|
||||
}
|
||||
|
||||
if (p.Y <= 8) {
|
||||
LineUp();
|
||||
} else if (p.Y >= ActualHeight - 8) {
|
||||
LineDown();
|
||||
}
|
||||
} else {
|
||||
var rect = GetRectFromCharacterIndex(CaretIndex);
|
||||
if (rect.Left <= 0) {
|
||||
ScrollToHorizontalOffset(HorizontalOffset + rect.Left);
|
||||
} else if (rect.Right >= ActualWidth) {
|
||||
ScrollToHorizontalOffset(HorizontalOffset + rect.Right);
|
||||
}
|
||||
|
||||
if (rect.Top <= 0) {
|
||||
ScrollToVerticalOffset(VerticalOffset + rect.Top);
|
||||
} else if (rect.Bottom >= ActualHeight) {
|
||||
ScrollToVerticalOffset(VerticalOffset + rect.Bottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
228
src/Views/Controls/Tree.cs
Normal file
228
src/Views/Controls/Tree.cs
Normal file
|
@ -0,0 +1,228 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace SourceGit.Views.Controls {
|
||||
|
||||
/// <summary>
|
||||
/// 树
|
||||
/// </summary>
|
||||
public class Tree : TreeView {
|
||||
public static readonly DependencyProperty MultiSelectionProperty = DependencyProperty.Register(
|
||||
"MultiSelection",
|
||||
typeof(bool),
|
||||
typeof(Tree),
|
||||
new PropertyMetadata(false));
|
||||
|
||||
public bool MultiSelection {
|
||||
get { return (bool)GetValue(MultiSelectionProperty); }
|
||||
set { SetValue(MultiSelectionProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IndentProperty = DependencyProperty.Register(
|
||||
"Indent",
|
||||
typeof(double),
|
||||
typeof(TreeItem),
|
||||
new PropertyMetadata(16.0));
|
||||
|
||||
public double Indent {
|
||||
get { return (double)GetValue(IndentProperty); }
|
||||
set { SetValue(IndentProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly RoutedEvent SelectionChangedEvent = EventManager.RegisterRoutedEvent(
|
||||
"SelectionChanged",
|
||||
RoutingStrategy.Bubble,
|
||||
typeof(RoutedEventHandler),
|
||||
typeof(Tree));
|
||||
|
||||
public event RoutedEventHandler SelectionChanged {
|
||||
add { AddHandler(SelectionChangedEvent, value); }
|
||||
remove { RemoveHandler(SelectionChangedEvent, value); }
|
||||
}
|
||||
|
||||
public List<object> Selected {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Tree() {
|
||||
Selected = new List<object>();
|
||||
PreviewMouseDown += OnPreviewMouseDown;
|
||||
}
|
||||
|
||||
public TreeItem FindItem(DependencyObject elem) {
|
||||
if (elem == null) return null;
|
||||
if (elem is TreeItem) return elem as TreeItem;
|
||||
if (elem is Tree) return null;
|
||||
return FindItem(VisualTreeHelper.GetParent(elem));
|
||||
}
|
||||
|
||||
public void SelectAll() {
|
||||
SelectAllChildren(this);
|
||||
RaiseEvent(new RoutedEventArgs(SelectionChangedEvent));
|
||||
}
|
||||
|
||||
public void UnselectAll() {
|
||||
if (Selected.Count == 0) return;
|
||||
|
||||
UnselectAllChildren(this);
|
||||
Selected.Clear();
|
||||
RaiseEvent(new RoutedEventArgs(SelectionChangedEvent));
|
||||
}
|
||||
|
||||
protected override DependencyObject GetContainerForItemOverride() {
|
||||
return new TreeItem(0, Indent);
|
||||
}
|
||||
|
||||
protected override bool IsItemItsOwnContainerOverride(object item) {
|
||||
return item is TreeItem;
|
||||
}
|
||||
|
||||
protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue) {
|
||||
base.OnItemsSourceChanged(oldValue, newValue);
|
||||
|
||||
if (Selected.Count > 0) {
|
||||
Selected.Clear();
|
||||
RaiseEvent(new RoutedEventArgs(SelectionChangedEvent));
|
||||
}
|
||||
}
|
||||
|
||||
private TreeItem FindItemByDataContext(ItemsControl control, object data) {
|
||||
if (control == null) return null;
|
||||
|
||||
for (int i = 0; i < control.Items.Count; i++) {
|
||||
var child = control.ItemContainerGenerator.ContainerFromIndex(i) as TreeItem;
|
||||
if (control.Items[i] == data) return child;
|
||||
|
||||
var found = FindItemByDataContext(child, data);
|
||||
if (found != null) return found;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void OnPreviewMouseDown(object sender, MouseButtonEventArgs e) {
|
||||
var hit = VisualTreeHelper.HitTest(this, e.GetPosition(this));
|
||||
if (hit == null || hit.VisualHit == null) return;
|
||||
|
||||
var item = FindItem(hit.VisualHit);
|
||||
if (item == null) return;
|
||||
|
||||
if (!MultiSelection) {
|
||||
if (item.IsChecked) return;
|
||||
AddSelected(item, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) {
|
||||
if (item.IsChecked) {
|
||||
RemoveSelected(item);
|
||||
} else {
|
||||
AddSelected(item, false);
|
||||
}
|
||||
} else if ((Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) && Selected.Count > 0) {
|
||||
var last = FindItemByDataContext(this, Selected.Last());
|
||||
if (last == item) return;
|
||||
|
||||
var lastPos = last.PointToScreen(new Point(0, 0));
|
||||
var curPos = item.PointToScreen(new Point(0, 0));
|
||||
if (lastPos.Y > curPos.Y) {
|
||||
SelectRange(this, item, last);
|
||||
} else {
|
||||
SelectRange(this, last, item);
|
||||
}
|
||||
|
||||
AddSelected(item, false);
|
||||
} else if (e.RightButton == MouseButtonState.Pressed) {
|
||||
if (item.IsChecked) return;
|
||||
AddSelected(item, true);
|
||||
} else {
|
||||
if (item.IsChecked && Selected.Count == 1) return;
|
||||
AddSelected(item, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddSelected(TreeItem item, bool removeOthers) {
|
||||
if (removeOthers && Selected.Count > 0) {
|
||||
UnselectAllChildren(this);
|
||||
Selected.Clear();
|
||||
}
|
||||
|
||||
item.IsChecked = true;
|
||||
Selected.Add(item.DataContext);
|
||||
RaiseEvent(new RoutedEventArgs(SelectionChangedEvent));
|
||||
}
|
||||
|
||||
private void RemoveSelected(TreeItem item) {
|
||||
item.IsChecked = false;
|
||||
Selected.Remove(item.DataContext);
|
||||
RaiseEvent(new RoutedEventArgs(SelectionChangedEvent));
|
||||
}
|
||||
|
||||
private void SelectAllChildren(ItemsControl control) {
|
||||
for (int i = 0; i < control.Items.Count; i++) {
|
||||
var child = control.ItemContainerGenerator.ContainerFromIndex(i) as TreeItem;
|
||||
if (child == null) continue;
|
||||
|
||||
child.IsChecked = true;
|
||||
Selected.Add(control.Items[i]);
|
||||
SelectAllChildren(child);
|
||||
}
|
||||
}
|
||||
|
||||
private void UnselectAllChildren(ItemsControl control) {
|
||||
for (int i = 0; i < control.Items.Count; i++) {
|
||||
var child = control.ItemContainerGenerator.ContainerFromIndex(i) as TreeItem;
|
||||
if (child == null) continue;
|
||||
if (child.IsChecked) child.IsChecked = false;
|
||||
UnselectAllChildren(child);
|
||||
}
|
||||
}
|
||||
|
||||
private int SelectRange(ItemsControl control, TreeItem from, TreeItem to, int matches = 0) {
|
||||
for (int i = 0; i < control.Items.Count; i++) {
|
||||
var child = control.ItemContainerGenerator.ContainerFromIndex(i) as TreeItem;
|
||||
if (child == null) continue;
|
||||
|
||||
if (matches == 1) {
|
||||
if (child == to) return 2;
|
||||
Selected.Add(control.Items[i]);
|
||||
child.IsChecked = true;
|
||||
if (TryEndRangeSelection(child, to)) return 2;
|
||||
} else if (child == from) {
|
||||
matches = 1;
|
||||
if (TryEndRangeSelection(child, to)) return 2;
|
||||
} else {
|
||||
matches = SelectRange(child, from, to, matches);
|
||||
if (matches == 2) return 2;
|
||||
}
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
private bool TryEndRangeSelection(ItemsControl control, TreeItem end) {
|
||||
for (int i = 0; i < control.Items.Count; i++) {
|
||||
var child = control.ItemContainerGenerator.ContainerFromIndex(i) as TreeItem;
|
||||
if (child == null) continue;
|
||||
|
||||
if (child == end) {
|
||||
return true;
|
||||
} else {
|
||||
Selected.Add(control.Items[i]);
|
||||
child.IsChecked = true;
|
||||
|
||||
var ended = TryEndRangeSelection(child, end);
|
||||
if (ended) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
41
src/Views/Controls/TreeItem.cs
Normal file
41
src/Views/Controls/TreeItem.cs
Normal file
|
@ -0,0 +1,41 @@
|
|||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SourceGit.Views.Controls {
|
||||
|
||||
/// <summary>
|
||||
/// 树节点
|
||||
/// </summary>
|
||||
public class TreeItem : TreeViewItem {
|
||||
|
||||
public static readonly DependencyProperty IsCheckedProperty = DependencyProperty.Register(
|
||||
"IsChecked",
|
||||
typeof(bool),
|
||||
typeof(TreeItem),
|
||||
new PropertyMetadata(false));
|
||||
|
||||
public bool IsChecked {
|
||||
get { return (bool)GetValue(IsCheckedProperty); }
|
||||
set { SetValue(IsCheckedProperty, value); }
|
||||
}
|
||||
|
||||
private int depth = 0;
|
||||
private double indent = 16;
|
||||
|
||||
public TreeItem(int depth, double indent) {
|
||||
this.depth = depth;
|
||||
this.indent = indent;
|
||||
|
||||
Padding = new Thickness(indent * depth, 0, 0, 0);
|
||||
RequestBringIntoView += (o, e) => e.Handled = true;
|
||||
}
|
||||
|
||||
protected override DependencyObject GetContainerForItemOverride() {
|
||||
return new TreeItem(depth + 1, indent);
|
||||
}
|
||||
|
||||
protected override bool IsItemItsOwnContainerOverride(object item) {
|
||||
return item is TreeItem;
|
||||
}
|
||||
}
|
||||
}
|
36
src/Views/Controls/WindowBorder.cs
Normal file
36
src/Views/Controls/WindowBorder.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace SourceGit.Views.Controls {
|
||||
|
||||
/// <summary>
|
||||
/// 主窗体Border
|
||||
/// </summary>
|
||||
public class WindowBorder : Border {
|
||||
|
||||
public WindowBorder() {
|
||||
Background = FindResource("Brush.Window") as Brush;
|
||||
BorderBrush = FindResource("Brush.Border0") as Brush;
|
||||
BorderThickness = new Thickness(1);
|
||||
Margin = new Thickness(0);
|
||||
|
||||
Loaded += (o, e) => {
|
||||
var owner = Parent as Window;
|
||||
if (owner != null) {
|
||||
owner.StateChanged += (o1, e1) => {
|
||||
if (owner.WindowState == WindowState.Maximized) {
|
||||
BorderThickness = new Thickness(0);
|
||||
Margin = new Thickness(
|
||||
(SystemParameters.MaximizedPrimaryScreenWidth - SystemParameters.WorkArea.Width) / 2
|
||||
);
|
||||
} else {
|
||||
BorderThickness = new Thickness(1);
|
||||
Margin = new Thickness(0);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
18
src/Views/Converters/BoolToCollapsed.cs
Normal file
18
src/Views/Converters/BoolToCollapsed.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace SourceGit.Views.Converters {
|
||||
|
||||
public class BoolToCollapsed : IValueConverter {
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
|
||||
return (bool)value ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
19
src/Views/Converters/BranchToName.cs
Normal file
19
src/Views/Converters/BranchToName.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace SourceGit.Views.Converters {
|
||||
|
||||
public class BranchToName : IValueConverter {
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
|
||||
var b = value as Models.Branch;
|
||||
if (b == null) return "";
|
||||
return string.IsNullOrEmpty(b.Remote) ? b.Name : $"{b.Remote}/{b.Name}";
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
17
src/Views/Converters/InverseBool.cs
Normal file
17
src/Views/Converters/InverseBool.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace SourceGit.Views.Converters {
|
||||
|
||||
public class InverseBool : IValueConverter {
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
|
||||
return !(bool)value;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
21
src/Views/Converters/PureFileName.cs
Normal file
21
src/Views/Converters/PureFileName.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace SourceGit.Views.Converters {
|
||||
|
||||
/// <summary>
|
||||
/// 将路径转换为纯文件名
|
||||
/// </summary>
|
||||
public class PureFileName : IValueConverter {
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
|
||||
return Path.GetFileName(value as string);
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
21
src/Views/Converters/PureFolderName.cs
Normal file
21
src/Views/Converters/PureFolderName.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace SourceGit.Views.Converters {
|
||||
|
||||
/// <summary>
|
||||
/// 将路径转换为纯目录
|
||||
/// </summary>
|
||||
public class PureFolderName : IValueConverter {
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
|
||||
return Path.GetDirectoryName(value as string);
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
98
src/Views/FolderBrowser.xaml
Normal file
98
src/Views/FolderBrowser.xaml
Normal file
|
@ -0,0 +1,98 @@
|
|||
<Window x:Class="SourceGit.Views.FolderBrowser"
|
||||
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:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
mc:Ignorable="d"
|
||||
Title="{StaticResource Text.FolderDialog}"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
ResizeMode="NoResize"
|
||||
Height="400" Width="400">
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome UseAeroCaptionButtons="False" CornerRadius="0" CaptionHeight="28" ResizeBorderThickness="1"/>
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
<controls:WindowBorder>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="48"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Title Bar -->
|
||||
<Grid Grid.Row="0" Background="{StaticResource Brush.TitleBar}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- ICON -->
|
||||
<Path Grid.Column="0" Width="16" Height="16" Margin="6,0" Data="{StaticResource Icon.Folder.Open}"/>
|
||||
|
||||
<!-- Title -->
|
||||
<TextBlock Grid.Column="1" Text="{Binding ElementName=me, Path=Description}"/>
|
||||
|
||||
<!-- Close -->
|
||||
<controls:IconButton
|
||||
Grid.Column="3"
|
||||
Click="Quit"
|
||||
Width="32" Height="32"
|
||||
Padding="9"
|
||||
WindowChrome.IsHitTestVisibleInChrome="True"
|
||||
Icon="{StaticResource Icon.Close}"
|
||||
HoverBackground="Red"/>
|
||||
</Grid>
|
||||
|
||||
<!-- Selected -->
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="8,4">
|
||||
<TextBlock Text="{StaticResource Text.FolderDialog.Selected}"/>
|
||||
<TextBlock x:Name="txtSelected" Text="NONE" Foreground="{StaticResource Brush.FG2}" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- File System Tree -->
|
||||
<Border
|
||||
Grid.Row="2"
|
||||
Margin="8,0"
|
||||
Background="{StaticResource Brush.Contents}"
|
||||
BorderBrush="{StaticResource Brush.Border0}"
|
||||
BorderThickness="1">
|
||||
<controls:Tree x:Name="tree" FontFamily="Consolas" ItemsSource="{Binding ElementName=me, Path=Nodes}" SelectionChanged="OnTreeSelectionChanged">
|
||||
<controls:Tree.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type controls:TreeItem}" BasedOn="{StaticResource Style.TreeItem}">
|
||||
<EventSetter Event="Expanded" Handler="OnTreeNodeExpanded"/>
|
||||
</Style>
|
||||
</controls:Tree.ItemContainerStyle>
|
||||
|
||||
<controls:Tree.ItemTemplate>
|
||||
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
|
||||
<StackPanel Orientation="Horizontal" Height="24">
|
||||
<Path x:Name="Icon" Width="14" Height="14" Fill="Goldenrod" Data="{StaticResource Icon.Folder.Fill}"/>
|
||||
<TextBlock Text="{Binding Name}" Margin="6,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<HierarchicalDataTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type controls:TreeItem}}, Path=IsExpanded}" Value="True">
|
||||
<Setter TargetName="Icon" Property="Data" Value="{StaticResource Icon.Folder.Open}"/>
|
||||
</DataTrigger>
|
||||
</HierarchicalDataTemplate.Triggers>
|
||||
</HierarchicalDataTemplate>
|
||||
</controls:Tree.ItemTemplate>
|
||||
</controls:Tree>
|
||||
</Border>
|
||||
|
||||
<!-- Options -->
|
||||
<Border Grid.Row="3">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
|
||||
<Button x:Name="btnSure" IsEnabled="False" Width="100" Height="32" Content="{StaticResource Text.Sure}" Background="{StaticResource Brush.Accent1}" BorderBrush="{StaticResource Brush.FG1}"/>
|
||||
<Button Click="Quit" Width="100" Height="32" Margin="8,0,0,0" Content="{StaticResource Text.Cancel}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</controls:WindowBorder>
|
||||
</Window>
|
102
src/Views/FolderBrowser.xaml.cs
Normal file
102
src/Views/FolderBrowser.xaml.cs
Normal file
|
@ -0,0 +1,102 @@
|
|||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
|
||||
namespace SourceGit.Views {
|
||||
|
||||
/// <summary>
|
||||
/// 目录选择对话框
|
||||
/// </summary>
|
||||
public partial class FolderBrowser : Window {
|
||||
|
||||
/// <summary>
|
||||
/// 目录树节点.
|
||||
/// </summary>
|
||||
public class Node : INotifyPropertyChanged {
|
||||
public string Name { get; set; }
|
||||
public string Path { get; set; }
|
||||
public ObservableCollection<Node> Children { get; set; }
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public Node(string name, string path) {
|
||||
Name = name;
|
||||
Path = path;
|
||||
Children = new ObservableCollection<Node>();
|
||||
}
|
||||
|
||||
public void CollectChildren() {
|
||||
Children.Clear();
|
||||
|
||||
try {
|
||||
var dir = new DirectoryInfo(Path);
|
||||
var subs = dir.GetDirectories();
|
||||
|
||||
foreach (var sub in subs) {
|
||||
if ((sub.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) continue;
|
||||
Children.Add(new Node(sub.Name, sub.FullName));
|
||||
}
|
||||
} catch { }
|
||||
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Children"));
|
||||
}
|
||||
}
|
||||
|
||||
public string Description { get; set; }
|
||||
public ObservableCollection<Node> Nodes { get; set; }
|
||||
|
||||
public FolderBrowser(string description, Action<string> onOK) {
|
||||
Description = description;
|
||||
Nodes = new ObservableCollection<Node>();
|
||||
|
||||
var drives = DriveInfo.GetDrives();
|
||||
foreach (var d in drives) {
|
||||
var node = new Node(d.Name, d.Name);
|
||||
node.CollectChildren();
|
||||
Nodes.Add(node);
|
||||
}
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
btnSure.Click += (o, e) => {
|
||||
if (tree.Selected.Count == 0) return;
|
||||
var node = tree.Selected[0] as Node;
|
||||
onOK?.Invoke(node.Path);
|
||||
Close();
|
||||
};
|
||||
}
|
||||
|
||||
public static void Open(Window owner, string description, Action<string> onOK) {
|
||||
var dialog = new FolderBrowser(description, onOK);
|
||||
if (owner == null) dialog.Owner = Application.Current.MainWindow;
|
||||
else dialog.Owner = owner;
|
||||
dialog.ShowDialog();
|
||||
}
|
||||
|
||||
private void OnTreeNodeExpanded(object sender, RoutedEventArgs e) {
|
||||
var item = sender as Controls.TreeItem;
|
||||
if (item == null) return;
|
||||
|
||||
var node = item.DataContext as Node;
|
||||
if (node == null) return;
|
||||
|
||||
foreach (var c in node.Children) c.CollectChildren();
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void OnTreeSelectionChanged(object sender, RoutedEventArgs e) {
|
||||
if (tree.Selected.Count == 0) {
|
||||
btnSure.IsEnabled = false;
|
||||
txtSelected.Text = "NONE";
|
||||
} else {
|
||||
btnSure.IsEnabled = true;
|
||||
txtSelected.Text = (tree.Selected[0] as Node).Path;
|
||||
}
|
||||
}
|
||||
|
||||
private void Quit(object sender, RoutedEventArgs e) {
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
125
src/Views/Histories.xaml
Normal file
125
src/Views/Histories.xaml
Normal file
|
@ -0,0 +1,125 @@
|
|||
<Window x:Class="SourceGit.Views.Histories"
|
||||
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:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
xmlns:models="clr-namespace:SourceGit.Models"
|
||||
xmlns:widgets="clr-namespace:SourceGit.Views.Widgets"
|
||||
mc:Ignorable="d"
|
||||
Title="{StaticResource Text.FileHistory}"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
MinHeight="600" MinWidth="800">
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome UseAeroCaptionButtons="False" CornerRadius="0" CaptionHeight="28" ResizeBorderThickness="1"/>
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
<controls:WindowBorder>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Title Bar -->
|
||||
<Grid Grid.Row="0" Background="{StaticResource Brush.TitleBar}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Icon -->
|
||||
<Path Grid.Column="0" Margin="6,0" Width="16" Height="16" Data="{StaticResource Icon.Histories}"/>
|
||||
|
||||
<!-- Title -->
|
||||
<TextBlock Grid.Column="1" Text="{StaticResource Text.FileHistory}"/>
|
||||
|
||||
<!-- Window Commands -->
|
||||
<StackPanel Grid.Column="3" Orientation="Horizontal" WindowChrome.IsHitTestVisibleInChrome="True">
|
||||
<controls:IconButton Click="Minimize" Width="28" Padding="8" Icon="{StaticResource Icon.Minimize}" HoverBackground="#40000000" Opacity="1"/>
|
||||
<controls:IconButton Click="MaximizeOrRestore" Width="28" Padding="8" Icon="{StaticResource Icon.Maximize}" HoverBackground="#40000000" Opacity="1"/>
|
||||
<controls:IconButton Click="Quit" Width="28" Padding="8" Icon="{StaticResource Icon.Close}" HoverBackground="Red" Opacity="1"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Body -->
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="300" MinWidth="300" MaxWidth="600"/>
|
||||
<ColumnDefinition Width="1"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Commit List -->
|
||||
<DataGrid
|
||||
x:Name="commitList"
|
||||
Grid.Column="0"
|
||||
Background="{StaticResource Brush.Contents}"
|
||||
SelectionMode="Single"
|
||||
SelectionUnit="FullRow"
|
||||
SelectedCellsChanged="OnCommitSelectedChanged">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTemplateColumn Width="*">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate DataType="{x:Type models:Commit}">
|
||||
<Border BorderBrush="{StaticResource Brush.Border2}" BorderThickness="0,0,0,1" Padding="4">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="36"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<controls:Avatar
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Center"
|
||||
Width="32" Height="32"
|
||||
Email="{Binding Author.Email}"
|
||||
FallbackLabel="{Binding Author.Name}"/>
|
||||
|
||||
<Grid Grid.Column="1" Margin="8,0,0,0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="72"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="120"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Column="0" FontSize="11">
|
||||
<Hyperlink NavigateUri="{Binding SHA}" RequestNavigate="GotoCommit" Foreground="DarkOrange" ToolTip="GOTO COMMIT">
|
||||
<Run Text="{Binding ShortSHA, Mode=OneWay}"/>
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Grid.Column="1" Text="{Binding Author.Name}" FontSize="11" Foreground="{StaticResource Brush.FG2}"/>
|
||||
<TextBlock Grid.Column="2" Text="{Binding Author.Time}" FontSize="11" Foreground="{StaticResource Brush.FG2}"/>
|
||||
</Grid>
|
||||
|
||||
<TextBlock Grid.Row="1" Text="{Binding Subject}" Margin="0,6,0,0"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
<!-- Loading -->
|
||||
<controls:Loading Grid.Column="0" x:Name="loading" Width="48" Height="48" IsAnimating="True"/>
|
||||
|
||||
<!-- Splitter -->
|
||||
<GridSplitter Grid.Column="1" Width="1" HorizontalAlignment="Center" VerticalAlignment="Stretch" Background="Transparent"/>
|
||||
|
||||
<!-- Diff Viewer -->
|
||||
<widgets:DiffViewer Grid.Column="2" x:Name="diffViewer"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</controls:WindowBorder>
|
||||
</Window>
|
70
src/Views/Histories.xaml.cs
Normal file
70
src/Views/Histories.xaml.cs
Normal file
|
@ -0,0 +1,70 @@
|
|||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Navigation;
|
||||
|
||||
namespace SourceGit.Views {
|
||||
|
||||
/// <summary>
|
||||
/// 文件历史
|
||||
/// </summary>
|
||||
public partial class Histories : Window {
|
||||
private string repo = null;
|
||||
private string file = null;
|
||||
|
||||
public Histories(string repo, string file) {
|
||||
this.repo = repo;
|
||||
this.file = file;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
Task.Run(() => {
|
||||
var commits = new Commands.Commits(repo, $"-n 10000 -- \"{file}\"").Result();
|
||||
Dispatcher.Invoke(() => {
|
||||
loading.IsAnimating = false;
|
||||
loading.Visibility = Visibility.Collapsed;
|
||||
commitList.ItemsSource = commits;
|
||||
commitList.SelectedIndex = 0;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#region WINDOW_COMMANDS
|
||||
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
|
||||
|
||||
#region EVENTS
|
||||
private void OnCommitSelectedChanged(object sender, SelectedCellsChangedEventArgs e) {
|
||||
var commit = (sender as DataGrid).SelectedItem as Models.Commit;
|
||||
if (commit == null) return;
|
||||
|
||||
var start = $"{commit.SHA}^";
|
||||
if (commit.Parents.Count == 0) start = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
||||
|
||||
diffViewer.Diff(repo, new Widgets.DiffViewer.Option() {
|
||||
RevisionRange = new string[] { start, commit.SHA },
|
||||
Path = file
|
||||
});
|
||||
}
|
||||
|
||||
private void GotoCommit(object sender, RequestNavigateEventArgs e) {
|
||||
Models.Watcher.Get(repo).NavigateTo(e.Uri.OriginalString);
|
||||
e.Handled = true;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
65
src/Views/Launcher.xaml
Normal file
65
src/Views/Launcher.xaml
Normal file
|
@ -0,0 +1,65 @@
|
|||
<Window x:Class="SourceGit.Views.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:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
xmlns:widgets="clr-namespace:SourceGit.Views.Widgets"
|
||||
xmlns:models="clr-namespace:SourceGit.Models"
|
||||
mc:Ignorable="d"
|
||||
UseLayoutRounding="True"
|
||||
Title="{StaticResource Text.About.Title}"
|
||||
MinWidth="1280" MinHeight="720"
|
||||
Width="{Binding Source={x:Static models:Preference.Instance}, Path=Window.Width, Mode=TwoWay}"
|
||||
Height="{Binding Source={x:Static models:Preference.Instance}, Path=Window.Height, Mode=TwoWay}">
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome UseAeroCaptionButtons="False" CornerRadius="0" CaptionHeight="28" ResizeBorderThickness="1"/>
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
<controls:WindowBorder>
|
||||
<controls:DragDropAdornerLayer>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Titlebar -->
|
||||
<Grid Grid.Row="0" Background="{StaticResource Brush.TitleBar}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="200"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Bottom border -->
|
||||
<Rectangle Grid.Column="0" Grid.ColumnSpan="2" Fill="{StaticResource Brush.Border0}" Height="1" VerticalAlignment="Bottom"/>
|
||||
|
||||
<!-- Tabs -->
|
||||
<widgets:PageTabBar
|
||||
Grid.Column="0"
|
||||
x:Name="tabs"
|
||||
TabAdd="OnTabAdding"
|
||||
TabSelected="OnTabSelected"
|
||||
TabClosed="OnTabClosed"/>
|
||||
|
||||
<!-- Right controls -->
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Top" Height="27" WindowChrome.IsHitTestVisibleInChrome="True">
|
||||
<controls:IconButton Click="OpenPreference" Width="28" Padding="6" Icon="{StaticResource Icon.Preference}" ToolTip="{StaticResource Text.Launcher.Preference}"/>
|
||||
<controls:IconButton Click="OpenAbout" Width="28" Padding="6" Icon="{StaticResource Icon.Help}" ToolTip="{StaticResource Text.Launcher.About}"/>
|
||||
<controls:IconButton Click="Minimize" Width="28" Padding="8" Icon="{StaticResource Icon.Minimize}" HoverBackground="#40000000" Opacity="1"/>
|
||||
<controls:IconButton Click="MaximizeOrRestore" Width="28" Padding="8" Icon="{StaticResource Icon.Maximize}" HoverBackground="#40000000" Opacity="1"/>
|
||||
<controls:IconButton Click="Quit" Width="28" Padding="8" Icon="{StaticResource Icon.Close}" HoverBackground="Red" Opacity="1"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Contents -->
|
||||
<controls:PageContainer x:Name="container" Grid.Row="1"/>
|
||||
|
||||
<!-- Alerts -->
|
||||
<widgets:Exceptions
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Right" VerticalAlignment="Top"
|
||||
Width="330" Height="Auto"/>
|
||||
</controls:DragDropAdornerLayer>
|
||||
</controls:WindowBorder>
|
||||
</Window>
|
97
src/Views/Launcher.xaml.cs
Normal file
97
src/Views/Launcher.xaml.cs
Normal file
|
@ -0,0 +1,97 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
namespace SourceGit.Views {
|
||||
|
||||
/// <summary>
|
||||
/// 主窗体
|
||||
/// </summary>
|
||||
public partial class Launcher : Window {
|
||||
|
||||
public Launcher() {
|
||||
Models.Watcher.Opened += OpenRepository;
|
||||
InitializeComponent();
|
||||
OnTabAdding(null, null);
|
||||
}
|
||||
|
||||
#region OPEN_REPO
|
||||
private void OpenRepository(Models.Repository repo) {
|
||||
if (tabs.Goto(repo.Path)) return;
|
||||
|
||||
Task.Run(() => {
|
||||
var cmd = new Commands.Config(repo.Path);
|
||||
repo.GitFlow.Feature = cmd.Get("gitflow.prefix.feature");
|
||||
repo.GitFlow.Release = cmd.Get("gitflow.prefix.release");
|
||||
repo.GitFlow.Hotfix = cmd.Get("gitflow.prefix.hotfix");
|
||||
});
|
||||
|
||||
Commands.AutoFetch.Start(repo.Path);
|
||||
|
||||
var page = new Widgets.Dashboard(repo);
|
||||
var tab = new Widgets.PageTabItem(repo.Name, false, repo.Bookmark, repo.Path);
|
||||
container.Add(repo.Path, page);
|
||||
Controls.PopupWidget.RegisterContainer(repo.Path, page);
|
||||
|
||||
var front = container.Get(tabs.Current);
|
||||
if (front == null || front is Widgets.Dashboard) {
|
||||
tabs.Add(repo.Path, tab);
|
||||
} else {
|
||||
tabs.Replace(tabs.Current, repo.Path, tab);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region RIGHT_COMMANDS
|
||||
private void OpenPreference(object sender, RoutedEventArgs e) {
|
||||
var dialog = new Preference() { Owner = this };
|
||||
dialog.ShowDialog();
|
||||
}
|
||||
|
||||
private void OpenAbout(object sender, RoutedEventArgs e) {
|
||||
var dialog = new About() { Owner = this };
|
||||
dialog.ShowDialog();
|
||||
}
|
||||
|
||||
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) {
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region TAB_OPERATION
|
||||
private void OnTabAdding(object sender, RoutedEventArgs e) {
|
||||
var id = Guid.NewGuid().ToString();
|
||||
var tab = new Widgets.PageTabItem(App.Text("PageSwitcher.Welcome.Title"), true, 0, App.Text("PageSwitcher.Welcome.Tip"));
|
||||
var page = new Widgets.Welcome();
|
||||
|
||||
container.Add(id, page);
|
||||
tabs.Add(id, tab);
|
||||
Controls.PopupWidget.RegisterContainer(id, page);
|
||||
}
|
||||
|
||||
private void OnTabSelected(object sender, Widgets.PageTabBar.TabEventArgs e) {
|
||||
container.Goto(e.TabId);
|
||||
Controls.PopupWidget.SetCurrentContainer(e.TabId);
|
||||
}
|
||||
|
||||
private void OnTabClosed(object sender, Widgets.PageTabBar.TabEventArgs e) {
|
||||
Models.Watcher.Close(e.TabId);
|
||||
Commands.AutoFetch.Stop(e.TabId);
|
||||
container.Remove(e.TabId);
|
||||
Controls.PopupWidget.UnregisterContainer(e.TabId);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
69
src/Views/Popups/AddSubmodule.xaml
Normal file
69
src/Views/Popups/AddSubmodule.xaml
Normal file
|
@ -0,0 +1,69 @@
|
|||
<controls:PopupWidget
|
||||
x:Class="SourceGit.Views.Popups.AddSubmodule"
|
||||
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:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
xmlns:validations="clr-namespace:SourceGit.Views.Validations"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="500" Height="Auto">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.URL}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<controls:TextEdit
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
x:Name="txtURL"
|
||||
Height="24"
|
||||
Placeholder="{StaticResource Text.RepositoryURL}">
|
||||
<controls:TextEdit.Text>
|
||||
<Binding ElementName="me" Path="URL" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay">
|
||||
<Binding.ValidationRules>
|
||||
<validations:GitURL/>
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</controls:TextEdit.Text>
|
||||
</controls:TextEdit>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.ParentFolder}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<controls:TextEdit
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
x:Name="txtPath"
|
||||
Height="24"
|
||||
Placeholder="{StaticResource Text.ParentFolder.Placeholder}">
|
||||
<controls:TextEdit.Text>
|
||||
<Binding ElementName="me" Path="Path" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay">
|
||||
<Binding.ValidationRules>
|
||||
<validations:SubmodulePath/>
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</controls:TextEdit.Text>
|
||||
</controls:TextEdit>
|
||||
|
||||
<CheckBox
|
||||
Grid.Row="2" Grid.Column="1"
|
||||
Margin="0,4,0,0"
|
||||
x:Name="chkNested"
|
||||
IsChecked="True"
|
||||
Content="{StaticResource Text.Submodule.FetchNested}"/>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
40
src/Views/Popups/AddSubmodule.xaml.cs
Normal file
40
src/Views/Popups/AddSubmodule.xaml.cs
Normal file
|
@ -0,0 +1,40 @@
|
|||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
/// <summary>
|
||||
/// 新增子模块面板
|
||||
/// </summary>
|
||||
public partial class AddSubmodule : Controls.PopupWidget {
|
||||
private string repo = null;
|
||||
|
||||
public string URL { get; set; }
|
||||
public string Path { get; set; }
|
||||
|
||||
public AddSubmodule(string repo) {
|
||||
this.repo = repo;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
return App.Text("Submodule.Add");
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
txtURL.GetBindingExpression(TextBox.TextProperty).UpdateSource();
|
||||
if (Validation.GetHasError(txtURL)) return null;
|
||||
|
||||
txtPath.GetBindingExpression(TextBox.TextProperty).UpdateSource();
|
||||
if (Validation.GetHasError(txtPath)) return null;
|
||||
|
||||
var recursive = chkNested.IsChecked == true;
|
||||
|
||||
return Task.Run(() => {
|
||||
Models.Watcher.SetEnabled(repo, false);
|
||||
var succ = new Commands.Submodule(repo).Add(URL, Path, recursive, UpdateProgress);
|
||||
Models.Watcher.SetEnabled(repo, true);
|
||||
return succ;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
94
src/Views/Popups/Apply.xaml
Normal file
94
src/Views/Popups/Apply.xaml
Normal file
|
@ -0,0 +1,94 @@
|
|||
<controls:PopupWidget
|
||||
x:Class="SourceGit.Views.Popups.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:converters="clr-namespace:SourceGit.Views.Converters"
|
||||
xmlns:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
xmlns:models="clr-namespace:SourceGit.Models"
|
||||
xmlns:validations="clr-namespace:SourceGit.Views.Validations"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="800" Height="Auto">
|
||||
<Grid>
|
||||
<Grid.Resources>
|
||||
<converters:InverseBool x:Key="InverseBool"/>
|
||||
</Grid.Resources>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.Apply.File}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<Grid
|
||||
Grid.Row="0" Grid.Column="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<controls:TextEdit
|
||||
Grid.Column="0"
|
||||
x:Name="txtPath"
|
||||
Height="24"
|
||||
Placeholder="{StaticResource Text.Apply.File.Placeholder}">
|
||||
<controls:TextEdit.Text>
|
||||
<Binding Path="File" ElementName="me" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay">
|
||||
<Binding.ValidationRules>
|
||||
<validations:PatchFile/>
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</controls:TextEdit.Text>
|
||||
</controls:TextEdit>
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
Click="OpenFileBrowser"
|
||||
Height="24" Width="24"
|
||||
Margin="2,0,0,0">
|
||||
<Path Width="14" Height="14" Data="{StaticResource Icon.Folder}"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.Apply.WS}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<ComboBox
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
x:Name="cmbWSOption"
|
||||
ItemsSource="{Binding Source={x:Static models:WhitespaceOption.Supported}}"
|
||||
IsEnabled="{Binding ElementName=chkIngoreWS, Path=IsChecked, Converter={StaticResource InverseBool}}"
|
||||
SelectedIndex="0"
|
||||
Height="24"
|
||||
VerticalAlignment="Center">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Height="20">
|
||||
<TextBlock Text="{Binding Name}" Margin="4,0"/>
|
||||
<TextBlock Text="{Binding Desc}" Margin="4,0" FontSize="11" Foreground="{StaticResource Brush.FG2}"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<CheckBox
|
||||
Grid.Row="2" Grid.Column="1"
|
||||
Margin="0,4,0,0"
|
||||
x:Name="chkIngoreWS"
|
||||
IsChecked="True"
|
||||
Content="{StaticResource Text.Apply.IgnoreWS}"/>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
50
src/Views/Popups/Apply.xaml.cs
Normal file
50
src/Views/Popups/Apply.xaml.cs
Normal file
|
@ -0,0 +1,50 @@
|
|||
using Microsoft.Win32;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
/// <summary>
|
||||
/// 应用补丁
|
||||
/// </summary>
|
||||
public partial class Apply : Controls.PopupWidget {
|
||||
private string repo = null;
|
||||
public string File { get; set; }
|
||||
|
||||
public Apply(string repo) {
|
||||
this.repo = repo;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
return App.Text("Apply.Title");
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
txtPath.GetBindingExpression(TextBox.TextProperty).UpdateSource();
|
||||
if (Validation.GetHasError(txtPath)) return null;
|
||||
|
||||
var ignoreWS = chkIngoreWS.IsChecked == true;
|
||||
var wsMode = (cmbWSOption.SelectedItem as Models.WhitespaceOption).Arg;
|
||||
|
||||
return Task.Run(() => {
|
||||
Models.Watcher.SetEnabled(repo, false);
|
||||
var succ = new Commands.Apply(repo, File, ignoreWS, wsMode).Exec();
|
||||
Models.Watcher.SetEnabled(repo, true);
|
||||
return succ;
|
||||
});
|
||||
}
|
||||
|
||||
private void OpenFileBrowser(object sender, System.Windows.RoutedEventArgs e) {
|
||||
var dialog = new OpenFileDialog();
|
||||
dialog.Filter = "Patch File|*.patch";
|
||||
dialog.Title = App.Text("Apply.File.Placeholder");
|
||||
dialog.InitialDirectory = repo;
|
||||
dialog.CheckFileExists = true;
|
||||
|
||||
if (dialog.ShowDialog() == true) {
|
||||
File = dialog.FileName;
|
||||
txtPath.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
41
src/Views/Popups/CherryPick.xaml
Normal file
41
src/Views/Popups/CherryPick.xaml
Normal file
|
@ -0,0 +1,41 @@
|
|||
<controls:PopupWidget
|
||||
x:Class="SourceGit.Views.Popups.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"
|
||||
xmlns:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="800" Height="Auto">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.CherryPick.Commit}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<StackPanel
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
VerticalAlignment="Center">
|
||||
<Path Width="14" Height="14" Data="{StaticResource Icon.Commit}"/>
|
||||
<TextBlock x:Name="txtCommit" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
Margin="0,4,0,0"
|
||||
x:Name="chkCommit"
|
||||
IsChecked="True"
|
||||
Content="{StaticResource Text.CherryPick.CommitChanges}"/>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
35
src/Views/Popups/CherryPick.xaml.cs
Normal file
35
src/Views/Popups/CherryPick.xaml.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
/// <summary>
|
||||
/// 遴选面板
|
||||
/// </summary>
|
||||
public partial class CherryPick : Controls.PopupWidget {
|
||||
private string repo = null;
|
||||
private string commit = null;
|
||||
|
||||
public CherryPick(string repo, Models.Commit commit) {
|
||||
this.repo = repo;
|
||||
this.commit = commit.SHA;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
txtCommit.Text = $"{commit.ShortSHA} {commit.Subject}";
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
return App.Text("CherryPick.Title");
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
var noCommits = chkCommit.IsChecked != true;
|
||||
|
||||
return Task.Run(() => {
|
||||
Models.Watcher.SetEnabled(repo, false);
|
||||
new Commands.CherryPick(repo, commit, noCommits).Exec();
|
||||
Models.Watcher.SetEnabled(repo, true);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
128
src/Views/Popups/Clone.xaml
Normal file
128
src/Views/Popups/Clone.xaml
Normal file
|
@ -0,0 +1,128 @@
|
|||
<controls:PopupWidget x:Class="SourceGit.Views.Popups.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:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
xmlns:validations="clr-namespace:SourceGit.Views.Validations"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="500" Height="Auto">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Text="{StaticResource Text.Clone.RemoteURL}"
|
||||
Margin="0,0,4,0"
|
||||
HorizontalAlignment="Right"/>
|
||||
<controls:TextEdit
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
x:Name="txtUrl"
|
||||
Height="24"
|
||||
Placeholder="{StaticResource Text.Clone.RemoteURL.Placeholder}">
|
||||
<controls:TextEdit.Text>
|
||||
<Binding ElementName="me" Path="Uri" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay">
|
||||
<Binding.ValidationRules>
|
||||
<validations:GitURL/>
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</controls:TextEdit.Text>
|
||||
</controls:TextEdit>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1" Grid.Column="0"
|
||||
Text="{StaticResource Text.Clone.Folder}"
|
||||
Margin="0,0,4,0"
|
||||
HorizontalAlignment="Right"/>
|
||||
<Grid Grid.Row="1" Grid.Column="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="30"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<controls:TextEdit
|
||||
Grid.Column="0"
|
||||
x:Name="txtFolder"
|
||||
Height="24"
|
||||
Placeholder="{StaticResource Text.Clone.Folder.Placeholder}">
|
||||
<controls:TextEdit.Text>
|
||||
<Binding ElementName="me" Path="Folder" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay">
|
||||
<Binding.ValidationRules>
|
||||
<validations:CloneDir/>
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</controls:TextEdit.Text>
|
||||
</controls:TextEdit>
|
||||
|
||||
<controls:IconButton
|
||||
Grid.Column="1"
|
||||
Click="OnFolderSelectorClick"
|
||||
Width="24" Height="24"
|
||||
Margin="2,0,0,0" Padding="4"
|
||||
BorderBrush="{StaticResource Brush.Border1}"
|
||||
BorderThickness="1"
|
||||
Icon="{StaticResource Icon.Folder.Open}"/>
|
||||
</Grid>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2" Grid.Column="0"
|
||||
Text="{StaticResource Text.Clone.LocalName}"
|
||||
Margin="0,0,4,0"
|
||||
HorizontalAlignment="Right"/>
|
||||
<controls:TextEdit
|
||||
Grid.Row="2" Grid.Column="1"
|
||||
Height="24"
|
||||
x:Name="txtLocal"
|
||||
Placeholder="{StaticResource Text.Clone.LocalName.Placeholder}">
|
||||
<controls:TextEdit.Text>
|
||||
<Binding ElementName="me" Path="LocalName" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay">
|
||||
<Binding.ValidationRules>
|
||||
<validations:LocalRepositoryName/>
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</controls:TextEdit.Text>
|
||||
</controls:TextEdit>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3" Grid.Column="0"
|
||||
Text="{StaticResource Text.Clone.RemoteName}"
|
||||
Margin="0,0,4,0"
|
||||
HorizontalAlignment="Right"/>
|
||||
<controls:TextEdit
|
||||
Grid.Row="3" Grid.Column="1"
|
||||
x:Name="txtRemote"
|
||||
Height="24"
|
||||
Placeholder="{StaticResource Text.Clone.RemoteName.Placeholder}">
|
||||
<controls:TextEdit.Text>
|
||||
<Binding ElementName="me" Path="RemoteName" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay">
|
||||
<Binding.ValidationRules>
|
||||
<validations:RemoteName x:Name="ruleRemote"/>
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</controls:TextEdit.Text>
|
||||
</controls:TextEdit>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4" Grid.Column="0"
|
||||
Text="{StaticResource Text.Clone.AdditionalParam}"
|
||||
Margin="0,0,4,0"
|
||||
HorizontalAlignment="Right"/>
|
||||
<controls:TextEdit
|
||||
Grid.Row="4" Grid.Column="1"
|
||||
Height="24"
|
||||
Placeholder="{StaticResource Text.Clone.AdditionalParam.Placeholder}"
|
||||
Text="{Binding ElementName=me, Path=ExtraArgs, Mode=TwoWay}"/>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
63
src/Views/Popups/Clone.xaml.cs
Normal file
63
src/Views/Popups/Clone.xaml.cs
Normal file
|
@ -0,0 +1,63 @@
|
|||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
|
||||
/// <summary>
|
||||
/// 克隆
|
||||
/// </summary>
|
||||
public partial class Clone : Controls.PopupWidget {
|
||||
|
||||
public string Uri { get; set; }
|
||||
public string Folder { get; set; }
|
||||
public string LocalName { get; set; }
|
||||
public string RemoteName { get; set; }
|
||||
public string ExtraArgs { get; set; }
|
||||
|
||||
public Clone() {
|
||||
Folder = Models.Preference.Instance.Git.DefaultCloneDir;
|
||||
InitializeComponent();
|
||||
ruleRemote.IsOptional = true;
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
return App.Text("Clone");
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
var checks = new Controls.TextEdit[] { txtUrl, txtFolder, txtLocal, txtRemote };
|
||||
foreach (var edit in checks) {
|
||||
edit.GetBindingExpression(TextBox.TextProperty).UpdateSource();
|
||||
if (Validation.GetHasError(edit)) return null;
|
||||
}
|
||||
|
||||
return Task.Run(() => {
|
||||
var extras = string.IsNullOrEmpty(ExtraArgs) ? "" : ExtraArgs;
|
||||
if (!string.IsNullOrEmpty(RemoteName)) extras += $" --origin {RemoteName}";
|
||||
|
||||
var succ = new Commands.Clone(Folder, Uri, LocalName, extras, UpdateProgress).Exec();
|
||||
if (!succ) return false;
|
||||
|
||||
var path = Folder;
|
||||
if (!string.IsNullOrEmpty(LocalName)) {
|
||||
path += $"/{LocalName}";
|
||||
} else {
|
||||
var idx = Uri.LastIndexOfAny(new char[] { '\\', '/' });
|
||||
var name = Uri.Substring(idx + 1);
|
||||
path += $"/{name.Replace(".git", "")}";
|
||||
}
|
||||
|
||||
var repo = Models.Preference.Instance.AddRepository(path, path + "/.git", "");
|
||||
if (repo != null) Dispatcher.Invoke(() => Models.Watcher.Open(repo));
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void OnFolderSelectorClick(object sender, System.Windows.RoutedEventArgs e) {
|
||||
FolderBrowser.Open(null, App.Text("Clone.Folder.Placeholder"), path => {
|
||||
Folder = path;
|
||||
txtFolder.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
44
src/Views/Popups/Configure.xaml
Normal file
44
src/Views/Popups/Configure.xaml
Normal file
|
@ -0,0 +1,44 @@
|
|||
<controls:PopupWidget
|
||||
x:Class="SourceGit.Views.Popups.Configure"
|
||||
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:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="500" Height="Auto">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.Configure.User}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<controls:TextEdit
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
Text="{Binding ElementName=me, Path=UserName, Mode=TwoWay}"
|
||||
Height="24"
|
||||
Placeholder="{StaticResource Text.Configure.User.Placeholder}"/>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.Configure.Email}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<controls:TextEdit
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
Text="{Binding ElementName=me, Path=UserEmail, Mode=TwoWay}"
|
||||
Height="24"
|
||||
Placeholder="{StaticResource Text.Configure.Email.Placeholder}"/>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
39
src/Views/Popups/Configure.xaml.cs
Normal file
39
src/Views/Popups/Configure.xaml.cs
Normal file
|
@ -0,0 +1,39 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
/// <summary>
|
||||
/// 仓库配置
|
||||
/// </summary>
|
||||
public partial class Configure : Controls.PopupWidget {
|
||||
private string repo = null;
|
||||
|
||||
public string UserName { get; set; }
|
||||
public string UserEmail { get; set; }
|
||||
|
||||
public Configure(string repo) {
|
||||
this.repo = repo;
|
||||
|
||||
var cmd = new Commands.Config(repo);
|
||||
UserName = cmd.Get("user.name");
|
||||
UserEmail = cmd.Get("user.email");
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
return App.Text("Configure");
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
return Task.Run(() => {
|
||||
var cmd = new Commands.Config(repo);
|
||||
|
||||
var oldUser = cmd.Get("user.name");
|
||||
if (oldUser != UserName) cmd.Set("user.name", UserName);
|
||||
var oldEmail = cmd.Get("user.email");
|
||||
if (oldEmail != UserEmail) cmd.Set("user.email", UserEmail);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
77
src/Views/Popups/CreateBranch.xaml
Normal file
77
src/Views/Popups/CreateBranch.xaml
Normal file
|
@ -0,0 +1,77 @@
|
|||
<controls:PopupWidget
|
||||
x:Class="SourceGit.Views.Popups.CreateBranch"
|
||||
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:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
xmlns:converters="clr-namespace:SourceGit.Views.Converters"
|
||||
xmlns:validations="clr-namespace:SourceGit.Views.Validations"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="500" Height="Auto">
|
||||
<Grid>
|
||||
<Grid.Resources>
|
||||
<converters:InverseBool x:Key="InverseBool"/>
|
||||
</Grid.Resources>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.CreateBranch.BasedOn}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<StackPanel
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
VerticalAlignment="Center">
|
||||
<Path x:Name="iconBased" Width="14" Height="14" Data="{StaticResource Icon.Branch}"/>
|
||||
<TextBlock x:Name="txtBased" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.CreateBranch.Name}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<controls:TextEdit
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
x:Name="txtBranchName"
|
||||
Height="24"
|
||||
Placeholder="{StaticResource Text.CreateBranch.Name.Placeholder}">
|
||||
<controls:TextEdit.Text>
|
||||
<Binding ElementName="me" Path="BranchName" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay">
|
||||
<Binding.ValidationRules>
|
||||
<validations:BranchName x:Name="ruleBranch"/>
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</controls:TextEdit.Text>
|
||||
</controls:TextEdit>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.CreateBranch.LocalChanges}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal">
|
||||
<RadioButton Content="{StaticResource Text.CreateBranch.LocalChanges.StashAndReply}" GroupName="LocalChanges" IsChecked="{Binding AutoStash, ElementName=me}"/>
|
||||
<RadioButton Content="{StaticResource Text.CreateBranch.LocalChanges.Discard}" Margin="8,0,0,0" GroupName="LocalChanges" IsChecked="{Binding AutoStash, ElementName=me, Mode=OneWay, Converter={StaticResource InverseBool}}"/>
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox
|
||||
Grid.Row="3" Grid.Column="1"
|
||||
x:Name="chkCheckout"
|
||||
IsChecked="True"
|
||||
Content="{StaticResource Text.CreateBranch.Checkout}"/>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
83
src/Views/Popups/CreateBranch.xaml.cs
Normal file
83
src/Views/Popups/CreateBranch.xaml.cs
Normal file
|
@ -0,0 +1,83 @@
|
|||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
/// <summary>
|
||||
/// 新建分支面板
|
||||
/// </summary>
|
||||
public partial class CreateBranch : Controls.PopupWidget {
|
||||
private string repo = null;
|
||||
private string basedOn = null;
|
||||
|
||||
public string BranchName { get; set; }
|
||||
public bool AutoStash { get; set; }
|
||||
|
||||
public CreateBranch(Models.Repository repo, Models.Branch branch) {
|
||||
this.repo = repo.Path;
|
||||
this.basedOn = branch.Head;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
ruleBranch.Repo = repo;
|
||||
iconBased.Data = FindResource("Icon.Branch") as Geometry;
|
||||
txtBased.Text = !string.IsNullOrEmpty(branch.Remote) ? $"{branch.Remote}/{branch.Name}" : branch.Name;
|
||||
}
|
||||
|
||||
public CreateBranch(Models.Repository repo, Models.Commit commit) {
|
||||
this.repo = repo.Path;
|
||||
this.basedOn = commit.SHA;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
ruleBranch.Repo = repo;
|
||||
iconBased.Data = FindResource("Icon.Commit") as Geometry;
|
||||
txtBased.Text = $"{commit.ShortSHA} {commit.Subject}";
|
||||
}
|
||||
|
||||
public CreateBranch(Models.Repository repo, Models.Tag tag) {
|
||||
this.repo = repo.Path;
|
||||
this.basedOn = tag.SHA;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
ruleBranch.Repo = repo;
|
||||
iconBased.Data = FindResource("Icon.Tag") as Geometry;
|
||||
txtBased.Text = tag.Name;
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
return App.Text("CreateBranch");
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
txtBranchName.GetBindingExpression(TextBox.TextProperty).UpdateSource();
|
||||
if (Validation.GetHasError(txtBranchName)) return null;
|
||||
|
||||
var checkout = chkCheckout.IsChecked == true;
|
||||
|
||||
return Task.Run(() => {
|
||||
Models.Watcher.SetEnabled(repo, false);
|
||||
if (checkout) {
|
||||
if (AutoStash) {
|
||||
var changes = new Commands.LocalChanges(repo).Result();
|
||||
if (changes.Count > 0) {
|
||||
if (!new Commands.Stash(repo).Push(null, "NEWBRANCH_AUTO_STASH", true)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
AutoStash = true;
|
||||
}
|
||||
}
|
||||
|
||||
new Commands.Checkout(repo).Branch(BranchName, basedOn);
|
||||
if (AutoStash) new Commands.Stash(repo).Pop("stash@{0}");
|
||||
} else {
|
||||
new Commands.Branch(repo, BranchName).Create(basedOn);
|
||||
}
|
||||
Models.Watcher.SetEnabled(repo, true);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
69
src/Views/Popups/CreateTag.xaml
Normal file
69
src/Views/Popups/CreateTag.xaml
Normal file
|
@ -0,0 +1,69 @@
|
|||
<controls:PopupWidget
|
||||
x:Class="SourceGit.Views.Popups.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:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
xmlns:validations="clr-namespace:SourceGit.Views.Validations"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="800" Height="Auto">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="64"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.CreateTag.BasedOn}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<StackPanel
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
VerticalAlignment="Center">
|
||||
<Path x:Name="iconBased" Width="14" Height="14" Data="{StaticResource Icon.Branch}"/>
|
||||
<TextBlock x:Name="txtBased" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.CreateTag.Name}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<controls:TextEdit
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
x:Name="txtTagName"
|
||||
Height="24"
|
||||
Placeholder="{StaticResource Text.CreateTag.Name.Placeholder}">
|
||||
<controls:TextEdit.Text>
|
||||
<Binding ElementName="me" Path="TagName" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay">
|
||||
<Binding.ValidationRules>
|
||||
<validations:TagName x:Name="ruleTag"/>
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</controls:TextEdit.Text>
|
||||
</controls:TextEdit>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2" Grid.Column="0"
|
||||
Margin="0,6,8,0"
|
||||
Text="{StaticResource Text.CreateTag.Message}"
|
||||
HorizontalAlignment="Right" VerticalAlignment="Top"/>
|
||||
<controls:TextEdit
|
||||
Grid.Row="2" Grid.Column="1"
|
||||
Text="{Binding ElementName=me, Path=Message, Mode=TwoWay}"
|
||||
Height="56" Padding="0,2"
|
||||
AcceptsReturn="True"
|
||||
Placeholder="{StaticResource Text.CreateTag.Message.Placeholder}"
|
||||
PlaceholderBaseline="Top"/>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
55
src/Views/Popups/CreateTag.xaml.cs
Normal file
55
src/Views/Popups/CreateTag.xaml.cs
Normal file
|
@ -0,0 +1,55 @@
|
|||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
|
||||
/// <summary>
|
||||
/// 创建分支面板
|
||||
/// </summary>
|
||||
public partial class CreateTag : Controls.PopupWidget {
|
||||
private string repo = null;
|
||||
private string basedOn = null;
|
||||
|
||||
public string TagName { get; set; }
|
||||
public string Message { get; set; }
|
||||
|
||||
public CreateTag(Models.Repository repo, Models.Branch branch) {
|
||||
this.repo = repo.Path;
|
||||
this.basedOn = branch.Head;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
ruleTag.Tags = new Commands.Tags(repo.Path).Result();
|
||||
iconBased.Data = FindResource("Icon.Branch") as Geometry;
|
||||
txtBased.Text = !string.IsNullOrEmpty(branch.Remote) ? $"{branch.Remote}/{branch.Name}" : branch.Name;
|
||||
}
|
||||
|
||||
public CreateTag(Models.Repository repo, Models.Commit commit) {
|
||||
this.repo = repo.Path;
|
||||
this.basedOn = commit.SHA;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
ruleTag.Tags = new Commands.Tags(repo.Path).Result();
|
||||
iconBased.Data = FindResource("Icon.Commit") as Geometry;
|
||||
txtBased.Text = $"{commit.ShortSHA} {commit.Subject}";
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
return App.Text("CreateTag");
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
txtTagName.GetBindingExpression(TextBox.TextProperty).UpdateSource();
|
||||
if (Validation.GetHasError(txtTagName)) return null;
|
||||
|
||||
return Task.Run(() => {
|
||||
Models.Watcher.SetEnabled(repo, false);
|
||||
new Commands.Tag(repo).Add(TagName, basedOn, Message);
|
||||
Models.Watcher.SetEnabled(repo, true);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
33
src/Views/Popups/DeleteBranch.xaml
Normal file
33
src/Views/Popups/DeleteBranch.xaml
Normal file
|
@ -0,0 +1,33 @@
|
|||
<controls:PopupWidget
|
||||
x:Class="SourceGit.Views.Popups.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:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="500" Height="Auto">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.DeleteBranch.Branch}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<StackPanel
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
VerticalAlignment="Center">
|
||||
<Path Width="14" Height="14" Data="{StaticResource Icon.Branch}"/>
|
||||
<TextBlock x:Name="txtTarget" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
40
src/Views/Popups/DeleteBranch.xaml.cs
Normal file
40
src/Views/Popups/DeleteBranch.xaml.cs
Normal file
|
@ -0,0 +1,40 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
/// <summary>
|
||||
/// 删除分支确认
|
||||
/// </summary>
|
||||
public partial class DeleteBranch : Controls.PopupWidget {
|
||||
private string repo = null;
|
||||
private string branch = null;
|
||||
private string remote = null;
|
||||
|
||||
public DeleteBranch(string repo, string branch, string remote = null) {
|
||||
this.repo = repo;
|
||||
this.branch = branch;
|
||||
this.remote = remote;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
if (string.IsNullOrEmpty(remote)) txtTarget.Text = branch;
|
||||
else txtTarget.Text = $"{remote}/{branch}";
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
return App.Text("DeleteBranch");
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
return Task.Run(() => {
|
||||
Models.Watcher.SetEnabled(repo, false);
|
||||
if (string.IsNullOrEmpty(remote)) {
|
||||
new Commands.Branch(repo, branch).Delete();
|
||||
} else {
|
||||
new Commands.Push(repo, remote, branch).Exec();
|
||||
}
|
||||
Models.Watcher.SetEnabled(repo, true);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
33
src/Views/Popups/DeleteRemote.xaml
Normal file
33
src/Views/Popups/DeleteRemote.xaml
Normal file
|
@ -0,0 +1,33 @@
|
|||
<controls:PopupWidget
|
||||
x:Class="SourceGit.Views.Popups.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"
|
||||
xmlns:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="500" Height="Auto">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.DeleteRemote.Remote}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<StackPanel
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
VerticalAlignment="Center">
|
||||
<Path Width="14" Height="14" Data="{StaticResource Icon.Remote}"/>
|
||||
<TextBlock x:Name="txtTarget" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
33
src/Views/Popups/DeleteRemote.xaml.cs
Normal file
33
src/Views/Popups/DeleteRemote.xaml.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
|
||||
/// <summary>
|
||||
/// 删除远程确认
|
||||
/// </summary>
|
||||
public partial class DeleteRemote : Controls.PopupWidget {
|
||||
private string repo = null;
|
||||
private string remote = null;
|
||||
|
||||
public DeleteRemote(string repo, string remote) {
|
||||
this.repo = repo;
|
||||
this.remote = remote;
|
||||
|
||||
InitializeComponent();
|
||||
txtTarget.Text = remote;
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
return App.Text("DeleteRemote");
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
return Task.Run(() => {
|
||||
Models.Watcher.SetEnabled(repo, false);
|
||||
new Commands.Remote(repo).Delete(remote);
|
||||
Models.Watcher.SetEnabled(repo, true);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
33
src/Views/Popups/DeleteSubmodule.xaml
Normal file
33
src/Views/Popups/DeleteSubmodule.xaml
Normal file
|
@ -0,0 +1,33 @@
|
|||
<controls:PopupWidget
|
||||
x:Class="SourceGit.Views.Popups.DeleteSubmodule"
|
||||
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:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="500" Height="Auto">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.DeleteSubmodule.Path}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<StackPanel
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
VerticalAlignment="Center">
|
||||
<Path Width="14" Height="14" Data="{StaticResource Icon.Submodule}"/>
|
||||
<TextBlock x:Name="txtPath" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
33
src/Views/Popups/DeleteSubmodule.xaml.cs
Normal file
33
src/Views/Popups/DeleteSubmodule.xaml.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
/// <summary>
|
||||
/// 删除子模块面板
|
||||
/// </summary>
|
||||
public partial class DeleteSubmodule : Controls.PopupWidget {
|
||||
private string repo = null;
|
||||
private string submodule = null;
|
||||
|
||||
public DeleteSubmodule(string repo, string submodule) {
|
||||
this.repo = repo;
|
||||
this.submodule = submodule;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
txtPath.Text = submodule;
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
return App.Text("DeleteSubmodule");
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
return Task.Run(() => {
|
||||
Models.Watcher.SetEnabled(repo, false);
|
||||
new Commands.Submodule(repo).Delete(submodule);
|
||||
Models.Watcher.SetEnabled(repo, true);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
41
src/Views/Popups/DeleteTag.xaml
Normal file
41
src/Views/Popups/DeleteTag.xaml
Normal file
|
@ -0,0 +1,41 @@
|
|||
<controls:PopupWidget
|
||||
x:Class="SourceGit.Views.Popups.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"
|
||||
xmlns:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="500" Height="Auto">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.DeleteTag.Tag}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<StackPanel
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
VerticalAlignment="Center">
|
||||
<Path Width="14" Height="14" Data="{StaticResource Icon.Tag}"/>
|
||||
<TextBlock x:Name="txtTag" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
Margin="0,4,0,0"
|
||||
x:Name="chkPush"
|
||||
IsChecked="True"
|
||||
Content="{StaticResource Text.DeleteTag.WithRemote}"/>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
35
src/Views/Popups/DeleteTag.xaml.cs
Normal file
35
src/Views/Popups/DeleteTag.xaml.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
/// <summary>
|
||||
/// 删除标签
|
||||
/// </summary>
|
||||
public partial class DeleteTag : Controls.PopupWidget {
|
||||
private string repo = null;
|
||||
private string tag = null;
|
||||
|
||||
public DeleteTag(string repo, string tag) {
|
||||
this.repo = repo;
|
||||
this.tag = tag;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
txtTag.Text = tag;
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
return App.Text("DeleteTag");
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
var push = chkPush.IsChecked == true;
|
||||
|
||||
return Task.Run(() => {
|
||||
Models.Watcher.SetEnabled(repo, false);
|
||||
new Commands.Tag(repo).Delete(tag, push);
|
||||
Models.Watcher.SetEnabled(repo, true);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
39
src/Views/Popups/Discard.xaml
Normal file
39
src/Views/Popups/Discard.xaml
Normal file
|
@ -0,0 +1,39 @@
|
|||
<controls:PopupWidget
|
||||
x:Class="SourceGit.Views.Popups.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:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="500" Height="Auto">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.Discard.Changes}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<StackPanel
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
Orientation="Horizontal">
|
||||
<Path x:Name="icon" Width="12" Height="12" Data="{StaticResource Icon.File}"/>
|
||||
<TextBlock x:Name="txtTip" Margin="4,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
Margin="0,4,0,0"
|
||||
Text="{StaticResource Text.Discard.Warning}"
|
||||
Foreground="{StaticResource Brush.FG2}"/>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
42
src/Views/Popups/Discard.xaml.cs
Normal file
42
src/Views/Popups/Discard.xaml.cs
Normal file
|
@ -0,0 +1,42 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
/// <summary>
|
||||
/// 忽略变更
|
||||
/// </summary>
|
||||
public partial class Discard : Controls.PopupWidget {
|
||||
private string repo = null;
|
||||
private List<Models.Change> changes = null;
|
||||
|
||||
public Discard(string repo, List<Models.Change> changes) {
|
||||
this.repo = repo;
|
||||
this.changes = changes;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
if (changes == null || changes.Count == 0) {
|
||||
icon.Data = FindResource("Icon.Folder") as Geometry;
|
||||
txtTip.Text = App.Text("Discard.All");
|
||||
} else if (changes.Count == 1) {
|
||||
txtTip.Text = changes[0].Path;
|
||||
} else {
|
||||
txtTip.Text = App.Text("Discard.Total", changes.Count);
|
||||
}
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
return App.Text("Discard");
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
return Task.Run(() => {
|
||||
Models.Watcher.SetEnabled(repo, false);
|
||||
new Commands.Discard(repo, changes).Exec();
|
||||
Models.Watcher.SetEnabled(repo, true);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
57
src/Views/Popups/Fetch.xaml
Normal file
57
src/Views/Popups/Fetch.xaml
Normal file
|
@ -0,0 +1,57 @@
|
|||
<controls:PopupWidget x:Class="SourceGit.Views.Popups.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:converters="clr-namespace:SourceGit.Views.Converters"
|
||||
xmlns:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="500" Height="Auto">
|
||||
<Grid>
|
||||
<Grid.Resources>
|
||||
<converters:InverseBool x:Key="InverseBool"/>
|
||||
</Grid.Resources>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.Fetch.Remote}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<ComboBox
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
x:Name="remotes"
|
||||
VerticalAlignment="Center"
|
||||
IsEnabled="{Binding ElementName=chkFetchAll, Path=IsChecked, Converter={StaticResource InverseBool}}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Height="20">
|
||||
<Path Margin="4,0,0,0" Width="14" Height="14" Data="{StaticResource Icon.Remote}"/>
|
||||
<TextBlock Text="{Binding Name}" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<CheckBox Grid.Row="1" Grid.Column="1"
|
||||
Margin="0,4,0,0"
|
||||
x:Name="chkFetchAll"
|
||||
IsChecked="True"
|
||||
Content="{StaticResource Text.Fetch.AllRemotes}"/>
|
||||
|
||||
<CheckBox Grid.Row="2" Grid.Column="1"
|
||||
x:Name="chkPrune"
|
||||
IsChecked="True"
|
||||
Content="{StaticResource Text.Fetch.Prune}"/>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
41
src/Views/Popups/Fetch.xaml.cs
Normal file
41
src/Views/Popups/Fetch.xaml.cs
Normal file
|
@ -0,0 +1,41 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
|
||||
/// <summary>
|
||||
/// 拉取更新
|
||||
/// </summary>
|
||||
public partial class Fetch : Controls.PopupWidget {
|
||||
private string repo = null;
|
||||
|
||||
public Fetch(Models.Repository repo, string preferRemote) {
|
||||
this.repo = repo.Path;
|
||||
InitializeComponent();
|
||||
remotes.ItemsSource = repo.Remotes;
|
||||
if (preferRemote != null) {
|
||||
remotes.SelectedIndex = repo.Remotes.FindIndex(x => x.Name == preferRemote);
|
||||
chkFetchAll.IsChecked = false;
|
||||
} else {
|
||||
remotes.SelectedIndex = 0;
|
||||
chkFetchAll.IsChecked = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
return App.Text("Fetch.Title");
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
var prune = chkPrune.IsChecked == true;
|
||||
var remote = (remotes.SelectedItem as Models.Remote).Name;
|
||||
if (chkFetchAll.IsChecked == true) remote = "--all";
|
||||
|
||||
return Task.Run(() => {
|
||||
Models.Watcher.SetEnabled(repo, false);
|
||||
var succ = new Commands.Fetch(repo, remote, prune, UpdateProgress).Exec();
|
||||
Models.Watcher.SetEnabled(repo, true);
|
||||
return succ;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
35
src/Views/Popups/GitFlowFinish.xaml
Normal file
35
src/Views/Popups/GitFlowFinish.xaml
Normal file
|
@ -0,0 +1,35 @@
|
|||
<controls:PopupWidget
|
||||
x:Class="SourceGit.Views.Popups.GitFlowFinish"
|
||||
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:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="800" Height="Auto">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
x:Name="txtPrefix"
|
||||
HorizontalAlignment="Right"/>
|
||||
<Path
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
Width="14" Height="14"
|
||||
Data="{StaticResource Icon.Branch}"/>
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="2"
|
||||
Margin="4,0,0,0"
|
||||
x:Name="txtName"/>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
57
src/Views/Popups/GitFlowFinish.xaml.cs
Normal file
57
src/Views/Popups/GitFlowFinish.xaml.cs
Normal file
|
@ -0,0 +1,57 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
/// <summary>
|
||||
/// 完成GitFlow分支开发
|
||||
/// </summary>
|
||||
public partial class GitFlowFinish : Controls.PopupWidget {
|
||||
private string repo = null;
|
||||
private string name = null;
|
||||
private Models.GitFlowBranchType type = Models.GitFlowBranchType.None;
|
||||
|
||||
public GitFlowFinish(Models.Repository repo, string branch, Models.GitFlowBranchType type) {
|
||||
this.repo = repo.Path;
|
||||
this.type = type;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
txtName.Text = branch;
|
||||
switch (type) {
|
||||
case Models.GitFlowBranchType.Feature:
|
||||
txtPrefix.Text = App.Text("GitFlow.Feature");
|
||||
name = branch.Substring(repo.GitFlow.Feature.Length);
|
||||
break;
|
||||
case Models.GitFlowBranchType.Release:
|
||||
txtPrefix.Text = App.Text("GitFlow.Release");
|
||||
name = branch.Substring(repo.GitFlow.Release.Length);
|
||||
break;
|
||||
case Models.GitFlowBranchType.Hotfix:
|
||||
txtPrefix.Text = App.Text("GitFlow.Hotfix");
|
||||
name = branch.Substring(repo.GitFlow.Hotfix.Length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
switch (type) {
|
||||
case Models.GitFlowBranchType.Feature:
|
||||
return App.Text("GitFlow.FinishFeature");
|
||||
case Models.GitFlowBranchType.Release:
|
||||
return App.Text("GitFlow.FinishRelease");
|
||||
case Models.GitFlowBranchType.Hotfix:
|
||||
return App.Text("GitFlow.FinishHotfix");
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
return Task.Run(() => {
|
||||
Models.Watcher.SetEnabled(repo, false);
|
||||
new Commands.GitFlow(repo).Finish(type, name);
|
||||
Models.Watcher.SetEnabled(repo, true);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
41
src/Views/Popups/GitFlowStart.xaml
Normal file
41
src/Views/Popups/GitFlowStart.xaml
Normal file
|
@ -0,0 +1,41 @@
|
|||
<controls:PopupWidget
|
||||
x:Class="SourceGit.Views.Popups.GitFlowStart"
|
||||
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:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
xmlns:validations="clr-namespace:SourceGit.Views.Validations"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="500" Height="Auto">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
x:Name="txtPrefix"
|
||||
HorizontalAlignment="Right"/>
|
||||
<controls:TextEdit
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
x:Name="txtBranchName"
|
||||
Height="24"
|
||||
Placeholder="{StaticResource Text.GitFlow.StartPlaceholder}">
|
||||
<controls:TextEdit.Text>
|
||||
<Binding ElementName="me" Path="BranchName" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay">
|
||||
<Binding.ValidationRules>
|
||||
<validations:BranchName x:Name="ruleBranch"/>
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</controls:TextEdit.Text>
|
||||
</controls:TextEdit>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
62
src/Views/Popups/GitFlowStart.xaml.cs
Normal file
62
src/Views/Popups/GitFlowStart.xaml.cs
Normal file
|
@ -0,0 +1,62 @@
|
|||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
/// <summary>
|
||||
/// Git-Flow start命令操作面板
|
||||
/// </summary>
|
||||
public partial class GitFlowStart : Controls.PopupWidget {
|
||||
private string repo = null;
|
||||
private Models.GitFlowBranchType type = Models.GitFlowBranchType.None;
|
||||
|
||||
public string BranchName { get; set; }
|
||||
|
||||
public GitFlowStart(Models.Repository repo, Models.GitFlowBranchType type) {
|
||||
this.repo = repo.Path;
|
||||
this.type = type;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
ruleBranch.Repo = repo;
|
||||
switch (type) {
|
||||
case Models.GitFlowBranchType.Feature:
|
||||
ruleBranch.Prefix = repo.GitFlow.Feature;
|
||||
txtPrefix.Text = repo.GitFlow.Feature;
|
||||
break;
|
||||
case Models.GitFlowBranchType.Release:
|
||||
ruleBranch.Prefix = repo.GitFlow.Release;
|
||||
txtPrefix.Text = repo.GitFlow.Release;
|
||||
break;
|
||||
case Models.GitFlowBranchType.Hotfix:
|
||||
ruleBranch.Prefix = repo.GitFlow.Hotfix;
|
||||
txtPrefix.Text = repo.GitFlow.Hotfix;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
switch (type) {
|
||||
case Models.GitFlowBranchType.Feature:
|
||||
return App.Text("GitFlow.StartFeatureTitle");
|
||||
case Models.GitFlowBranchType.Release:
|
||||
return App.Text("GitFlow.StartReleaseTitle");
|
||||
case Models.GitFlowBranchType.Hotfix:
|
||||
return App.Text("GitFlow.StartHotfixTitle");
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
txtBranchName.GetBindingExpression(TextBox.TextProperty).UpdateSource();
|
||||
if (Validation.GetHasError(txtBranchName)) return null;
|
||||
|
||||
return Task.Run(() => {
|
||||
Models.Watcher.SetEnabled(repo, false);
|
||||
new Commands.GitFlow(repo).Start(type, BranchName);
|
||||
Models.Watcher.SetEnabled(repo, true);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
43
src/Views/Popups/Init.xaml
Normal file
43
src/Views/Popups/Init.xaml
Normal file
|
@ -0,0 +1,43 @@
|
|||
<controls:PopupWidget x:Class="SourceGit.Views.Popups.Init"
|
||||
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:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="500" Height="Auto">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="100"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Text="{StaticResource Text.Init.Path}"
|
||||
HorizontalAlignment="Right"/>
|
||||
|
||||
<Path
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
Width="12" Height="12"
|
||||
Margin="8,0"
|
||||
Data="{StaticResource Icon.Folder.Fill}"
|
||||
Fill="Goldenrod"/>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="2"
|
||||
Text="{Binding ElementName=me, Path=WorkDir}"/>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2"
|
||||
Text="{StaticResource Text.Init.Tip}"
|
||||
Foreground="{StaticResource Brush.FG2}"/>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
31
src/Views/Popups/Init.xaml.cs
Normal file
31
src/Views/Popups/Init.xaml.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
|
||||
/// <summary>
|
||||
/// 初始化Git仓库确认框
|
||||
/// </summary>
|
||||
public partial class Init : Controls.PopupWidget {
|
||||
public string WorkDir { get; set; }
|
||||
|
||||
public Init(string dir) {
|
||||
WorkDir = dir;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
return App.Text("Init");
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
return Task.Run(() => {
|
||||
var succ = new Commands.Init(WorkDir).Exec();
|
||||
if (!succ) return false;
|
||||
|
||||
var repo = Models.Preference.Instance.AddRepository(WorkDir, WorkDir + "\\.git", "");
|
||||
Models.Watcher.Open(repo);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
102
src/Views/Popups/InitGitFlow.xaml
Normal file
102
src/Views/Popups/InitGitFlow.xaml
Normal file
|
@ -0,0 +1,102 @@
|
|||
<controls:PopupWidget
|
||||
x:Class="SourceGit.Views.Popups.InitGitFlow"
|
||||
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:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="500" Height="Auto">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="8"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.GitFlow.ProductionBranch}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<controls:TextEdit
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
x:Name="txtMaster"
|
||||
Height="24"
|
||||
Placeholder="master"
|
||||
Text="master"/>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.GitFlow.DevelopBranch}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<controls:TextEdit
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
x:Name="txtDevelop"
|
||||
Height="24"
|
||||
Placeholder="develop"
|
||||
Text="develop"/>
|
||||
|
||||
<Rectangle
|
||||
Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2"
|
||||
Height="1"
|
||||
Fill="{StaticResource Brush.Border2}"/>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.GitFlow.FeaturePrefix}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<controls:TextEdit
|
||||
Grid.Row="3" Grid.Column="1"
|
||||
x:Name="txtFeature"
|
||||
Height="24"
|
||||
Placeholder="feature/"
|
||||
Text="feature/"/>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.GitFlow.ReleasePrefix}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<controls:TextEdit
|
||||
Grid.Row="4" Grid.Column="1"
|
||||
x:Name="txtRelease"
|
||||
Height="24"
|
||||
Placeholder="release/"
|
||||
Text="release/"/>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="5" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.GitFlow.HotfixPrefix}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<controls:TextEdit
|
||||
Grid.Row="5" Grid.Column="1"
|
||||
x:Name="txtHotfix"
|
||||
Height="24"
|
||||
Placeholder="hotfix/"
|
||||
Text="hotfix/"/>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="6" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.GitFlow.TagPrefix}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<controls:TextEdit
|
||||
Grid.Row="6" Grid.Column="1"
|
||||
x:Name="txtTag"
|
||||
Height="24"
|
||||
Placeholder="{StaticResource Text.Optional}"/>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
50
src/Views/Popups/InitGitFlow.xaml.cs
Normal file
50
src/Views/Popups/InitGitFlow.xaml.cs
Normal file
|
@ -0,0 +1,50 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
/// <summary>
|
||||
/// 初始化Git-Flow
|
||||
/// </summary>
|
||||
public partial class InitGitFlow : Controls.PopupWidget {
|
||||
private Models.Repository repo = null;
|
||||
|
||||
public InitGitFlow(Models.Repository repo) {
|
||||
this.repo = repo;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
return App.Text("GitFlow.Init");
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
var master = txtMaster.Text;
|
||||
var dev = txtDevelop.Text;
|
||||
var feature = txtFeature.Text;
|
||||
var release = txtRelease.Text;
|
||||
var hotfix = txtHotfix.Text;
|
||||
var version = txtTag.Text;
|
||||
|
||||
return Task.Run(() => {
|
||||
Models.Watcher.SetEnabled(repo.Path, false);
|
||||
var succ = new Commands.GitFlow(repo.Path).Init(master, dev, feature, release, hotfix, version);
|
||||
var cmd = new Commands.Config(repo.Path);
|
||||
if (succ) {
|
||||
repo.GitFlow.Feature = cmd.Get("gitflow.prefix.feature");
|
||||
repo.GitFlow.Release = cmd.Get("gitflow.prefix.release");
|
||||
repo.GitFlow.Hotfix = cmd.Get("gitflow.prefix.hotfix");
|
||||
} else {
|
||||
cmd.Set("gitflow.branch.master", null);
|
||||
cmd.Set("gitflow.branch.develop", null);
|
||||
cmd.Set("gitflow.prefix.feature", null);
|
||||
cmd.Set("gitflow.prefix.bugfix", null);
|
||||
cmd.Set("gitflow.prefix.release", null);
|
||||
cmd.Set("gitflow.prefix.hotfix", null);
|
||||
cmd.Set("gitflow.prefix.support", null);
|
||||
cmd.Set("gitflow.prefix.versiontag", null);
|
||||
}
|
||||
Models.Watcher.SetEnabled(repo.Path, true);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
71
src/Views/Popups/Merge.xaml
Normal file
71
src/Views/Popups/Merge.xaml
Normal file
|
@ -0,0 +1,71 @@
|
|||
<controls:PopupWidget
|
||||
x:Class="SourceGit.Views.Popups.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:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
xmlns:models="clr-namespace:SourceGit.Models"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="500" Height="Auto">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.Merge.Source}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<StackPanel
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
VerticalAlignment="Center">
|
||||
<Path Width="14" Height="14" Data="{StaticResource Icon.Branch}"/>
|
||||
<TextBlock x:Name="txtSource" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.Merge.Into}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<StackPanel
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
VerticalAlignment="Center">
|
||||
<Path Width="14" Height="14" Data="{StaticResource Icon.Branch}"/>
|
||||
<TextBlock x:Name="txtInto" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.Merge.Mode}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<ComboBox
|
||||
Grid.Row="2" Grid.Column="1"
|
||||
x:Name="cmbMode"
|
||||
ItemsSource="{Binding Source={x:Static models:MergeOption.Supported}}"
|
||||
SelectedIndex="0"
|
||||
Height="24"
|
||||
VerticalAlignment="Center">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Height="20">
|
||||
<TextBlock Text="{Binding Name}" Margin="4,0"/>
|
||||
<TextBlock Text="{Binding Desc}" Margin="4,0" FontSize="11" Foreground="{StaticResource Brush.FG2}"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
35
src/Views/Popups/Merge.xaml.cs
Normal file
35
src/Views/Popups/Merge.xaml.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
/// <summary>
|
||||
/// 合并操作界面
|
||||
/// </summary>
|
||||
public partial class Merge : Controls.PopupWidget {
|
||||
private string repo = null;
|
||||
private string source = null;
|
||||
|
||||
public Merge(string repo, string source, string dest) {
|
||||
this.repo = repo;
|
||||
this.source = source;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
txtSource.Text = source;
|
||||
txtInto.Text = dest;
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
return App.Text("Merge");
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
var mode = (cmbMode.SelectedItem as Models.MergeOption).Arg;
|
||||
return Task.Run(() => {
|
||||
Models.Watcher.SetEnabled(repo, false);
|
||||
new Commands.Merge(repo, source, mode).Exec();
|
||||
Models.Watcher.SetEnabled(repo, true);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
93
src/Views/Popups/Pull.xaml
Normal file
93
src/Views/Popups/Pull.xaml
Normal file
|
@ -0,0 +1,93 @@
|
|||
<controls:PopupWidget
|
||||
x:Class="SourceGit.Views.Popups.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"
|
||||
xmlns:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
xmlns:converters="clr-namespace:SourceGit.Views.Converters"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="500" Height="Auto">
|
||||
<UserControl.Resources>
|
||||
<converters:BranchToName x:Key="BranchToName"/>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.Pull.Remote}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<ComboBox
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
x:Name="cmbRemotes"
|
||||
Height="24"
|
||||
VerticalAlignment="Center"
|
||||
SelectionChanged="OnRemoteSelectionChanged">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Height="20">
|
||||
<Path Margin="4,0,0,0" Width="14" Height="14" Data="{StaticResource Icon.Remote}"/>
|
||||
<TextBlock Text="{Binding Name}" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.Pull.Branch}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<ComboBox
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
x:Name="cmbBranches"
|
||||
Height="24"
|
||||
VerticalAlignment="Center">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Height="20">
|
||||
<Path Margin="4,0,0,0" Width="14" Height="14" Data="{StaticResource Icon.Branch}"/>
|
||||
<TextBlock Text="{Binding ., Converter={StaticResource BranchToName}}" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.Pull.Into}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<StackPanel
|
||||
Grid.Row="2" Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
VerticalAlignment="Center">
|
||||
<Path Width="14" Height="14" Data="{StaticResource Icon.Branch}"/>
|
||||
<TextBlock x:Name="txtInto" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox Grid.Row="3" Grid.Column="1"
|
||||
Margin="0,4,0,0"
|
||||
x:Name="chkUseRebase"
|
||||
IsChecked="True"
|
||||
Content="{StaticResource Text.Pull.UseRebase}"/>
|
||||
|
||||
<CheckBox Grid.Row="4" Grid.Column="1"
|
||||
x:Name="chkAutoStash"
|
||||
IsChecked="True"
|
||||
Content="{StaticResource Text.Pull.AutoStash}"/>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
68
src/Views/Popups/Pull.xaml.cs
Normal file
68
src/Views/Popups/Pull.xaml.cs
Normal file
|
@ -0,0 +1,68 @@
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
|
||||
/// <summary>
|
||||
/// 拉回
|
||||
/// </summary>
|
||||
public partial class Pull : Controls.PopupWidget {
|
||||
private Models.Repository repo = null;
|
||||
private Models.Branch prefered = null;
|
||||
|
||||
public Pull(Models.Repository repo, Models.Branch preferRemoteBranch) {
|
||||
this.repo = repo;
|
||||
this.prefered = preferRemoteBranch;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
var current = repo.Branches.Find(x => x.IsCurrent);
|
||||
txtInto.Text = current.Name;
|
||||
|
||||
if (prefered == null && !string.IsNullOrEmpty(current.Upstream)) {
|
||||
prefered = repo.Branches.Find(x => x.FullName == current.Upstream);
|
||||
}
|
||||
|
||||
cmbRemotes.ItemsSource = repo.Remotes;
|
||||
if (prefered != null) {
|
||||
cmbRemotes.SelectedItem = repo.Remotes.Find(x => x.Name == prefered.Remote);
|
||||
} else {
|
||||
cmbRemotes.SelectedItem = repo.Remotes[0];
|
||||
}
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
return App.Text("Pull.Title");
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
var branch = cmbBranches.SelectedItem as Models.Branch;
|
||||
if (branch == null) return null;
|
||||
|
||||
var rebase = chkUseRebase.IsChecked == true;
|
||||
var autoStash = chkAutoStash.IsChecked == true;
|
||||
|
||||
return Task.Run(() => {
|
||||
Models.Watcher.SetEnabled(repo.Path, false);
|
||||
var succ = new Commands.Pull(repo.Path, branch.Remote, branch.Name, rebase, autoStash, UpdateProgress).Exec();
|
||||
Models.Watcher.SetEnabled(repo.Path, true);
|
||||
return succ;
|
||||
});
|
||||
}
|
||||
|
||||
private void OnRemoteSelectionChanged(object sender, SelectionChangedEventArgs e) {
|
||||
var remote = cmbRemotes.SelectedItem as Models.Remote;
|
||||
if (remote == null) return;
|
||||
|
||||
var branches = repo.Branches.Where(x => x.Remote == remote.Name).ToList();
|
||||
cmbBranches.ItemsSource = branches;
|
||||
|
||||
if (prefered != null && remote.Name == prefered.Remote) {
|
||||
cmbBranches.SelectedItem = branches.Find(x => x.FullName == prefered.FullName);
|
||||
} else {
|
||||
cmbBranches.SelectedItem = branches[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
100
src/Views/Popups/Push.xaml
Normal file
100
src/Views/Popups/Push.xaml
Normal file
|
@ -0,0 +1,100 @@
|
|||
<controls:PopupWidget
|
||||
x:Class="SourceGit.Views.Popups.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:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
xmlns:converters="clr-namespace:SourceGit.Views.Converters"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="500" Height="Auto">
|
||||
<UserControl.Resources>
|
||||
<converters:BranchToName x:Key="BranchToName"/>
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.Push.Local}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<ComboBox
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
x:Name="cmbLocalBranches"
|
||||
Height="24"
|
||||
VerticalAlignment="Center"
|
||||
SelectionChanged="OnLocalSelectionChanged">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Height="20">
|
||||
<Path Margin="4,0,0,0" Width="14" Height="14" Data="{StaticResource Icon.Branch}"/>
|
||||
<TextBlock Text="{Binding Name}" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.Push.Remote}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<ComboBox
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
x:Name="cmbRemotes"
|
||||
Height="24"
|
||||
VerticalAlignment="Center"
|
||||
SelectionChanged="OnRemoteSelectionChanged">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Height="20">
|
||||
<Path Margin="4,0,0,0" Width="14" Height="14" Data="{StaticResource Icon.Remote}"/>
|
||||
<TextBlock Text="{Binding Name}" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.Push.To}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<ComboBox
|
||||
Grid.Row="2" Grid.Column="1"
|
||||
x:Name="cmbRemoteBranches"
|
||||
Height="24"
|
||||
VerticalAlignment="Center">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Height="20">
|
||||
<Path Margin="4,0,0,0" Width="14" Height="14" Data="{StaticResource Icon.Branch}"/>
|
||||
<TextBlock Text="{Binding ., Converter={StaticResource BranchToName}}" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<CheckBox Grid.Row="3" Grid.Column="1"
|
||||
Margin="0,4,0,0"
|
||||
x:Name="chkAllTags"
|
||||
IsChecked="True"
|
||||
Content="{StaticResource Text.Push.WithAllTags}"/>
|
||||
|
||||
<CheckBox Grid.Row="4" Grid.Column="1"
|
||||
x:Name="chkForce"
|
||||
IsChecked="True"
|
||||
Content="{StaticResource Text.Push.Force}"/>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
108
src/Views/Popups/Push.xaml.cs
Normal file
108
src/Views/Popups/Push.xaml.cs
Normal file
|
@ -0,0 +1,108 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
/// <summary>
|
||||
/// 推送
|
||||
/// </summary>
|
||||
public partial class Push : Controls.PopupWidget {
|
||||
private Models.Repository repo = null;
|
||||
|
||||
public Push(Models.Repository repo, Models.Branch localBranch) {
|
||||
this.repo = repo;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
var localBranches = repo.Branches.Where(x => x.IsLocal).ToList();
|
||||
cmbLocalBranches.ItemsSource = localBranches;
|
||||
if (localBranch != null) cmbLocalBranches.SelectedItem = localBranch;
|
||||
else cmbLocalBranches.SelectedItem = localBranches.Find(x => x.IsCurrent);
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
return App.Text("Push.Title");
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
var localBranch = cmbLocalBranches.SelectedItem as Models.Branch;
|
||||
if (localBranch == null) return null;
|
||||
|
||||
var remoteBranch = cmbRemoteBranches.SelectedItem as Models.Branch;
|
||||
if (remoteBranch == null) return null;
|
||||
|
||||
var withTags = chkAllTags.IsChecked == true;
|
||||
var force = chkForce.IsChecked == true;
|
||||
var track = string.IsNullOrEmpty(localBranch.Upstream);
|
||||
|
||||
return Task.Run(() => {
|
||||
Models.Watcher.SetEnabled(repo.Path, false);
|
||||
var succ = new Commands.Push(
|
||||
repo.Path,
|
||||
localBranch.Name,
|
||||
remoteBranch.Remote,
|
||||
remoteBranch.Name.Replace(" (new)", ""),
|
||||
withTags,
|
||||
force,
|
||||
track,
|
||||
UpdateProgress).Exec();
|
||||
Models.Watcher.SetEnabled(repo.Path, true);
|
||||
return succ;
|
||||
});
|
||||
}
|
||||
|
||||
private void OnLocalSelectionChanged(object sender, SelectionChangedEventArgs e) {
|
||||
var local = cmbLocalBranches.SelectedItem as Models.Branch;
|
||||
if (local == null) return;
|
||||
|
||||
cmbRemotes.ItemsSource = null;
|
||||
cmbRemotes.ItemsSource = repo.Remotes;
|
||||
|
||||
if (!string.IsNullOrEmpty(local.Upstream)) {
|
||||
cmbRemotes.SelectedItem = repo.Remotes.Find(x => local.Upstream.StartsWith($"refs/remotes/{x.Name}/"));
|
||||
} else {
|
||||
cmbRemotes.SelectedItem = repo.Remotes[0];
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRemoteSelectionChanged(object sender, SelectionChangedEventArgs e) {
|
||||
var local = cmbLocalBranches.SelectedItem as Models.Branch;
|
||||
if (local == null) return;
|
||||
|
||||
var remote = cmbRemotes.SelectedItem as Models.Remote;
|
||||
if (remote == null) return;
|
||||
|
||||
var remoteBranches = new List<Models.Branch>();
|
||||
remoteBranches.AddRange(repo.Branches.Where(x => x.Remote == remote.Name));
|
||||
cmbRemoteBranches.ItemsSource = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(local.Upstream)) {
|
||||
foreach (var b in remoteBranches) {
|
||||
if (b.FullName == local.Upstream) {
|
||||
cmbRemoteBranches.ItemsSource = remoteBranches;
|
||||
cmbRemoteBranches.SelectedItem = b;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var match = $"refs/remotes/{remote.Name}/{local.Name}";
|
||||
foreach (var b in remoteBranches) {
|
||||
if (b.FullName == match) {
|
||||
cmbRemoteBranches.ItemsSource = remoteBranches;
|
||||
cmbRemoteBranches.SelectedItem = b;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var prefer = new Models.Branch() {
|
||||
Remote = remote.Name,
|
||||
Name = $"{local.Name} (new)"
|
||||
};
|
||||
remoteBranches.Add(prefer);
|
||||
cmbRemoteBranches.ItemsSource = remoteBranches;
|
||||
cmbRemoteBranches.SelectedItem = prefer;
|
||||
}
|
||||
}
|
||||
}
|
53
src/Views/Popups/PushTag.xaml
Normal file
53
src/Views/Popups/PushTag.xaml
Normal file
|
@ -0,0 +1,53 @@
|
|||
<controls:PopupWidget
|
||||
x:Class="SourceGit.Views.Popups.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"
|
||||
xmlns:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="500" Height="Auto">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.PushTag.Tag}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<StackPanel
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
VerticalAlignment="Center">
|
||||
<Path Width="14" Height="14" Data="{StaticResource Icon.Tag}"/>
|
||||
<TextBlock x:Name="txtTag" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.PushTag.Remote}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<ComboBox
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
x:Name="cmbRemotes"
|
||||
VerticalAlignment="Center">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Height="20">
|
||||
<Path Margin="4,0,0,0" Width="14" Height="14" Data="{StaticResource Icon.Remote}"/>
|
||||
<TextBlock Text="{Binding Name}" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
38
src/Views/Popups/PushTag.xaml.cs
Normal file
38
src/Views/Popups/PushTag.xaml.cs
Normal file
|
@ -0,0 +1,38 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
/// <summary>
|
||||
/// 推送标签确认面板
|
||||
/// </summary>
|
||||
public partial class PushTag : Controls.PopupWidget {
|
||||
private string repo = null;
|
||||
private string tag = null;
|
||||
|
||||
public PushTag(Models.Repository repo, string tag) {
|
||||
this.repo = repo.Path;
|
||||
this.tag = tag;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
txtTag.Text = tag;
|
||||
cmbRemotes.ItemsSource = repo.Remotes;
|
||||
cmbRemotes.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
return App.Text("PushTag");
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
var remote = cmbRemotes.SelectedItem as Models.Remote;
|
||||
if (remote == null) return null;
|
||||
|
||||
return Task.Run(() => {
|
||||
Models.Watcher.SetEnabled(repo, false);
|
||||
new Commands.Push(repo, remote.Name, tag, false).Exec();
|
||||
Models.Watcher.SetEnabled(repo, true);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
54
src/Views/Popups/Rebase.xaml
Normal file
54
src/Views/Popups/Rebase.xaml
Normal file
|
@ -0,0 +1,54 @@
|
|||
<controls:PopupWidget
|
||||
x:Class="SourceGit.Views.Popups.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"
|
||||
xmlns:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="500" Height="Auto">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.Rebase.Target}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<StackPanel
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
VerticalAlignment="Center">
|
||||
<Path Width="14" Height="14" Data="{StaticResource Icon.Branch}"/>
|
||||
<TextBlock x:Name="txtCurrent" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.Rebase.On}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<StackPanel
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
VerticalAlignment="Center">
|
||||
<Path x:Name="iconBased" Width="14" Height="14" Data="{StaticResource Icon.Branch}"/>
|
||||
<TextBlock x:Name="txtOn" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox
|
||||
Grid.Row="2" Grid.Column="1"
|
||||
x:Name="chkAutoStash"
|
||||
IsChecked="True"
|
||||
Content="{StaticResource Text.Rebase.AutoStash}"/>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
48
src/Views/Popups/Rebase.xaml.cs
Normal file
48
src/Views/Popups/Rebase.xaml.cs
Normal file
|
@ -0,0 +1,48 @@
|
|||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
/// <summary>
|
||||
/// 变基操作面板
|
||||
/// </summary>
|
||||
public partial class Rebase : Controls.PopupWidget {
|
||||
private string repo = null;
|
||||
private string on = null;
|
||||
|
||||
public Rebase(string repo, string current, Models.Branch branch) {
|
||||
this.repo = repo;
|
||||
this.on = branch.Head;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
txtCurrent.Text = current;
|
||||
txtOn.Text = !string.IsNullOrEmpty(branch.Remote) ? $"{branch.Remote}/{branch.Name}" : branch.Name;
|
||||
iconBased.Data = FindResource("Icon.Branch") as Geometry;
|
||||
}
|
||||
|
||||
public Rebase(string repo, string current, Models.Commit commit) {
|
||||
this.repo = repo;
|
||||
this.on = commit.SHA;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
txtCurrent.Text = current;
|
||||
txtOn.Text = $"{commit.ShortSHA} {commit.Subject}";
|
||||
iconBased.Data = FindResource("Icon.Branch") as Geometry;
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
return App.Text("Rebase");
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
var autoStash = chkAutoStash.IsChecked == true;
|
||||
return Task.Run(() => {
|
||||
Models.Watcher.SetEnabled(repo, false);
|
||||
new Commands.Rebase(repo, on, autoStash).Exec();
|
||||
Models.Watcher.SetEnabled(repo, true);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
61
src/Views/Popups/Remote.xaml
Normal file
61
src/Views/Popups/Remote.xaml
Normal file
|
@ -0,0 +1,61 @@
|
|||
<controls:PopupWidget
|
||||
x:Class="SourceGit.Views.Popups.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:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
xmlns:validations="clr-namespace:SourceGit.Views.Validations"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="500" Height="Auto">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.Remote.Name}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<controls:TextEdit
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
x:Name="txtName"
|
||||
Height="24"
|
||||
Placeholder="{StaticResource Text.Remote.Name.Placeholder}">
|
||||
<controls:TextEdit.Text>
|
||||
<Binding ElementName="me" Path="RemoteName" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay">
|
||||
<Binding.ValidationRules>
|
||||
<validations:RemoteName x:Name="ruleName"/>
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</controls:TextEdit.Text>
|
||||
</controls:TextEdit>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.Remote.URL}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<controls:TextEdit
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
x:Name="txtUrl"
|
||||
Height="24"
|
||||
Placeholder="{StaticResource Text.Remote.URL.Placeholder}">
|
||||
<controls:TextEdit.Text>
|
||||
<Binding ElementName="me" Path="RemoteURL" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay">
|
||||
<Binding.ValidationRules>
|
||||
<validations:GitURL x:Name="ruleURL"/>
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</controls:TextEdit.Text>
|
||||
</controls:TextEdit>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
59
src/Views/Popups/Remote.xaml.cs
Normal file
59
src/Views/Popups/Remote.xaml.cs
Normal file
|
@ -0,0 +1,59 @@
|
|||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
/// <summary>
|
||||
/// 远程信息编辑面板
|
||||
/// </summary>
|
||||
public partial class Remote : Controls.PopupWidget {
|
||||
private Models.Repository repo = null;
|
||||
private Models.Remote remote = null;
|
||||
|
||||
public string RemoteName { get; set; }
|
||||
public string RemoteURL { get; set; }
|
||||
|
||||
public Remote(Models.Repository repo, Models.Remote remote) {
|
||||
this.repo = repo;
|
||||
this.remote = remote;
|
||||
|
||||
if (remote != null) {
|
||||
RemoteName = remote.Name;
|
||||
RemoteURL = remote.URL;
|
||||
}
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
ruleName.Repo = repo;
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
return App.Text(remote == null ? "Remote.AddTitle" : "Remote.EditTitle");
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
txtName.GetBindingExpression(TextBox.TextProperty).UpdateSource();
|
||||
if (Validation.GetHasError(txtName)) return null;
|
||||
|
||||
txtUrl.GetBindingExpression(TextBox.TextProperty).UpdateSource();
|
||||
if (Validation.GetHasError(txtUrl)) return null;
|
||||
|
||||
return Task.Run(() => {
|
||||
Models.Watcher.SetEnabled(repo.Path, false);
|
||||
if (remote == null) {
|
||||
var succ = new Commands.Remote(repo.Path).Add(RemoteName, RemoteURL);
|
||||
if (succ) new Commands.Fetch(repo.Path, RemoteName, true, UpdateProgress).Exec();
|
||||
} else {
|
||||
if (remote.URL != RemoteURL) {
|
||||
new Commands.Remote(repo.Path).SetURL(remote.Name, RemoteURL);
|
||||
}
|
||||
|
||||
if (remote.Name != RemoteName) {
|
||||
new Commands.Remote(repo.Path).Rename(remote.Name, RemoteName);
|
||||
}
|
||||
}
|
||||
Models.Watcher.SetEnabled(repo.Path, true);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
55
src/Views/Popups/RenameBranch.xaml
Normal file
55
src/Views/Popups/RenameBranch.xaml
Normal file
|
@ -0,0 +1,55 @@
|
|||
<controls:PopupWidget
|
||||
x:Class="SourceGit.Views.Popups.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:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
xmlns:validations="clr-namespace:SourceGit.Views.Validations"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="500" Height="Auto">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.RenameBranch.Target}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<StackPanel
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
VerticalAlignment="Center">
|
||||
<Path Width="14" Height="14" Data="{StaticResource Icon.Branch}"/>
|
||||
<TextBlock x:Name="txtTarget" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.RenameBranch.Name}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<controls:TextEdit
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
x:Name="txtNewName"
|
||||
Height="24"
|
||||
Placeholder="{StaticResource Text.RenameBranch.Name.Placeholder}">
|
||||
<controls:TextEdit.Text>
|
||||
<Binding ElementName="me" Path="NewName" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay">
|
||||
<Binding.ValidationRules>
|
||||
<validations:BranchName x:Name="ruleBranch"/>
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</controls:TextEdit.Text>
|
||||
</controls:TextEdit>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
40
src/Views/Popups/RenameBranch.xaml.cs
Normal file
40
src/Views/Popups/RenameBranch.xaml.cs
Normal file
|
@ -0,0 +1,40 @@
|
|||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
/// <summary>
|
||||
/// 本地分支改名
|
||||
/// </summary>
|
||||
public partial class RenameBranch : Controls.PopupWidget {
|
||||
private string repo = null;
|
||||
private string target = null;
|
||||
|
||||
public string NewName { get; set; }
|
||||
|
||||
public RenameBranch(Models.Repository repo, string target) {
|
||||
this.repo = repo.Path;
|
||||
this.target = target;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
ruleBranch.Repo = repo;
|
||||
txtTarget.Text = target;
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
return App.Text("RenameBranch");
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
txtNewName.GetBindingExpression(TextBox.TextProperty).UpdateSource();
|
||||
if (Validation.GetHasError(txtNewName)) return null;
|
||||
|
||||
return Task.Run(() => {
|
||||
Models.Watcher.SetEnabled(repo, false);
|
||||
new Commands.Branch(repo, target).Rename(NewName);
|
||||
Models.Watcher.SetEnabled(repo, true);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
72
src/Views/Popups/Reset.xaml
Normal file
72
src/Views/Popups/Reset.xaml
Normal file
|
@ -0,0 +1,72 @@
|
|||
<controls:PopupWidget
|
||||
x:Class="SourceGit.Views.Popups.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"
|
||||
xmlns:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
xmlns:models="clr-namespace:SourceGit.Models"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="500" Height="Auto">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.Reset.Target}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<StackPanel
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
VerticalAlignment="Center">
|
||||
<Path Width="14" Height="14" Data="{StaticResource Icon.Branch}"/>
|
||||
<TextBlock x:Name="txtCurrent" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.Reset.MoveTo}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<StackPanel
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
VerticalAlignment="Center">
|
||||
<Path Width="14" Height="14" Data="{StaticResource Icon.Commit}"/>
|
||||
<TextBlock x:Name="txtMoveTo" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.Reset.Mode}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<ComboBox
|
||||
Grid.Row="2" Grid.Column="1"
|
||||
x:Name="cmbMode"
|
||||
ItemsSource="{Binding Source={x:Static models:ResetMode.Supported}}"
|
||||
SelectedIndex="0"
|
||||
Height="24"
|
||||
VerticalAlignment="Center">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Height="20">
|
||||
<Path Width="12" Height="12" Margin="4,0,0,0" Fill="{Binding Color}" Data="M 0,0 A 180,180 180 1 1 1,1 Z"/>
|
||||
<TextBlock Text="{Binding Name}" Margin="4,0"/>
|
||||
<TextBlock Text="{Binding Desc}" Margin="4,0" FontSize="11" Foreground="{StaticResource Brush.FG2}"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
35
src/Views/Popups/Reset.xaml.cs
Normal file
35
src/Views/Popups/Reset.xaml.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
/// <summary>
|
||||
/// 重置面板
|
||||
/// </summary>
|
||||
public partial class Reset : Controls.PopupWidget {
|
||||
private string repo = null;
|
||||
private string revision = null;
|
||||
|
||||
public Reset(string repo, string current, Models.Commit to) {
|
||||
this.repo = repo;
|
||||
this.revision = to.SHA;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
txtCurrent.Text = current;
|
||||
txtMoveTo.Text = $"{to.ShortSHA} {to.Subject}";
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
return App.Text("Reset");
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
var mode = cmbMode.SelectedItem as Models.ResetMode;
|
||||
return Task.Run(() => {
|
||||
Models.Watcher.SetEnabled(repo, false);
|
||||
new Commands.Reset(repo, revision, mode.Arg).Exec();
|
||||
Models.Watcher.SetEnabled(repo, true);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
41
src/Views/Popups/Revert.xaml
Normal file
41
src/Views/Popups/Revert.xaml
Normal file
|
@ -0,0 +1,41 @@
|
|||
<controls:PopupWidget
|
||||
x:Class="SourceGit.Views.Popups.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"
|
||||
xmlns:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="500" Height="Auto">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.Revert.Commit}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<StackPanel
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
VerticalAlignment="Center">
|
||||
<Path Width="14" Height="14" Data="{StaticResource Icon.Commit}"/>
|
||||
<TextBlock x:Name="txtCommit" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
Margin="0,4,0,0"
|
||||
x:Name="chkCommit"
|
||||
IsChecked="True"
|
||||
Content="{StaticResource Text.Revert.CommitChanges}"/>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
35
src/Views/Popups/Revert.xaml.cs
Normal file
35
src/Views/Popups/Revert.xaml.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
/// <summary>
|
||||
/// 撤销面板
|
||||
/// </summary>
|
||||
public partial class Revert : Controls.PopupWidget {
|
||||
private string repo = null;
|
||||
private string commit = null;
|
||||
|
||||
public Revert(string repo, Models.Commit commit) {
|
||||
this.repo = repo;
|
||||
this.commit = commit.SHA;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
txtCommit.Text = $"{commit.ShortSHA} {commit.Subject}";
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
return App.Text("Revert");
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
var commitChanges = chkCommit.IsChecked == true;
|
||||
|
||||
return Task.Run(() => {
|
||||
Models.Watcher.SetEnabled(repo, false);
|
||||
new Commands.Revert(repo, commit, commitChanges).Exec();
|
||||
Models.Watcher.SetEnabled(repo, true);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
38
src/Views/Popups/Stash.xaml
Normal file
38
src/Views/Popups/Stash.xaml
Normal file
|
@ -0,0 +1,38 @@
|
|||
<controls:PopupWidget
|
||||
x:Class="SourceGit.Views.Popups.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:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="500" Height="Auto">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
Text="{StaticResource Text.Stash.Message}"
|
||||
HorizontalAlignment="Right"/>
|
||||
<controls:TextEdit
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
x:Name="txtMessage"
|
||||
Height="24"
|
||||
Placeholder="{StaticResource Text.Stash.Message.Placeholder}"/>
|
||||
<CheckBox
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
Margin="0,4,0,0"
|
||||
x:Name="chkIncludeUntracked"
|
||||
IsChecked="True"
|
||||
Content="{StaticResource Text.Stash.IncludeUntracked}"/>
|
||||
</Grid>
|
||||
</controls:PopupWidget>
|
46
src/Views/Popups/Stash.xaml.cs
Normal file
46
src/Views/Popups/Stash.xaml.cs
Normal file
|
@ -0,0 +1,46 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.Views.Popups {
|
||||
|
||||
/// <summary>
|
||||
/// 贮藏
|
||||
/// </summary>
|
||||
public partial class Stash : Controls.PopupWidget {
|
||||
private string repo = null;
|
||||
private List<string> files = null;
|
||||
|
||||
public Stash(string repo, List<string> files) {
|
||||
this.repo = repo;
|
||||
this.files = files;
|
||||
|
||||
InitializeComponent();
|
||||
chkIncludeUntracked.IsEnabled = files == null || files.Count == 0;
|
||||
}
|
||||
|
||||
public override string GetTitle() {
|
||||
return App.Text("Stash.Title");
|
||||
}
|
||||
|
||||
public override Task<bool> Start() {
|
||||
var includeUntracked = chkIncludeUntracked.IsChecked == true;
|
||||
var message = txtMessage.Text;
|
||||
|
||||
return Task.Run(() => {
|
||||
Models.Watcher.SetEnabled(repo, false);
|
||||
if (files == null || files.Count == 0) {
|
||||
new Commands.Stash(repo).Push(null, message, includeUntracked);
|
||||
} else {
|
||||
for (int i = 0; i < files.Count; i += 10) {
|
||||
var count = Math.Min(10, files.Count - i);
|
||||
var step = files.GetRange(i, count);
|
||||
new Commands.Stash(repo).Push(step, message, includeUntracked);
|
||||
}
|
||||
}
|
||||
Models.Watcher.SetEnabled(repo, true);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
307
src/Views/Preference.xaml
Normal file
307
src/Views/Preference.xaml
Normal file
|
@ -0,0 +1,307 @@
|
|||
<Window x:Class="SourceGit.Views.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:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
xmlns:models="clr-namespace:SourceGit.Models"
|
||||
mc:Ignorable="d"
|
||||
Title="{StaticResource Text.Preference}"
|
||||
Width="500" SizeToContent="Height"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
ResizeMode="NoResize">
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome UseAeroCaptionButtons="False" CornerRadius="0" CaptionHeight="28" ResizeBorderThickness="1"/>
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
<controls:WindowBorder>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Title bar -->
|
||||
<Grid Grid.Row="0" Background="{StaticResource Brush.TitleBar}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Icon -->
|
||||
<Path Grid.Column="0" Width="14" Height="14" Margin="6,0" Data="{StaticResource Icon.Preference}"/>
|
||||
|
||||
<!-- Title -->
|
||||
<TextBlock Grid.Column="1" Text="{StaticResource Text.Preference}"/>
|
||||
|
||||
<!-- Close -->
|
||||
<controls:IconButton
|
||||
Grid.Column="3"
|
||||
Click="Quit"
|
||||
Padding="9"
|
||||
Icon="{StaticResource Icon.Close}"
|
||||
HoverBackground="Red"
|
||||
WindowChrome.IsHitTestVisibleInChrome="True"/>
|
||||
</Grid>
|
||||
|
||||
<!-- Body -->
|
||||
<Grid Grid.Row="1" Margin="16,8">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="36"/>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="8"/>
|
||||
<RowDefinition Height="36"/>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="8"/>
|
||||
<RowDefinition Height="36"/>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="6"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="128"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- General Group -->
|
||||
<StackPanel Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Text="{StaticResource Text.Preference.General}"
|
||||
FontSize="16" FontWeight="DemiBold"
|
||||
Foreground="{StaticResource Brush.FG2}"/>
|
||||
<TextBlock
|
||||
Text="{StaticResource Text.Preference.RestartRequired}"
|
||||
Foreground="{StaticResource Brush.FG2}"
|
||||
Margin="16,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Language -->
|
||||
<TextBlock
|
||||
Grid.Row="1" Grid.Column="0"
|
||||
Text="{StaticResource Text.Preference.Locale}"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0,0,6,0"/>
|
||||
<ComboBox
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
Height="24"
|
||||
ItemsSource="{Binding Source={x:Static models:Locale.Supported}}"
|
||||
DisplayMemberPath="Name"
|
||||
SelectedValuePath="Resource"
|
||||
SelectedValue="{Binding Source={x:Static models:Preference.Instance}, Path=General.Locale, Mode=TwoWay}"/>
|
||||
|
||||
<!-- Avatar -->
|
||||
<TextBlock
|
||||
Grid.Row="2" Grid.Column="0"
|
||||
Text="{StaticResource Text.Preference.AvatarServer}"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0,0,6,0"/>
|
||||
<ComboBox
|
||||
Grid.Row="2" Grid.Column="1"
|
||||
Height="24"
|
||||
ItemsSource="{Binding Source={x:Static models:AvatarServer.Supported}}"
|
||||
DisplayMemberPath="Name"
|
||||
SelectedValuePath="Url"
|
||||
SelectedValue="{Binding Source={x:Static models:Preference.Instance}, Path=General.AvatarServer, Mode=TwoWay}"/>
|
||||
|
||||
<!-- Use Dark Theme -->
|
||||
<CheckBox
|
||||
Grid.Row="3" Grid.Column="1"
|
||||
Content="{StaticResource Text.Preference.UseDark}"
|
||||
IsChecked="{Binding Source={x:Static models:Preference.Instance}, Path=General.UseDarkTheme, Mode=TwoWay}"/>
|
||||
|
||||
<!-- Enable Check For Update -->
|
||||
<CheckBox
|
||||
Grid.Row="4" Grid.Column="1"
|
||||
Content="{StaticResource Text.Preference.CheckUpdate}"
|
||||
IsChecked="{Binding Source={x:Static models:Preference.Instance}, Path=General.CheckForUpdate, Mode=TwoWay}"/>
|
||||
|
||||
<!-- Auto Fetch -->
|
||||
<CheckBox
|
||||
Grid.Row="5" Grid.Column="1"
|
||||
Content="{StaticResource Text.Preference.AutoFetch}"
|
||||
IsChecked="{Binding Source={x:Static models:Preference.Instance}, Path=General.AutoFetchRemotes, Mode=TwoWay}"/>
|
||||
|
||||
<!-- Git Group -->
|
||||
<TextBlock
|
||||
Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="2"
|
||||
Text="{StaticResource Text.Preference.Git}"
|
||||
FontSize="16" FontWeight="DemiBold"
|
||||
Foreground="{StaticResource Brush.FG2}"/>
|
||||
|
||||
<!-- Git Executable Path -->
|
||||
<TextBlock
|
||||
Grid.Row="8" Grid.Column="0"
|
||||
Text="{StaticResource Text.Preference.Git.Path}"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0,0,6,0"/>
|
||||
<Grid Grid.Row="8" Grid.Column="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<controls:TextEdit
|
||||
Grid.Column="0"
|
||||
x:Name="editGitPath"
|
||||
Height="24"
|
||||
Text="{Binding Source={x:Static models:Preference.Instance}, Path=Git.Path, Mode=TwoWay}"
|
||||
Placeholder="{StaticResource Text.Preference.Git.Path.Placeholder}"/>
|
||||
<controls:IconButton
|
||||
Grid.Column="1"
|
||||
Click="SelectGitPath"
|
||||
Width="24" Height="24"
|
||||
Margin="4,0,0,0" Padding="4"
|
||||
BorderThickness="1" BorderBrush="{StaticResource Brush.Border1}"
|
||||
Icon="{StaticResource Icon.Folder.Open}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- Default Clone Dir -->
|
||||
<TextBlock
|
||||
Grid.Row="9" Grid.Column="0"
|
||||
Text="{StaticResource Text.Preference.Git.Dir}"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0,0,6,0"/>
|
||||
<Grid Grid.Row="9" Grid.Column="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<controls:TextEdit
|
||||
Grid.Column="0"
|
||||
x:Name="txtGitCloneDir"
|
||||
Height="24"
|
||||
Placeholder="{StaticResource Text.Preference.Git.Dir.Placeholder}"
|
||||
Text="{Binding Source={x:Static models:Preference.Instance}, Path=Git.DefaultCloneDir, Mode=TwoWay}"/>
|
||||
<controls:IconButton
|
||||
Grid.Column="1"
|
||||
Click="SelectGitCloneDir"
|
||||
Width="24" Height="24"
|
||||
Margin="4,0,0,0" Padding="4"
|
||||
BorderThickness="1" BorderBrush="{StaticResource Brush.Border1}"
|
||||
Icon="{StaticResource Icon.Folder.Open}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- User -->
|
||||
<TextBlock
|
||||
Grid.Row="10" Grid.Column="0"
|
||||
Text="{StaticResource Text.Preference.Git.User}"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0,0,6,0"/>
|
||||
<controls:TextEdit
|
||||
Grid.Row="10" Grid.Column="1"
|
||||
Height="24"
|
||||
Text="{Binding ElementName=me, Path=User, Mode=TwoWay}"
|
||||
Placeholder="{StaticResource Text.Preference.Git.User.Placeholder}"/>
|
||||
|
||||
<!-- Email -->
|
||||
<TextBlock
|
||||
Grid.Row="11" Grid.Column="0"
|
||||
Text="{StaticResource Text.Preference.Git.Email}"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0,0,6,0"/>
|
||||
<controls:TextEdit
|
||||
Grid.Row="11" Grid.Column="1"
|
||||
Height="24"
|
||||
Text="{Binding ElementName=me, Path=Email, Mode=TwoWay}"
|
||||
Placeholder="{StaticResource Text.Preference.Git.Email.Placeholder}"/>
|
||||
|
||||
<!-- CRLF -->
|
||||
<TextBlock
|
||||
Grid.Row="12" Grid.Column="0"
|
||||
Text="{StaticResource Text.Preference.Git.CRLF}"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0,0,6,0"/>
|
||||
<ComboBox
|
||||
Grid.Row="12" Grid.Column="1"
|
||||
Height="24"
|
||||
ItemsSource="{Binding Source={x:Static models:CRLFOption.Supported}}"
|
||||
SelectedValuePath="Value"
|
||||
SelectedValue="{Binding ElementName=me, Path=CRLF, Mode=TwoWay}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Height="20">
|
||||
<TextBlock Text="{Binding Display}" Margin="2,0"/>
|
||||
<TextBlock Text="{Binding Desc}" Margin="8,0,0,0" FontSize="10" Foreground="{StaticResource Brush.FG2}"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<!-- Merge Tool Group -->
|
||||
<TextBlock
|
||||
Grid.Row="14" Grid.Column="0" Grid.ColumnSpan="2"
|
||||
Text="{StaticResource Text.Preference.Merger}"
|
||||
FontSize="16" FontWeight="DemiBold"
|
||||
Foreground="{StaticResource Brush.FG2}"/>
|
||||
|
||||
<!-- Merge Tool Type -->
|
||||
<TextBlock
|
||||
Grid.Row="15" Grid.Column="0"
|
||||
Text="{StaticResource Text.Preference.Merger.Type}"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0,0,6,0"/>
|
||||
<ComboBox
|
||||
Grid.Row="15" Grid.Column="1"
|
||||
Height="24"
|
||||
ItemsSource="{Binding Source={x:Static models:MergeTool.Supported}}"
|
||||
DisplayMemberPath="Name"
|
||||
SelectedValuePath="Type"
|
||||
SelectedValue="{Binding Source={x:Static models:Preference.Instance}, Path=MergeTool.Type, Mode=TwoWay}"
|
||||
SelectionChanged="MergeToolChanged"/>
|
||||
|
||||
<!-- Merge Tool Executable Path -->
|
||||
<TextBlock
|
||||
Grid.Row="16" Grid.Column="0"
|
||||
Text="{StaticResource Text.Preference.Merger.Path}"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0,0,6,0"/>
|
||||
<Grid Grid.Row="16" Grid.Column="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<controls:TextEdit
|
||||
Grid.Column="0"
|
||||
Height="24"
|
||||
x:Name="txtMergeExec"
|
||||
Placeholder="{StaticResource Text.Preference.Merger.Path.Placeholder}"
|
||||
Text="{Binding Source={x:Static models:Preference.Instance}, Path=MergeTool.Path, Mode=TwoWay}"/>
|
||||
<controls:IconButton
|
||||
Grid.Column="1"
|
||||
Click="SelectMergeTool"
|
||||
Width="24" Height="24"
|
||||
Margin="4,0,0,0" Padding="4"
|
||||
BorderThickness="1" BorderBrush="{StaticResource Brush.Border1}"
|
||||
Icon="{StaticResource Icon.Folder.Open}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- Merge Tool Command -->
|
||||
<TextBlock
|
||||
Grid.Row="17" Grid.Column="0"
|
||||
Text="{StaticResource Text.Preference.Merger.Cmd}"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0,0,6,0"/>
|
||||
<TextBlock
|
||||
Grid.Row="17" Grid.Column="1"
|
||||
x:Name="txtMergeCmd"
|
||||
Text="{Binding ElementName=me, Path=MergeCmd}"
|
||||
Foreground="{StaticResource Brush.FG2}"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</controls:WindowBorder>
|
||||
</Window>
|
101
src/Views/Preference.xaml.cs
Normal file
101
src/Views/Preference.xaml.cs
Normal file
|
@ -0,0 +1,101 @@
|
|||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SourceGit.Views {
|
||||
|
||||
/// <summary>
|
||||
/// 设置面板
|
||||
/// </summary>
|
||||
public partial class Preference : Window {
|
||||
|
||||
public string User { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string CRLF { get; set; }
|
||||
public string MergeCmd { get; set; }
|
||||
|
||||
public Preference() {
|
||||
User = new Commands.Config().Get("user.name");
|
||||
Email = new Commands.Config().Get("user.email");
|
||||
CRLF = new Commands.Config().Get("core.autocrlf");
|
||||
if (string.IsNullOrEmpty(CRLF)) CRLF = "false";
|
||||
|
||||
var merger = Models.MergeTool.Supported.Find(x => x.Type == Models.Preference.Instance.MergeTool.Type);
|
||||
if (merger != null) MergeCmd = merger.Cmd;
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
#region EVENTS
|
||||
private void SelectGitPath(object sender, RoutedEventArgs e) {
|
||||
var dialog = new OpenFileDialog();
|
||||
dialog.Filter = "Git Executable|git.exe";
|
||||
dialog.FileName = "git.exe";
|
||||
dialog.Title = App.Text("Preference.Dialog.GitExe");
|
||||
dialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
|
||||
dialog.CheckFileExists = true;
|
||||
|
||||
if (dialog.ShowDialog() == true) {
|
||||
Models.Preference.Instance.Git.Path = dialog.FileName;
|
||||
editGitPath?.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectGitCloneDir(object sender, RoutedEventArgs e) {
|
||||
FolderBrowser.Open(this, App.Text("Preference.Dialog.GitDir"), path => {
|
||||
Models.Preference.Instance.Git.DefaultCloneDir = path;
|
||||
txtGitCloneDir?.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
|
||||
});
|
||||
}
|
||||
|
||||
private void SelectMergeTool(object sender, RoutedEventArgs e) {
|
||||
var type = Models.Preference.Instance.MergeTool.Type;
|
||||
var tool = Models.MergeTool.Supported.Find(x => x.Type == type);
|
||||
|
||||
if (tool == null || tool.Type == 0) return;
|
||||
|
||||
var dialog = new OpenFileDialog();
|
||||
dialog.Filter = $"{tool.Name} Executable|{tool.Exec}";
|
||||
dialog.Title = App.Text("Preference.Dialog.Merger", tool.Name);
|
||||
dialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
|
||||
dialog.CheckFileExists = true;
|
||||
|
||||
if (dialog.ShowDialog() == true) {
|
||||
Models.Preference.Instance.MergeTool.Path = dialog.FileName;
|
||||
txtMergeExec?.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
|
||||
}
|
||||
}
|
||||
|
||||
private void MergeToolChanged(object sender, SelectionChangedEventArgs e) {
|
||||
var selector = sender as ComboBox;
|
||||
var type = (int)selector.SelectedValue;
|
||||
var tool = Models.MergeTool.Supported.Find(x => x.Type == type);
|
||||
if (tool == null) return;
|
||||
|
||||
Models.Preference.Instance.MergeTool.Path = tool.Finder();
|
||||
MergeCmd = tool.Cmd;
|
||||
|
||||
txtMergeExec?.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
|
||||
txtMergeCmd?.GetBindingExpression(TextBlock.TextProperty).UpdateTarget();
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void Quit(object sender, RoutedEventArgs e) {
|
||||
var cmd = new Commands.Config();
|
||||
var oldUser = cmd.Get("user.name");
|
||||
if (oldUser != User) cmd.Set("user.name", User);
|
||||
|
||||
var oldEmail = cmd.Get("user.email");
|
||||
if (oldEmail != Email) cmd.Set("user.email", Email);
|
||||
|
||||
var oldCRLF = cmd.Get("core.autocrlf");
|
||||
if (oldCRLF != CRLF) cmd.Set("core.autocrlf", CRLF);
|
||||
|
||||
Models.Preference.Save();
|
||||
Close();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
115
src/Views/Upgrade.xaml
Normal file
115
src/Views/Upgrade.xaml
Normal file
|
@ -0,0 +1,115 @@
|
|||
<Window x:Class="SourceGit.Views.Upgrade"
|
||||
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:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
mc:Ignorable="d"
|
||||
Title="{StaticResource Text.UpdateAvailable}"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
Height="400" Width="500"
|
||||
ResizeMode="NoResize">
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome UseAeroCaptionButtons="False" CornerRadius="0" CaptionHeight="28" ResizeBorderThickness="1"/>
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
<controls:WindowBorder>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="32"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Title Bar -->
|
||||
<Grid Grid.Row="0" Background="{StaticResource Brush.TitleBar}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Icon -->
|
||||
<Path Grid.Column="0" Width="16" Height="16" Margin="6,0" Data="{StaticResource Icon.Fetch}"/>
|
||||
|
||||
<!-- Title -->
|
||||
<TextBlock Grid.Column="1" Text="{StaticResource Text.UpdateAvailable}"/>
|
||||
|
||||
<!-- Close -->
|
||||
<controls:IconButton
|
||||
Grid.Column="3"
|
||||
Click="Quit"
|
||||
Width="32" Height="32"
|
||||
Padding="9"
|
||||
Icon="{StaticResource Icon.Close}"
|
||||
HoverBackground="Red"
|
||||
WindowChrome.IsHitTestVisibleInChrome="True"/>
|
||||
</Grid>
|
||||
|
||||
<!-- Body -->
|
||||
<StackPanel Grid.Row="1" Orientation="Vertical" Margin="8,16,8,0">
|
||||
<!-- Title -->
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
|
||||
<Path Width="20" Height="20" Data="{StaticResource Icon.Git}" Fill="{StaticResource Brush.Logo}"/>
|
||||
<TextBlock x:Name="txtRelease" Margin="8,0,0,0" FontSize="18" FontWeight="Bold" Text="Release 1.0 Is Out!!!"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Release Info -->
|
||||
<Grid Margin="0,12,0,0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="20"/>
|
||||
<RowDefinition Height="20"/>
|
||||
<RowDefinition Height="20"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="120"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="{StaticResource Text.UpdateAvailable.Time}" FontWeight="Bold"/>
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding ElementName=me, Path=Version.PublishTime}"/>
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="{StaticResource Text.UpdateAvailable.Based}" FontWeight="Bold"/>
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding ElementName=me, Path=Version.CommitSHA}"/>
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="{StaticResource Text.UpdateAvailable.IsPreRelease}" FontWeight="Bold"/>
|
||||
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding ElementName=me, Path=Version.IsPrerelease}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<!-- CHANGELOG -->
|
||||
<Border Grid.Row="2" Margin="8" Background="{StaticResource Brush.Contents}" BorderBrush="{StaticResource Brush.Border1}" BorderThickness="1">
|
||||
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
|
||||
<TextBlock
|
||||
FontSize="10pt"
|
||||
FontFamily="Consolas"
|
||||
Padding="8"
|
||||
VerticalAlignment="Top"
|
||||
Foreground="{StaticResource Brush.FG2}"
|
||||
Text="{Binding ElementName=me, Path=Version.Body}"/>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
|
||||
<!-- Options -->
|
||||
<StackPanel Grid.Row="4" HorizontalAlignment="Center" VerticalAlignment="Top" Orientation="Horizontal">
|
||||
<Button
|
||||
Click="Download"
|
||||
Width="100" Height="24"
|
||||
Content="{StaticResource Text.UpdateAvailable.Download}"
|
||||
Background="{StaticResource Brush.Accent1}"
|
||||
BorderBrush="{StaticResource Brush.FG1}"
|
||||
BorderThickness="1"/>
|
||||
|
||||
<Button
|
||||
Click="Quit"
|
||||
Width="100" Height="24"
|
||||
Margin="8,0,0,0"
|
||||
Content="{StaticResource Text.Cancel}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</controls:WindowBorder>
|
||||
</Window>
|
34
src/Views/Upgrade.xaml.cs
Normal file
34
src/Views/Upgrade.xaml.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using System.Diagnostics;
|
||||
using System.Windows;
|
||||
|
||||
namespace SourceGit.Views {
|
||||
/// <summary>
|
||||
/// 新版本提示窗口
|
||||
/// </summary>
|
||||
public partial class Upgrade : Window {
|
||||
public Models.Version Version { get; set; } = new Models.Version();
|
||||
|
||||
public Upgrade(Models.Version ver) {
|
||||
Version = ver;
|
||||
InitializeComponent();
|
||||
txtRelease.Text = App.Text("UpdateAvailable.Title", ver.Name);
|
||||
}
|
||||
|
||||
public static void Open(Window owner, Models.Version ver) {
|
||||
var dialog = new Upgrade(ver) { Owner = owner };
|
||||
dialog.ShowDialog();
|
||||
}
|
||||
|
||||
private void Download(object sender, RoutedEventArgs e) {
|
||||
var info = new ProcessStartInfo("cmd", $"/c start https://gitee.com/sourcegit/SourceGit/releases/{Version.TagName}");
|
||||
info.CreateNoWindow = true;
|
||||
|
||||
Process.Start(info);
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void Quit(object sender, RoutedEventArgs e) {
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
27
src/Views/Validations/BranchName.cs
Normal file
27
src/Views/Validations/BranchName.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SourceGit.Views.Validations {
|
||||
public class BranchName : ValidationRule {
|
||||
private static readonly Regex REG_FORMAT = new Regex(@"^[\w\-/\.]+$");
|
||||
|
||||
public Models.Repository Repo { get; set; }
|
||||
public string Prefix { get; set; } = "";
|
||||
|
||||
public override ValidationResult Validate(object value, CultureInfo cultureInfo) {
|
||||
var name = value as string;
|
||||
if (string.IsNullOrEmpty(name)) new ValidationResult(false, App.Text("EmptyBranchName"));
|
||||
if (!REG_FORMAT.IsMatch(name)) return new ValidationResult(false, App.Text("BadBranchName"));
|
||||
|
||||
name = Prefix + name;
|
||||
foreach (var t in Repo.Branches) {
|
||||
if (t.Name == name) {
|
||||
return new ValidationResult(false, App.Text("DuplicatedBranchName"));
|
||||
}
|
||||
}
|
||||
|
||||
return ValidationResult.ValidResult;
|
||||
}
|
||||
}
|
||||
}
|
13
src/Views/Validations/CloneDir.cs
Normal file
13
src/Views/Validations/CloneDir.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SourceGit.Views.Validations {
|
||||
public class CloneDir : ValidationRule {
|
||||
public override ValidationResult Validate(object value, CultureInfo cultureInfo) {
|
||||
return Directory.Exists(value as string)
|
||||
? ValidationResult.ValidResult
|
||||
: new ValidationResult(false, App.Text("BadCloneFolder"));
|
||||
}
|
||||
}
|
||||
}
|
13
src/Views/Validations/CommitMessage.cs
Normal file
13
src/Views/Validations/CommitMessage.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System.Globalization;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SourceGit.Views.Validations {
|
||||
public class CommitMessage : ValidationRule {
|
||||
public override ValidationResult Validate(object value, CultureInfo cultureInfo) {
|
||||
var subject = value as string;
|
||||
return string.IsNullOrWhiteSpace(subject)
|
||||
? new ValidationResult(false, App.Text("EmptyCommitMessage"))
|
||||
: ValidationResult.ValidResult;
|
||||
}
|
||||
}
|
||||
}
|
18
src/Views/Validations/GitURL.cs
Normal file
18
src/Views/Validations/GitURL.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SourceGit.Views.Validations {
|
||||
|
||||
public class GitURL : ValidationRule {
|
||||
public override ValidationResult Validate(object value, CultureInfo cultureInfo) {
|
||||
string url = value as string;
|
||||
bool valid = !string.IsNullOrEmpty(url)
|
||||
&& (url.StartsWith("http://", StringComparison.Ordinal)
|
||||
|| url.StartsWith("https://", StringComparison.Ordinal)
|
||||
|| url.StartsWith("git@", StringComparison.Ordinal)
|
||||
|| url.StartsWith("file://", StringComparison.Ordinal));
|
||||
return valid ? ValidationResult.ValidResult : new ValidationResult(false, App.Text("BadRemoteUri"));
|
||||
}
|
||||
}
|
||||
}
|
16
src/Views/Validations/LocalRepositoryName.cs
Normal file
16
src/Views/Validations/LocalRepositoryName.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SourceGit.Views.Validations {
|
||||
public class LocalRepositoryName : ValidationRule {
|
||||
private static readonly Regex REG_FORMAT = new Regex(@"^[\w\-]+$");
|
||||
|
||||
public override ValidationResult Validate(object value, CultureInfo cultureInfo) {
|
||||
var name = value as string;
|
||||
if (string.IsNullOrEmpty(name)) return ValidationResult.ValidResult;
|
||||
if (!REG_FORMAT.IsMatch(name)) return new ValidationResult(false, App.Text("BadLocalName"));
|
||||
return ValidationResult.ValidResult;
|
||||
}
|
||||
}
|
||||
}
|
13
src/Views/Validations/PatchFile.cs
Normal file
13
src/Views/Validations/PatchFile.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SourceGit.Views.Validations {
|
||||
public class PatchFile : ValidationRule {
|
||||
public override ValidationResult Validate(object value, CultureInfo cultureInfo) {
|
||||
return File.Exists(value as string)
|
||||
? ValidationResult.ValidResult
|
||||
: new ValidationResult(false, App.Text("BadPatchFile"));
|
||||
}
|
||||
}
|
||||
}
|
31
src/Views/Validations/RemoteName.cs
Normal file
31
src/Views/Validations/RemoteName.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SourceGit.Views.Validations {
|
||||
public class RemoteName : ValidationRule {
|
||||
private static readonly Regex REG_FORMAT = new Regex(@"^[\w\-\.]+$");
|
||||
|
||||
public Models.Repository Repo { get; set; }
|
||||
public bool IsOptional { get; set; }
|
||||
|
||||
public override ValidationResult Validate(object value, CultureInfo cultureInfo) {
|
||||
var name = value as string;
|
||||
if (string.IsNullOrEmpty(name)) {
|
||||
return IsOptional ? ValidationResult.ValidResult : new ValidationResult(false, App.Text("EmptyRemoteName"));
|
||||
}
|
||||
|
||||
if (!REG_FORMAT.IsMatch(name)) return new ValidationResult(false, App.Text("BadRemoteName"));
|
||||
|
||||
if (Repo != null) {
|
||||
foreach (var t in Repo.Remotes) {
|
||||
if (t.Name == name) {
|
||||
return new ValidationResult(false, App.Text("DuplicatedRemoteName"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ValidationResult.ValidResult;
|
||||
}
|
||||
}
|
||||
}
|
17
src/Views/Validations/SubmodulePath.cs
Normal file
17
src/Views/Validations/SubmodulePath.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SourceGit.Views.Validations {
|
||||
public class SubmodulePath : ValidationRule {
|
||||
public override ValidationResult Validate(object value, CultureInfo cultureInfo) {
|
||||
var path = value as string;
|
||||
if (string.IsNullOrEmpty(path)) return ValidationResult.ValidResult;
|
||||
|
||||
var regex = new Regex(@"^[\w\-\._/]+$");
|
||||
var succ = regex.IsMatch(path.Trim());
|
||||
return !succ ? new ValidationResult(false, App.Text("BadSubmodulePath")) : ValidationResult.ValidResult;
|
||||
}
|
||||
}
|
||||
}
|
26
src/Views/Validations/TagName.cs
Normal file
26
src/Views/Validations/TagName.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SourceGit.Views.Validations {
|
||||
public class TagName : ValidationRule {
|
||||
private static readonly Regex REG_FORMAT = new Regex(@"^[\w\-\.]+$");
|
||||
|
||||
public List<Models.Tag> Tags { get; set; }
|
||||
|
||||
public override ValidationResult Validate(object value, CultureInfo cultureInfo) {
|
||||
var name = value as string;
|
||||
if (string.IsNullOrEmpty(name)) new ValidationResult(false, App.Text("EmptyTagName"));
|
||||
if (!REG_FORMAT.IsMatch(name)) return new ValidationResult(false, App.Text("BadTagName"));
|
||||
|
||||
foreach (var t in Tags) {
|
||||
if (t.Name == name) {
|
||||
return new ValidationResult(false, App.Text("DuplicatedTagName"));
|
||||
}
|
||||
}
|
||||
|
||||
return ValidationResult.ValidResult;
|
||||
}
|
||||
}
|
||||
}
|
196
src/Views/Widgets/CommitChanges.xaml
Normal file
196
src/Views/Widgets/CommitChanges.xaml
Normal file
|
@ -0,0 +1,196 @@
|
|||
<UserControl x:Class="SourceGit.Views.Widgets.CommitChanges"
|
||||
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:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
xmlns:converters="clr-namespace:SourceGit.Views.Converters"
|
||||
xmlns:models="clr-namespace:SourceGit.Models"
|
||||
xmlns:widgets="clr-namespace:SourceGit.Views.Widgets"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<UserControl.Resources>
|
||||
<converters:PureFileName x:Key="PureFileName"/>
|
||||
<converters:PureFolderName x:Key="PureFolderName"/>
|
||||
|
||||
<Style x:Key="Style.DataGridRow.Changes" TargetType="{x:Type DataGridRow}" BasedOn="{StaticResource Style.DataGridRow}">
|
||||
<EventSetter Event="RequestBringIntoView" Handler="OnRequestBringIntoView"/>
|
||||
<EventSetter Event="ContextMenuOpening" Handler="OnDataGridContextMenuOpening"/>
|
||||
</Style>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="200" MinWidth="200" MaxWidth="400"/>
|
||||
<ColumnDefinition Width="1"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid Grid.Column="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<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"
|
||||
BorderBrush="{StaticResource Brush.Border2}"
|
||||
BorderThickness="1"/>
|
||||
<Path
|
||||
Grid.Column="0"
|
||||
Width="14" Height="14"
|
||||
Fill="{StaticResource Brush.FG2}"
|
||||
Data="{StaticResource Icon.Search}"
|
||||
IsHitTestVisible="False"/>
|
||||
<controls:TextEdit
|
||||
Grid.Column="1"
|
||||
Height="24"
|
||||
Margin="0"
|
||||
Placeholder="{StaticResource Text.CommitViewer.Changes.Search}"
|
||||
BorderThickness="0"
|
||||
TextChanged="SearchFilterChanged"/>
|
||||
|
||||
<controls:ChangeDisplaySwitcher
|
||||
Grid.Column="2"
|
||||
x:Name="modeSwitcher"
|
||||
Margin="4,0,0,0" Width="18" Height="18"
|
||||
Mode="{Binding Source={x:Static models:Preference.Instance}, Path=Window.ChangeInCommitInfo, Mode=TwoWay}"
|
||||
ModeChanged="OnDisplayModeChanged"/>
|
||||
</Grid>
|
||||
|
||||
<Border
|
||||
Grid.Row="1"
|
||||
BorderBrush="{StaticResource Brush.Border2}"
|
||||
BorderThickness="1"
|
||||
Background="{StaticResource Brush.Contents}">
|
||||
<Grid>
|
||||
<controls:Tree
|
||||
x:Name="modeTree"
|
||||
FontFamily="Consolas"
|
||||
SelectionChanged="OnTreeSelectionChanged">
|
||||
<controls:Tree.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type controls:TreeItem}" BasedOn="{StaticResource Style.TreeItem}">
|
||||
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
|
||||
<EventSetter Event="ContextMenuOpening" Handler="OnTreeContextMenuOpening"/>
|
||||
</Style>
|
||||
</controls:Tree.ItemContainerStyle>
|
||||
|
||||
<controls:Tree.ItemTemplate>
|
||||
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
|
||||
<Grid Height="24">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="18"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<controls:ChangeStatusIcon
|
||||
Grid.Column="0"
|
||||
Width="14" Height="14"
|
||||
IsLocalChange="False"
|
||||
Change="{Binding Change}"/>
|
||||
|
||||
<Path
|
||||
Grid.Column="0"
|
||||
x:Name="IconFolder"
|
||||
Width="14" Height="14"
|
||||
Fill="Goldenrod"
|
||||
Data="{StaticResource Icon.Folder.Fill}"/>
|
||||
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Text="{Binding Path, Converter={StaticResource PureFileName}}"
|
||||
Margin="4,0,0,0"
|
||||
FontSize="11"/>
|
||||
</Grid>
|
||||
|
||||
<HierarchicalDataTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding IsFolder}" Value="False">
|
||||
<Setter TargetName="IconFolder" Property="Visibility" Value="Collapsed"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding IsExpanded}" Value="True">
|
||||
<Setter TargetName="IconFolder" Property="Data" Value="{StaticResource Icon.Folder.Open}"/>
|
||||
</DataTrigger>
|
||||
</HierarchicalDataTemplate.Triggers>
|
||||
</HierarchicalDataTemplate>
|
||||
</controls:Tree.ItemTemplate>
|
||||
</controls:Tree>
|
||||
|
||||
<DataGrid
|
||||
x:Name="modeList"
|
||||
RowHeight="24"
|
||||
SelectionMode="Single"
|
||||
SelectionUnit="FullRow"
|
||||
SelectionChanged="OnListSelectionChanged"
|
||||
RowStyle="{StaticResource Style.DataGridRow.Changes}">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTemplateColumn Width="22" IsReadOnly="True">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate DataType="{x:Type models:Change}">
|
||||
<controls:ChangeStatusIcon Width="14" Height="14" IsLocalChange="False" Change="{Binding}"/>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTemplateColumn IsReadOnly="True">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock FontFamily="Consolas" Margin="2,0,0,0" Text="{Binding Path}"/>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
<DataGrid
|
||||
x:Name="modeGrid"
|
||||
RowHeight="24"
|
||||
SelectionMode="Single"
|
||||
SelectionUnit="FullRow"
|
||||
SelectionChanged="OnGridSelectionChanged"
|
||||
RowStyle="{StaticResource Style.DataGridRow.Changes}">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTemplateColumn Width="22" IsReadOnly="True">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate DataType="{x:Type models:Change}">
|
||||
<controls:ChangeStatusIcon Width="14" Height="14" IsLocalChange="False" Change="{Binding}"/>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTemplateColumn IsReadOnly="True">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock FontFamily="Consolas" Margin="2,0,0,0" Text="{Binding Path, Converter={StaticResource PureFileName}}"/>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTemplateColumn IsReadOnly="True">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock FontFamily="Consolas" Margin="4,0,0,0" Text="{Binding Path, Converter={StaticResource PureFolderName}}" Foreground="{StaticResource Brush.FG2}"/>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<GridSplitter
|
||||
Grid.Column="1"
|
||||
Width="1"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Stretch"
|
||||
Background="Transparent"/>
|
||||
|
||||
<widgets:DiffViewer
|
||||
Grid.Column="2"
|
||||
x:Name="diffViewer"
|
||||
Margin="4,0,0,0"/>
|
||||
</Grid>
|
||||
</UserControl>
|
310
src/Views/Widgets/CommitChanges.xaml.cs
Normal file
310
src/Views/Widgets/CommitChanges.xaml.cs
Normal file
|
@ -0,0 +1,310 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SourceGit.Views.Widgets {
|
||||
/// <summary>
|
||||
/// 显示提交中的变更列表
|
||||
/// </summary>
|
||||
public partial class CommitChanges : UserControl {
|
||||
private string repo = null;
|
||||
private List<Models.Commit> range = null;
|
||||
private List<Models.Change> cachedChanges = new List<Models.Change>();
|
||||
private string filter = null;
|
||||
|
||||
public class ChangeNode {
|
||||
public string Path { get; set; } = "";
|
||||
public Models.Change Change { get; set; } = null;
|
||||
public bool IsExpanded { get; set; } = false;
|
||||
public bool IsFolder => Change == null;
|
||||
public List<ChangeNode> Children { get; set; } = new List<ChangeNode>();
|
||||
}
|
||||
|
||||
public CommitChanges() {
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public void SetData(string repo, List<Models.Commit> range, List<Models.Change> changes) {
|
||||
this.repo = repo;
|
||||
this.range = range;
|
||||
this.cachedChanges = changes;
|
||||
|
||||
UpdateVisible();
|
||||
}
|
||||
|
||||
public void UpdateVisible() {
|
||||
Task.Run(() => {
|
||||
// 筛选出可见的列表
|
||||
List<Models.Change> visible;
|
||||
if (string.IsNullOrEmpty(filter)) {
|
||||
visible = cachedChanges;
|
||||
} else {
|
||||
visible = cachedChanges.Where(x => x.Path.ToUpper().Contains(filter)).ToList();
|
||||
}
|
||||
|
||||
// 排序
|
||||
visible.Sort((l, r) => l.Path.CompareTo(r.Path));
|
||||
|
||||
// 生成树节点
|
||||
var nodes = new List<ChangeNode>();
|
||||
var folders = new Dictionary<string, ChangeNode>();
|
||||
var expanded = visible.Count <= 50;
|
||||
|
||||
foreach (var c in visible) {
|
||||
var sepIdx = c.Path.IndexOf('/');
|
||||
if (sepIdx == -1) {
|
||||
nodes.Add(new ChangeNode() {
|
||||
Path = c.Path,
|
||||
Change = c,
|
||||
IsExpanded = false
|
||||
});
|
||||
} else {
|
||||
ChangeNode 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 ChangeNode() {
|
||||
Path = folder,
|
||||
Change = null,
|
||||
IsExpanded = expanded
|
||||
};
|
||||
nodes.Add(lastFolder);
|
||||
folders.Add(folder, lastFolder);
|
||||
} else {
|
||||
var cur = new ChangeNode() {
|
||||
Path = folder,
|
||||
Change = null,
|
||||
IsExpanded = expanded
|
||||
};
|
||||
folders.Add(folder, cur);
|
||||
lastFolder.Children.Add(cur);
|
||||
lastFolder = cur;
|
||||
}
|
||||
|
||||
start = sepIdx + 1;
|
||||
sepIdx = c.Path.IndexOf('/', start);
|
||||
}
|
||||
|
||||
lastFolder.Children.Add(new ChangeNode() {
|
||||
Path = c.Path,
|
||||
Change = c,
|
||||
IsExpanded = false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
folders.Clear();
|
||||
SortFileNodes(nodes);
|
||||
|
||||
Dispatcher.Invoke(() => {
|
||||
modeTree.ItemsSource = nodes;
|
||||
modeList.ItemsSource = visible;
|
||||
modeGrid.ItemsSource = visible;
|
||||
|
||||
UpdateMode();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void SortFileNodes(List<ChangeNode> nodes) {
|
||||
nodes.Sort((l, r) => {
|
||||
if (l.IsFolder == r.IsFolder) {
|
||||
return l.Path.CompareTo(r.Path);
|
||||
} else {
|
||||
return l.IsFolder ? -1 : 1;
|
||||
}
|
||||
});
|
||||
|
||||
foreach (var node in nodes) {
|
||||
if (node.Children.Count > 1) SortFileNodes(node.Children);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateMode() {
|
||||
var mode = modeSwitcher.Mode;
|
||||
|
||||
if (modeTree != null) {
|
||||
if (mode == Models.Change.DisplayMode.Tree) {
|
||||
modeTree.Visibility = Visibility.Visible;
|
||||
} else {
|
||||
modeTree.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
if (modeList != null) {
|
||||
if (mode == Models.Change.DisplayMode.List) {
|
||||
modeList.Visibility = Visibility.Visible;
|
||||
modeList.Columns[1].Width = DataGridLength.SizeToCells;
|
||||
modeList.Columns[1].Width = DataGridLength.Auto;
|
||||
} else {
|
||||
modeList.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
if (modeGrid != null) {
|
||||
if (mode == Models.Change.DisplayMode.Grid) {
|
||||
modeGrid.Visibility = Visibility.Visible;
|
||||
modeGrid.Columns[1].Width = DataGridLength.SizeToCells;
|
||||
modeGrid.Columns[1].Width = DataGridLength.Auto;
|
||||
modeGrid.Columns[2].Width = DataGridLength.SizeToCells;
|
||||
modeGrid.Columns[2].Width = DataGridLength.Auto;
|
||||
} else {
|
||||
modeGrid.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenChangeDiff(Models.Change change) {
|
||||
var revisions = new string[] { "", "" };
|
||||
if (range.Count == 2) {
|
||||
revisions[0] = range[0].SHA;
|
||||
revisions[1] = range[1].SHA;
|
||||
} else {
|
||||
revisions[0] = $"{range[0].SHA}^";
|
||||
revisions[1] = range[0].SHA;
|
||||
if (range[0].Parents.Count == 0) revisions[0] = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
||||
}
|
||||
|
||||
diffViewer.Diff(repo, new DiffViewer.Option() {
|
||||
RevisionRange = revisions,
|
||||
Path = change.Path,
|
||||
OrgPath = change.OriginalPath
|
||||
});
|
||||
}
|
||||
|
||||
private void OpenChangeContextMenu(Models.Change change) {
|
||||
var menu = new ContextMenu();
|
||||
var path = change.Path;
|
||||
|
||||
if (change.Index != Models.Change.Status.Deleted) {
|
||||
var history = new MenuItem();
|
||||
history.Header = App.Text("FileHistory");
|
||||
history.Click += (o, ev) => {
|
||||
var viewer = new Views.Histories(repo, path);
|
||||
viewer.Show();
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var blame = new MenuItem();
|
||||
blame.Header = App.Text("Blame");
|
||||
blame.Visibility = range.Count == 1 ? Visibility.Visible : Visibility.Collapsed;
|
||||
blame.Click += (obj, ev) => {
|
||||
var viewer = new Blame(repo, path, range[0].SHA);
|
||||
viewer.Show();
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var explore = new MenuItem();
|
||||
explore.Header = App.Text("RevealFile");
|
||||
explore.Click += (o, ev) => {
|
||||
var full = Path.GetFullPath(repo + "\\" + path);
|
||||
Process.Start("explorer", $"/select,{full}");
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var saveAs = new MenuItem();
|
||||
saveAs.Header = App.Text("SaveAs");
|
||||
saveAs.Visibility = range.Count == 1 ? Visibility.Visible : Visibility.Collapsed;
|
||||
saveAs.Click += (obj, ev) => {
|
||||
FolderBrowser.Open(null, App.Text("SaveFileTo"), saveTo => {
|
||||
var full = Path.Combine(saveTo, Path.GetFileName(path));
|
||||
new Commands.SaveRevisionFile(repo, path, range[0].SHA, full).Exec();
|
||||
});
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
menu.Items.Add(history);
|
||||
menu.Items.Add(blame);
|
||||
menu.Items.Add(explore);
|
||||
menu.Items.Add(saveAs);
|
||||
}
|
||||
|
||||
var copyPath = new MenuItem();
|
||||
copyPath.Header = App.Text("CopyPath");
|
||||
copyPath.Click += (obj, ev) => {
|
||||
Clipboard.SetText(path);
|
||||
};
|
||||
|
||||
menu.Items.Add(copyPath);
|
||||
menu.IsOpen = true;
|
||||
}
|
||||
|
||||
private void OnDisplayModeChanged(object sender, RoutedEventArgs e) {
|
||||
UpdateMode();
|
||||
}
|
||||
|
||||
private void SearchFilterChanged(object sender, TextChangedEventArgs e) {
|
||||
var edit = sender as Controls.TextEdit;
|
||||
filter = edit.Text.ToUpper();
|
||||
UpdateVisible();
|
||||
}
|
||||
|
||||
private void OnRequestBringIntoView(object sender, RequestBringIntoViewEventArgs e) {
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void OnTreeSelectionChanged(object sender, RoutedEventArgs e) {
|
||||
if (Models.Preference.Instance.Window.ChangeInCommitInfo != Models.Change.DisplayMode.Tree) return;
|
||||
|
||||
diffViewer.Reset();
|
||||
if (modeTree.Selected.Count == 0) return;
|
||||
|
||||
var change = (modeTree.Selected[0] as ChangeNode).Change;
|
||||
if (change == null) return;
|
||||
|
||||
OpenChangeDiff(change);
|
||||
}
|
||||
|
||||
private void OnListSelectionChanged(object sender, SelectionChangedEventArgs e) {
|
||||
if (Models.Preference.Instance.Window.ChangeInCommitInfo != Models.Change.DisplayMode.List) return;
|
||||
|
||||
diffViewer.Reset();
|
||||
|
||||
var change = (sender as DataGrid).SelectedItem as Models.Change;
|
||||
if (change == null) return;
|
||||
|
||||
OpenChangeDiff(change);
|
||||
}
|
||||
|
||||
private void OnGridSelectionChanged(object sender, SelectionChangedEventArgs e) {
|
||||
if (Models.Preference.Instance.Window.ChangeInCommitInfo != Models.Change.DisplayMode.Grid) return;
|
||||
|
||||
diffViewer.Reset();
|
||||
|
||||
var change = (sender as DataGrid).SelectedItem as Models.Change;
|
||||
if (change == null) return;
|
||||
|
||||
OpenChangeDiff(change);
|
||||
}
|
||||
|
||||
private void OnTreeContextMenuOpening(object sender, ContextMenuEventArgs e) {
|
||||
var item = sender as Controls.TreeItem;
|
||||
if (item == null) return;
|
||||
|
||||
var node = item.DataContext as ChangeNode;
|
||||
if (node == null || node.IsFolder) return;
|
||||
|
||||
OpenChangeContextMenu(node.Change);
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void OnDataGridContextMenuOpening(object sender, ContextMenuEventArgs e) {
|
||||
var row = sender as DataGridRow;
|
||||
if (row == null) return;
|
||||
|
||||
var change = row.Item as Models.Change;
|
||||
if (change == null) return;
|
||||
|
||||
OpenChangeContextMenu(change);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
344
src/Views/Widgets/CommitDetail.xaml
Normal file
344
src/Views/Widgets/CommitDetail.xaml
Normal file
|
@ -0,0 +1,344 @@
|
|||
<UserControl x:Class="SourceGit.Views.Widgets.CommitDetail"
|
||||
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:controls="clr-namespace:SourceGit.Views.Controls"
|
||||
xmlns:converters="clr-namespace:SourceGit.Views.Converters"
|
||||
xmlns:models="clr-namespace:SourceGit.Models"
|
||||
xmlns:widgets="clr-namespace:SourceGit.Views.Widgets"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<UserControl.Resources>
|
||||
<Style x:Key="Style.DataGridRow.TextPreview" TargetType="{x:Type DataGridRow}" BasedOn="{StaticResource Style.DataGridRow}">
|
||||
<EventSetter Event="RequestBringIntoView" Handler="OnRequestBringIntoView"/>
|
||||
</Style>
|
||||
<converters:PureFileName x:Key="PureFileName"/>
|
||||
</UserControl.Resources>
|
||||
|
||||
<TabControl>
|
||||
<TabItem Header="{StaticResource Text.CommitViewer.Info}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Author & Committer -->
|
||||
<Grid Grid.Row="0" Margin="0,8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="96"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="96"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Author Avatar -->
|
||||
<controls:Avatar
|
||||
Grid.Column="0"
|
||||
x:Name="avatarAuthor"
|
||||
Width="64" Height="64"
|
||||
HorizontalAlignment="Right"/>
|
||||
|
||||
<!-- Author Info -->
|
||||
<StackPanel Grid.Column="1" Margin="16,0,8,0" Orientation="Vertical">
|
||||
<TextBlock Text="{StaticResource Text.CommitViewer.Info.Author}" Foreground="{StaticResource Brush.FG2}"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,12,0,8">
|
||||
<controls:TextEdit x:Name="txtAuthorName" IsReadOnly="True" BorderThickness="0"/>
|
||||
<controls:TextEdit x:Name="txtAuthorEmail" IsReadOnly="True" BorderThickness="0" Foreground="{StaticResource Brush.FG2}" Margin="4,0,0,0"/>
|
||||
</StackPanel>
|
||||
<controls:TextEdit x:Name="txtAuthorTime" IsReadOnly="True" BorderThickness="0" Foreground="{StaticResource Brush.FG2}" FontSize="10"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Committer Avatar -->
|
||||
<controls:Avatar
|
||||
Grid.Column="2"
|
||||
x:Name="avatarCommitter"
|
||||
Width="64" Height="64"
|
||||
HorizontalAlignment="Right"/>
|
||||
|
||||
<!-- Committer Info -->
|
||||
<StackPanel x:Name="committerInfoPanel" Grid.Column="3" Margin="16,0,8,0" Orientation="Vertical">
|
||||
<TextBlock Text="{StaticResource Text.CommitViewer.Info.Committer}" Foreground="{StaticResource Brush.FG2}"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,12,0,6">
|
||||
<controls:TextEdit x:Name="txtCommitterName" IsReadOnly="True" BorderThickness="0"/>
|
||||
<controls:TextEdit x:Name="txtCommitterEmail" IsReadOnly="True" BorderThickness="0" Foreground="{StaticResource Brush.FG2}" Margin="4,0,0,0"/>
|
||||
</StackPanel>
|
||||
<controls:TextEdit x:Name="txtCommitterTime" IsReadOnly="True" BorderThickness="0" Foreground="{StaticResource Brush.FG2}" FontSize="10"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Line -->
|
||||
<Rectangle Grid.Row="1" Height="1" Margin="8" Fill="{StaticResource Brush.Border2}" VerticalAlignment="Center"/>
|
||||
|
||||
<!-- Base Information -->
|
||||
<Grid Grid.Row="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto" x:Name="rowParents"/>
|
||||
<RowDefinition Height="Auto" x:Name="rowRefs"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="96"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- SHA -->
|
||||
<TextBlock
|
||||
Grid.Row="0" Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
Text="{StaticResource Text.CommitViewer.Info.SHA}"
|
||||
Foreground="{StaticResource Brush.FG2}"/>
|
||||
<controls:TextEdit
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
Height="24"
|
||||
x:Name="txtSHA"
|
||||
IsReadOnly="True"
|
||||
BorderThickness="0"
|
||||
FontFamily="Consolas"
|
||||
Margin="11,0,0,0"/>
|
||||
|
||||
<!-- PARENTS -->
|
||||
<TextBlock
|
||||
Grid.Row="1" Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
Text="{StaticResource Text.CommitViewer.Info.Parents}"
|
||||
Foreground="{StaticResource Brush.FG2}"/>
|
||||
<ItemsControl Grid.Row="1" Grid.Column="1" x:Name="listParents" Height="24" Margin="13,0,0,0">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<VirtualizingStackPanel Orientation="Horizontal" VerticalAlignment="Center"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Margin="0,0,8,0" FontFamily="Consolas">
|
||||
<Hyperlink RequestNavigate="OnNavigateParent" NavigateUri="{Binding .}" ToolTip="{StaticResource Text.Goto}">
|
||||
<Run Text="{Binding .}"/>
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<!-- REFS -->
|
||||
<TextBlock
|
||||
Grid.Row="2" Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
Text="{StaticResource Text.CommitViewer.Info.Refs}"
|
||||
Foreground="{StaticResource Brush.FG2}"/>
|
||||
<ItemsControl Grid.Row="2" Grid.Column="1" x:Name="listRefs" Height="24" Margin="11,0,0,0">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<VirtualizingStackPanel Orientation="Horizontal" VerticalAlignment="Center"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="{x:Type models:Decorator}">
|
||||
<StackPanel Orientation="Horizontal" Height="16" Margin="2,0">
|
||||
<Border Background="{StaticResource Brush.Decorator}">
|
||||
<Path x:Name="Icon" Margin="4,0" Width="8" Height="8" Data="{StaticResource Icon.Branch}"/>
|
||||
</Border>
|
||||
|
||||
<Border x:Name="Color" Background="#FFFFB835">
|
||||
<TextBlock Text="{Binding Name}" FontSize="11" Margin="4,0" Foreground="Black"/>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<DataTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding Type}" Value="{x:Static models:DecoratorType.CurrentBranchHead}">
|
||||
<Setter TargetName="Icon" Property="Data" Value="{StaticResource Icon.Check}"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding Type}" Value="{x:Static models:DecoratorType.RemoteBranchHead}">
|
||||
<Setter TargetName="Icon" Property="Data" Value="{StaticResource Icon.Remote}"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding Type}" Value="{x:Static models:DecoratorType.Tag}">
|
||||
<Setter TargetName="Color" Property="Background" Value="#FF02C302"/>
|
||||
<Setter TargetName="Icon" Property="Data" Value="{StaticResource Icon.Tag}"/>
|
||||
</DataTrigger>
|
||||
</DataTemplate.Triggers>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<!-- Message -->
|
||||
<TextBlock
|
||||
Grid.Row="3" Grid.Column="0"
|
||||
Margin="0,4,0,0"
|
||||
HorizontalAlignment="Right" VerticalAlignment="Top"
|
||||
Text="{StaticResource Text.CommitViewer.Info.Message}"
|
||||
Foreground="{StaticResource Brush.FG2}"/>
|
||||
<controls:TextEdit
|
||||
Grid.Row="3" Grid.Column="1"
|
||||
x:Name="txtMessage"
|
||||
IsReadOnly="true"
|
||||
FontFamily="Consolas"
|
||||
BorderThickness="0"
|
||||
TextWrapping="Wrap"
|
||||
Margin="11,5,16,0"
|
||||
VerticalAlignment="Top"/>
|
||||
</Grid>
|
||||
|
||||
<!-- Line -->
|
||||
<Rectangle Grid.Row="3" Height="1" Margin="8" Fill="{StaticResource Brush.Border2}" VerticalAlignment="Center"/>
|
||||
|
||||
<!-- Change List -->
|
||||
<Grid Grid.Row="4">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="96"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="0,4,0,0"
|
||||
HorizontalAlignment="Right" VerticalAlignment="Top"
|
||||
Text="{StaticResource Text.CommitViewer.Info.Changed}"
|
||||
Foreground="{StaticResource Brush.FG2}"/>
|
||||
|
||||
<DataGrid
|
||||
Grid.Column="1"
|
||||
x:Name="changeList"
|
||||
RowHeight="24"
|
||||
Margin="11,0,0,2">
|
||||
<DataGrid.RowStyle>
|
||||
<Style TargetType="{x:Type DataGridRow}" BasedOn="{StaticResource Style.DataGridRow.TextPreview}">
|
||||
<EventSetter Event="ContextMenuOpening" Handler="OnChangeListContextMenuOpening"/>
|
||||
</Style>
|
||||
</DataGrid.RowStyle>
|
||||
<DataGrid.Columns>
|
||||
<DataGridTemplateColumn Width="22" IsReadOnly="True">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<controls:ChangeStatusIcon Width="14" Height="14" IsLocalChange="False" Change="{Binding}"/>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTemplateColumn Width="*" IsReadOnly="True">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock FontFamily="Consolas" Margin="2,0,0,0" Text="{Binding Path}"/>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
|
||||
<!-- Change Details -->
|
||||
<TabItem Header="{StaticResource Text.CommitViewer.Changes}">
|
||||
<widgets:CommitChanges x:Name="changeContainer"/>
|
||||
</TabItem>
|
||||
|
||||
<!-- Revision Files -->
|
||||
<TabItem Header="{StaticResource Text.CommitViewer.Files}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="200" MinWidth="200" MaxWidth="400"/>
|
||||
<ColumnDefinition Width="1"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Border Grid.Column="0" Background="{StaticResource Brush.Contents}" BorderBrush="{StaticResource Brush.Border2}" BorderThickness="1">
|
||||
<controls:Tree
|
||||
x:Name="treeFiles"
|
||||
FontFamily="Consolas"
|
||||
SelectionChanged="OnFilesSelectionChanged"
|
||||
ContextMenuOpening="OnFilesContextMenuOpening">
|
||||
<controls:Tree.ItemTemplate>
|
||||
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
|
||||
<StackPanel Orientation="Horizontal" Height="24">
|
||||
<Path x:Name="Icon" Width="14" Height="14" Data="{StaticResource Icon.File}"/>
|
||||
<TextBlock Margin="6,0,0,0" FontSize="11" Text="{Binding Path, Converter={StaticResource PureFileName}}"/>
|
||||
</StackPanel>
|
||||
|
||||
<HierarchicalDataTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding IsFolder}" Value="True">
|
||||
<Setter TargetName="Icon" Property="Fill" Value="Goldenrod"/>
|
||||
<Setter TargetName="Icon" Property="Opacity" Value="1"/>
|
||||
</DataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsFolder}" Value="True"/>
|
||||
<Condition Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type controls:TreeItem}}, Path=IsExpanded}" Value="False"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter TargetName="Icon" Property="Data" Value="{StaticResource Icon.Folder.Fill}"/>
|
||||
</MultiDataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsFolder}" Value="True"/>
|
||||
<Condition Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type controls:TreeItem}}, Path=IsExpanded}" Value="True"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter TargetName="Icon" Property="Data" Value="{StaticResource Icon.Folder.Open}"/>
|
||||
</MultiDataTrigger>
|
||||
</HierarchicalDataTemplate.Triggers>
|
||||
</HierarchicalDataTemplate>
|
||||
</controls:Tree.ItemTemplate>
|
||||
</controls:Tree>
|
||||
</Border>
|
||||
|
||||
<GridSplitter Grid.Column="1" Width="1" HorizontalAlignment="Center" VerticalAlignment="Stretch" Background="Transparent"/>
|
||||
|
||||
<Border Grid.Column="2" BorderBrush="{StaticResource Brush.Border2}" BorderThickness="1" Margin="2,0">
|
||||
<Grid>
|
||||
<Grid x:Name="layerTextPreview" Visibility="Collapsed" SizeChanged="OnTextPreviewSizeChanged">
|
||||
<DataGrid
|
||||
x:Name="txtPreviewData"
|
||||
FontFamily="Consolas"
|
||||
RowHeight="16"
|
||||
RowStyle="{StaticResource Style.DataGridRow.TextPreview}"
|
||||
FrozenColumnCount="1"
|
||||
ContextMenuOpening="OnTextPreviewContextMenuOpening"
|
||||
SelectionMode="Extended"
|
||||
SelectionUnit="FullRow">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Binding="{Binding Number}" ElementStyle="{StaticResource Style.TextBlock.LineNumber}"/>
|
||||
<DataGridTextColumn Binding="{Binding Data}" ElementStyle="{StaticResource Style.TextBlock.LineContent}"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
<Rectangle x:Name="txtPreviewSplitter" Width="1" Fill="{StaticResource Brush.Border2}" HorizontalAlignment="Left"/>
|
||||
</Grid>
|
||||
|
||||
<ScrollViewer
|
||||
x:Name="layerImagePreview"
|
||||
HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
|
||||
Visibility="Collapsed">
|
||||
<Image
|
||||
x:Name="imgPreviewData"
|
||||
Width="Auto" Height="Auto"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</ScrollViewer>
|
||||
|
||||
<StackPanel
|
||||
x:Name="layerRevisionPreview"
|
||||
Orientation="Vertical"
|
||||
VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||
Visibility="Collapsed">
|
||||
<Path x:Name="iconRevisionPreview" Width="64" Height="64" Data="{StaticResource Icon.Submodule}" Fill="{StaticResource Brush.FG2}"/>
|
||||
<TextBlock x:Name="txtRevisionPreview" Margin="0,16,0,0" FontFamily="Consolas" FontSize="18" FontWeight="UltraBold" HorizontalAlignment="Center" Foreground="{StaticResource Brush.FG2}"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel
|
||||
x:Name="layerBinaryPreview"
|
||||
Orientation="Vertical"
|
||||
VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||
Visibility="Collapsed">
|
||||
<Path Width="64" Height="64" Data="{StaticResource Icon.Error}" Fill="{StaticResource Brush.FG2}"/>
|
||||
<TextBlock Margin="0,16,0,0" Text="{StaticResource Text.BinaryNotSupported}" FontFamily="Consolas" FontSize="18" FontWeight="UltraBold" HorizontalAlignment="Center" Foreground="{StaticResource Brush.FG2}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</UserControl>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue