optimize<*>: remove deprecated apis (older than .NET 6)

This commit is contained in:
leo 2022-05-20 16:00:25 +08:00
parent 890ff29ac7
commit 338f91357e
8 changed files with 677 additions and 749 deletions

View file

@ -1,186 +1,178 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace SourceGit.Commands { namespace SourceGit.Commands {
/// <summary> /// <summary>
/// 用于取消命令执行的上下文对象 /// 用于取消命令执行的上下文对象
/// </summary> /// </summary>
public class Context { public class Context {
public bool IsCancelRequested { get; set; } = false; public bool IsCancelRequested { get; set; } = false;
} }
/// <summary> /// <summary>
/// 命令接口 /// 命令接口
/// </summary> /// </summary>
public class Command { public class Command {
/// <summary> /// <summary>
/// 读取全部输出时的结果 /// 读取全部输出时的结果
/// </summary> /// </summary>
public class ReadToEndResult { public class ReadToEndResult {
public bool IsSuccess { get; set; } public bool IsSuccess { get; set; }
public string Output { get; set; } public string Output { get; set; }
public string Error { get; set; } public string Error { get; set; }
} }
/// <summary> /// <summary>
/// 上下文 /// 上下文
/// </summary> /// </summary>
public Context Ctx { get; set; } = null; public Context Ctx { get; set; } = null;
/// <summary> /// <summary>
/// 运行路径 /// 运行路径
/// </summary> /// </summary>
public string Cwd { get; set; } = ""; public string Cwd { get; set; } = "";
/// <summary> /// <summary>
/// 参数 /// 参数
/// </summary> /// </summary>
public string Args { get; set; } = ""; public string Args { get; set; } = "";
/// <summary> /// <summary>
/// 是否忽略错误 /// 是否忽略错误
/// </summary> /// </summary>
public bool DontRaiseError { get; set; } = false; public bool DontRaiseError { get; set; } = false;
/// <summary> /// <summary>
/// 使用标准错误输出 /// 使用标准错误输出
/// </summary> /// </summary>
public bool TraitErrorAsOutput { get; set; } = false; public bool TraitErrorAsOutput { get; set; } = false;
/// <summary> /// <summary>
/// 用于设置该进程独有的环境变量 /// 用于设置该进程独有的环境变量
/// </summary> /// </summary>
public Dictionary<string, string> Envs { get; set; } = new Dictionary<string, string>(); public Dictionary<string, string> Envs { get; set; } = new Dictionary<string, string>();
/// <summary> /// <summary>
/// 运行 /// 运行
/// </summary> /// </summary>
public bool Exec() { public bool Exec() {
var start = new ProcessStartInfo(); var start = new ProcessStartInfo();
start.FileName = Models.Preference.Instance.Git.Path; start.FileName = Models.Preference.Instance.Git.Path;
start.Arguments = "--no-pager -c core.quotepath=off " + Args; start.Arguments = "--no-pager -c core.quotepath=off " + Args;
start.UseShellExecute = false; start.UseShellExecute = false;
start.CreateNoWindow = true; start.CreateNoWindow = true;
start.RedirectStandardOutput = true; start.RedirectStandardOutput = true;
start.RedirectStandardError = true; start.RedirectStandardError = true;
start.StandardOutputEncoding = Encoding.UTF8; start.StandardOutputEncoding = Encoding.UTF8;
start.StandardErrorEncoding = Encoding.UTF8; start.StandardErrorEncoding = Encoding.UTF8;
if (!string.IsNullOrEmpty(Cwd)) start.WorkingDirectory = Cwd; if (!string.IsNullOrEmpty(Cwd)) start.WorkingDirectory = Cwd;
foreach (var kv in Envs) start.EnvironmentVariables[kv.Key] = kv.Value; foreach (var kv in Envs) start.EnvironmentVariables[kv.Key] = kv.Value;
var progressFilter = new Regex(@"\s\d+%\s"); var progressFilter = new Regex(@"\s\d+%\s");
var errs = new List<string>(); var errs = new List<string>();
var proc = new Process() { StartInfo = start }; var proc = new Process() { StartInfo = start };
var isCancelled = false; var isCancelled = false;
proc.OutputDataReceived += (o, e) => { proc.OutputDataReceived += (o, e) => {
if (Ctx != null && Ctx.IsCancelRequested) { if (Ctx != null && Ctx.IsCancelRequested) {
isCancelled = true; isCancelled = true;
proc.CancelErrorRead(); proc.CancelErrorRead();
proc.CancelOutputRead(); proc.CancelOutputRead();
#if NET48 if (!proc.HasExited) proc.Kill(true);
if (!proc.HasExited) proc.Kill(); return;
#else }
if (!proc.HasExited) proc.Kill(true);
#endif if (e.Data == null) return;
return; OnReadline(e.Data);
} };
proc.ErrorDataReceived += (o, e) => {
if (e.Data == null) return; if (Ctx != null && Ctx.IsCancelRequested) {
OnReadline(e.Data); isCancelled = true;
}; proc.CancelErrorRead();
proc.ErrorDataReceived += (o, e) => { proc.CancelOutputRead();
if (Ctx != null && Ctx.IsCancelRequested) { if (!proc.HasExited) proc.Kill(true);
isCancelled = true; return;
proc.CancelErrorRead(); }
proc.CancelOutputRead();
#if NET48 if (string.IsNullOrEmpty(e.Data)) return;
if (!proc.HasExited) proc.Kill(); if (TraitErrorAsOutput) OnReadline(e.Data);
#else
if (!proc.HasExited) proc.Kill(true); if (progressFilter.IsMatch(e.Data)) return;
#endif if (e.Data.StartsWith("remote: Counting objects:", StringComparison.Ordinal)) return;
return; errs.Add(e.Data);
} };
if (string.IsNullOrEmpty(e.Data)) return; try {
if (TraitErrorAsOutput) OnReadline(e.Data); proc.Start();
} catch (Exception e) {
if (progressFilter.IsMatch(e.Data)) return; if (!DontRaiseError) Models.Exception.Raise(e.Message);
if (e.Data.StartsWith("remote: Counting objects:", StringComparison.Ordinal)) return; return false;
errs.Add(e.Data); }
};
proc.BeginOutputReadLine();
try { proc.BeginErrorReadLine();
proc.Start(); proc.WaitForExit();
} catch (Exception e) {
if (!DontRaiseError) Models.Exception.Raise(e.Message); int exitCode = proc.ExitCode;
return false; proc.Close();
}
if (!isCancelled && exitCode != 0 && errs.Count > 0) {
proc.BeginOutputReadLine(); if (!DontRaiseError) Models.Exception.Raise(string.Join("\n", errs));
proc.BeginErrorReadLine(); return false;
proc.WaitForExit(); } else {
return true;
int exitCode = proc.ExitCode; }
proc.Close(); }
if (!isCancelled && exitCode != 0 && errs.Count > 0) { /// <summary>
if (!DontRaiseError) Models.Exception.Raise(string.Join("\n", errs)); /// 直接读取全部标准输出
return false; /// </summary>
} else { public ReadToEndResult ReadToEnd() {
return true; var start = new ProcessStartInfo();
} start.FileName = Models.Preference.Instance.Git.Path;
} start.Arguments = "--no-pager -c core.quotepath=off " + Args;
start.UseShellExecute = false;
/// <summary> start.CreateNoWindow = true;
/// 直接读取全部标准输出 start.RedirectStandardOutput = true;
/// </summary> start.RedirectStandardError = true;
public ReadToEndResult ReadToEnd() { start.StandardOutputEncoding = Encoding.UTF8;
var start = new ProcessStartInfo(); start.StandardErrorEncoding = Encoding.UTF8;
start.FileName = Models.Preference.Instance.Git.Path;
start.Arguments = "--no-pager -c core.quotepath=off " + Args; if (!string.IsNullOrEmpty(Cwd)) start.WorkingDirectory = Cwd;
start.UseShellExecute = false;
start.CreateNoWindow = true; var proc = new Process() { StartInfo = start };
start.RedirectStandardOutput = true; try {
start.RedirectStandardError = true; proc.Start();
start.StandardOutputEncoding = Encoding.UTF8; } catch (Exception e) {
start.StandardErrorEncoding = Encoding.UTF8; return new ReadToEndResult() {
Output = "",
if (!string.IsNullOrEmpty(Cwd)) start.WorkingDirectory = Cwd; Error = e.Message,
IsSuccess = false,
var proc = new Process() { StartInfo = start }; };
try { }
proc.Start();
} catch (Exception e) { var rs = new ReadToEndResult();
return new ReadToEndResult() { rs.Output = proc.StandardOutput.ReadToEnd();
Output = "", rs.Error = proc.StandardError.ReadToEnd();
Error = e.Message,
IsSuccess = false, proc.WaitForExit();
}; rs.IsSuccess = proc.ExitCode == 0;
} proc.Close();
var rs = new ReadToEndResult(); return rs;
rs.Output = proc.StandardOutput.ReadToEnd(); }
rs.Error = proc.StandardError.ReadToEnd();
/// <summary>
proc.WaitForExit(); /// 调用Exec时的读取函数
rs.IsSuccess = proc.ExitCode == 0; /// </summary>
proc.Close(); /// <param name="line"></param>
public virtual void OnReadline(string line) { }
return rs; }
} }
/// <summary>
/// 调用Exec时的读取函数
/// </summary>
/// <param name="line"></param>
public virtual void OnReadline(string line) { }
}
}

View file

@ -1,80 +1,69 @@
using System; using System;
using System.Reflection; using System.Reflection;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Net.Http;
#if NET6_0
using System.Net.Http; namespace SourceGit.Models {
#else
using System.Net; /// <summary>
using System.Text; /// Github开放API中Release信息格式
#endif /// </summary>
public class Version {
namespace SourceGit.Models { [JsonPropertyName("id")]
public ulong Id { get; set; }
/// <summary> [JsonPropertyName("tag_name")]
/// Github开放API中Release信息格式 public string TagName { get; set; }
/// </summary> [JsonPropertyName("target_commitish")]
public class Version { public string CommitSHA { get; set; }
[JsonPropertyName("id")] [JsonPropertyName("prerelease")]
public ulong Id { get; set; } public bool PreRelease { get; set; }
[JsonPropertyName("tag_name")] [JsonPropertyName("name")]
public string TagName { get; set; } public string Name { get; set; }
[JsonPropertyName("target_commitish")] [JsonPropertyName("body")]
public string CommitSHA { get; set; } public string Body { get; set; }
[JsonPropertyName("prerelease")] [JsonPropertyName("created_at")]
public bool PreRelease { get; set; } public DateTime CreatedAt { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; } public string PublishTime {
[JsonPropertyName("body")] get { return CreatedAt.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss"); }
public string Body { get; set; } }
[JsonPropertyName("created_at")]
public DateTime CreatedAt { get; set; } public string IsPrerelease {
get { return PreRelease ? "YES" : "NO"; }
public string PublishTime { }
get { return CreatedAt.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss"); }
} public static void Check(Action<Version> onUpgradable) {
if (!Preference.Instance.General.CheckForUpdate) return;
public string IsPrerelease {
get { return PreRelease ? "YES" : "NO"; } var curDayOfYear = DateTime.Now.DayOfYear;
} var lastDayOfYear = Preference.Instance.General.LastCheckDay;
if (lastDayOfYear != curDayOfYear) {
public static void Check(Action<Version> onUpgradable) { Preference.Instance.General.LastCheckDay = curDayOfYear;
if (!Preference.Instance.General.CheckForUpdate) return; Task.Run(async () => {
try {
var curDayOfYear = DateTime.Now.DayOfYear; var req = new HttpClient();
var lastDayOfYear = Preference.Instance.General.LastCheckDay; var rsp = await req.GetAsync("https://api.github.com/repos/sourcegit-scm/sourcegit/releases/latest");
if (lastDayOfYear != curDayOfYear) { rsp.EnsureSuccessStatusCode();
Preference.Instance.General.LastCheckDay = curDayOfYear;
Task.Run(async () => { var raw = await rsp.Content.ReadAsStringAsync();
try { var ver = JsonSerializer.Deserialize<Version>(raw);
#if NET6_0 var cur = Assembly.GetExecutingAssembly().GetName().Version;
var req = new HttpClient();
var rsp = await req.GetAsync("https://api.github.com/repos/sourcegit-scm/sourcegit/releases/latest"); var matches = Regex.Match(ver.TagName, @"^v(\d+)\.(\d+).*");
rsp.EnsureSuccessStatusCode(); if (!matches.Success) return;
var raw = await rsp.Content.ReadAsStringAsync(); var major = int.Parse(matches.Groups[1].Value);
#else var minor = int.Parse(matches.Groups[2].Value);
var web = new WebClient() { Encoding = Encoding.UTF8 }; if (major > cur.Major || (major == cur.Major && minor > cur.Minor)) {
var raw = await web.DownloadStringTaskAsync("https://api.github.com/repos/sourcegit-scm/sourcegit/releases/latest"); onUpgradable?.Invoke(ver);
#endif }
var ver = JsonSerializer.Deserialize<Version>(raw); } catch {
var cur = Assembly.GetExecutingAssembly().GetName().Version; }
});
var matches = Regex.Match(ver.TagName, @"^v(\d+)\.(\d+).*"); }
if (!matches.Success) return; }
}
var major = int.Parse(matches.Groups[1].Value); }
var minor = int.Parse(matches.Groups[2].Value);
if (major > cur.Major || (major == cur.Major && minor > cur.Minor)) {
onUpgradable?.Invoke(ver);
}
} catch {
}
});
}
}
}
}

View file

@ -1,124 +1,124 @@
<controls:Window <controls:Window
x:Class="SourceGit.Views.About" x:Class="SourceGit.Views.About"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:SourceGit.Views.Controls" xmlns:controls="clr-namespace:SourceGit.Views.Controls"
mc:Ignorable="d" mc:Ignorable="d"
WindowStartupLocation="CenterOwner" WindowStartupLocation="CenterOwner"
Title="{DynamicResource Text.About}" Title="{DynamicResource Text.About}"
Width="420" SizeToContent="Height" Width="420" SizeToContent="Height"
ResizeMode="NoResize"> ResizeMode="NoResize">
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="28"/> <RowDefinition Height="28"/>
<RowDefinition Height="1"/> <RowDefinition Height="1"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<!-- Title bar --> <!-- Title bar -->
<Grid Grid.Row="0" Background="{DynamicResource Brush.TitleBar}"> <Grid Grid.Row="0" Background="{DynamicResource Brush.TitleBar}">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<!-- Icon --> <!-- Icon -->
<Path Grid.Column="0" Margin="6,0" Width="16" Height="16" Data="{StaticResource Icon.Help}"/> <Path Grid.Column="0" Margin="6,0" Width="16" Height="16" Data="{StaticResource Icon.Help}"/>
<!-- Title --> <!-- Title -->
<TextBlock Grid.Column="1" Text="{DynamicResource Text.About}"/> <TextBlock Grid.Column="1" Text="{DynamicResource Text.About}"/>
<!-- Close --> <!-- Close -->
<controls:IconButton <controls:IconButton
Grid.Column="3" Grid.Column="3"
Click="Quit" Click="Quit"
Width="28" Width="28"
Padding="8" Padding="8"
Icon="{StaticResource Icon.Close}" Icon="{StaticResource Icon.Close}"
HoverBackground="Red" HoverBackground="Red"
WindowChrome.IsHitTestVisibleInChrome="True"/> WindowChrome.IsHitTestVisibleInChrome="True"/>
</Grid> </Grid>
<Rectangle <Rectangle
Grid.Row="1" Grid.Row="1"
Height="1" Height="1"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Fill="{DynamicResource Brush.Border0}"/> Fill="{DynamicResource Brush.Border0}"/>
<!-- Content --> <!-- Content -->
<StackPanel Grid.Row="2" Orientation="Vertical"> <StackPanel Grid.Row="2" Orientation="Vertical">
<!-- LOGO --> <!-- LOGO -->
<Path <Path
Margin="0,16,0,0" Margin="0,16,0,0"
Width="64" Height="64" Width="64" Height="64"
Data="{StaticResource Icon.Git}" Data="{StaticResource Icon.Git}"
Fill="{DynamicResource Brush.Logo}"/> Fill="{DynamicResource Brush.Logo}"/>
<!-- Title --> <!-- Title -->
<TextBlock <TextBlock
Margin="0,24,0,8" Margin="0,24,0,8"
Text="{DynamicResource Text.About.Title}" Text="{DynamicResource Text.About.Title}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
FontSize="18"/> FontSize="18"/>
<!-- Version --> <!-- Version -->
<TextBlock <TextBlock
x:Name="version" x:Name="version"
Margin="0,0,0,8" Margin="0,0,0,8"
HorizontalAlignment="Center" HorizontalAlignment="Center"
FontSize="11" FontSize="11"
Text="VERSION: v1.0 .NET: v5.0"/> Text="VERSION: v1.0"/>
<DataGrid <DataGrid
x:Name="hotkeys" x:Name="hotkeys"
Grid.Row="2" Grid.Row="2"
Margin="16,8" Margin="16,8"
Background="{DynamicResource Brush.Contents}" Background="{DynamicResource Brush.Contents}"
GridLinesVisibility="All" GridLinesVisibility="All"
HorizontalGridLinesBrush="{DynamicResource Brush.Border0}" HorizontalGridLinesBrush="{DynamicResource Brush.Border0}"
VerticalGridLinesBrush="{DynamicResource Brush.Border0}" VerticalGridLinesBrush="{DynamicResource Brush.Border0}"
HeadersVisibility="Column" HeadersVisibility="Column"
RowHeight="24" RowHeight="24"
ColumnHeaderHeight="24" ColumnHeaderHeight="24"
IsHitTestVisible="False" IsHitTestVisible="False"
BorderThickness="1,1,0,0" BorderThickness="1,1,0,0"
BorderBrush="{DynamicResource Brush.Border0}"> BorderBrush="{DynamicResource Brush.Border0}">
<DataGrid.ColumnHeaderStyle> <DataGrid.ColumnHeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}"> <Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Template"> <Setter Property="Template">
<Setter.Value> <Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeader}"> <ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
<Border BorderThickness="0,0,1,1" BorderBrush="{DynamicResource Brush.Border0}" Background="{DynamicResource Brush.Window}"> <Border BorderThickness="0,0,1,1" BorderBrush="{DynamicResource Brush.Border0}" Background="{DynamicResource Brush.Window}">
<TextBlock <TextBlock
Text="{TemplateBinding Content}" Text="{TemplateBinding Content}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Center" VerticalAlignment="Center"
FontWeight="DemiBold"/> FontWeight="DemiBold"/>
</Border> </Border>
</ControlTemplate> </ControlTemplate>
</Setter.Value> </Setter.Value>
</Setter> </Setter>
</Style> </Style>
</DataGrid.ColumnHeaderStyle> </DataGrid.ColumnHeaderStyle>
<DataGrid.Columns> <DataGrid.Columns>
<DataGridTextColumn Width="100" Header="{DynamicResource Text.Hotkeys.Col.Key}" Binding="{Binding .Key}" ElementStyle="{StaticResource Style.TextBlock.LineContent}"/> <DataGridTextColumn Width="100" Header="{DynamicResource Text.Hotkeys.Col.Key}" Binding="{Binding .Key}" ElementStyle="{StaticResource Style.TextBlock.LineContent}"/>
<DataGridTextColumn Width="*" Header="{DynamicResource Text.Hotkeys.Col.Desc}" Binding="{Binding .Desc}" ElementStyle="{StaticResource Style.TextBlock.LineContent}"/> <DataGridTextColumn Width="*" Header="{DynamicResource Text.Hotkeys.Col.Desc}" Binding="{Binding .Desc}" ElementStyle="{StaticResource Style.TextBlock.LineContent}"/>
</DataGrid.Columns> </DataGrid.Columns>
</DataGrid> </DataGrid>
<!-- Official site --> <!-- Official site -->
<TextBlock HorizontalAlignment="Center" Margin="0,4,0,16"> <TextBlock HorizontalAlignment="Center" Margin="0,4,0,16">
<TextBlock Text="© 2021" Margin="0" Padding="0"/> <TextBlock Text="© 2021" Margin="0" Padding="0"/>
<Hyperlink NavigateUri="https://github.com/sourcegit-scm/sourcegit.git" RequestNavigate="OnRequestNavigate" ToolTip="https://github.com/sourcegit-scm/sourcegit.git"> <Hyperlink NavigateUri="https://github.com/sourcegit-scm/sourcegit.git" RequestNavigate="OnRequestNavigate" ToolTip="https://github.com/sourcegit-scm/sourcegit.git">
<Run Text="SourceGit."/> <Run Text="SourceGit."/>
</Hyperlink> </Hyperlink>
<TextBlock Text="All rights reserved." Margin="0" Padding="0"/> <TextBlock Text="All rights reserved." Margin="0" Padding="0"/>
</TextBlock> </TextBlock>
</StackPanel> </StackPanel>
</Grid> </Grid>
</controls:Window> </controls:Window>

View file

@ -1,55 +1,48 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Reflection; using System.Reflection;
using System.Windows; using System.Windows;
namespace SourceGit.Views { namespace SourceGit.Views {
/// <summary> /// <summary>
/// 关于对话框 /// 关于对话框
/// </summary> /// </summary>
public partial class About : Controls.Window { public partial class About : Controls.Window {
public class Keymap { public class Keymap {
public string Key { get; set; } public string Key { get; set; }
public string Desc { get; set; } public string Desc { get; set; }
public Keymap(string k, string d) { Key = k; Desc = App.Text($"Hotkeys.{d}"); } public Keymap(string k, string d) { Key = k; Desc = App.Text($"Hotkeys.{d}"); }
} }
public About() { public About() {
InitializeComponent(); InitializeComponent();
var asm = Assembly.GetExecutingAssembly().GetName(); var asm = Assembly.GetExecutingAssembly().GetName();
var framework = AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName; version.Text = $"VERSION : v{asm.Version.Major}.{asm.Version.Minor}";
var dotnetVer = framework.Substring(framework.IndexOf("=") + 1);
hotkeys.ItemsSource = new List<Keymap>() {
version.Text = $"VERSION : v{asm.Version.Major}.{asm.Version.Minor} .NET : {dotnetVer}"; new Keymap("CTRL + T", "NewTab"),
new Keymap("CTRL + W", "CloseTab"),
hotkeys.ItemsSource = new List<Keymap>() { new Keymap("CTRL + TAB", "NextTab"),
new Keymap("CTRL + T", "NewTab"), new Keymap("CTRL + [1-9]", "SwitchTo"),
new Keymap("CTRL + W", "CloseTab"), new Keymap("CTRL + F", "Search"),
new Keymap("CTRL + TAB", "NextTab"), new Keymap("F5", "Refresh"),
new Keymap("CTRL + [1-9]", "SwitchTo"), new Keymap("SPACE", "ToggleStage"),
new Keymap("CTRL + F", "Search"), new Keymap("ESC", "CancelPopup"),
new Keymap("F5", "Refresh"), };
new Keymap("SPACE", "ToggleStage"), }
new Keymap("ESC", "CancelPopup"),
}; private void OnRequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e) {
} var info = new ProcessStartInfo("cmd", $"/c start {e.Uri.AbsoluteUri}");
info.CreateNoWindow = true;
private void OnRequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e) { Process.Start(info);
#if NET48 }
Process.Start(e.Uri.AbsoluteUri);
#else private void Quit(object sender, RoutedEventArgs e) {
var info = new ProcessStartInfo("cmd", $"/c start {e.Uri.AbsoluteUri}"); Close();
info.CreateNoWindow = true; }
Process.Start(info); }
#endif }
}
private void Quit(object sender, RoutedEventArgs e) {
Close();
}
}
}

View file

@ -1,258 +1,230 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using System.Net.Http;
#if NET6_0
using System.Net.Http; namespace SourceGit.Views.Controls {
#endif
/// <summary>
namespace SourceGit.Views.Controls { /// 头像控件
/// </summary>
/// <summary> public class Avatar : Image {
/// 头像控件
/// </summary> /// <summary>
public class Avatar : Image { /// 显示FallbackLabel时的背景色
/// </summary>
/// <summary> private static readonly Brush[] BACKGROUND_BRUSHES = new Brush[] {
/// 显示FallbackLabel时的背景色 new LinearGradientBrush(Colors.Orange, Color.FromRgb(255, 213, 134), 90),
/// </summary> new LinearGradientBrush(Colors.DodgerBlue, Colors.LightSkyBlue, 90),
private static readonly Brush[] BACKGROUND_BRUSHES = new Brush[] { new LinearGradientBrush(Colors.LimeGreen, Color.FromRgb(124, 241, 124), 90),
new LinearGradientBrush(Colors.Orange, Color.FromRgb(255, 213, 134), 90), new LinearGradientBrush(Colors.Orchid, Color.FromRgb(248, 161, 245), 90),
new LinearGradientBrush(Colors.DodgerBlue, Colors.LightSkyBlue, 90), new LinearGradientBrush(Colors.Tomato, Color.FromRgb(252, 165, 150), 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>
/// <summary> public static readonly string CACHE_PATH = Path.Combine(
/// 头像资源本地缓存路径 Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
/// </summary> "SourceGit",
public static readonly string CACHE_PATH = Path.Combine( "avatars");
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"SourceGit", /// <summary>
"avatars"); /// 邮件属性定义
/// </summary>
/// <summary> public static readonly DependencyProperty EmailProperty = DependencyProperty.Register(
/// 邮件属性定义 "Email",
/// </summary> typeof(string),
public static readonly DependencyProperty EmailProperty = DependencyProperty.Register( typeof(Avatar),
"Email", new PropertyMetadata(null, OnEmailChanged));
typeof(string),
typeof(Avatar), /// <summary>
new PropertyMetadata(null, OnEmailChanged)); /// 邮件属性
/// </summary>
/// <summary> public string Email {
/// 邮件属性 get { return (string)GetValue(EmailProperty); }
/// </summary> set { SetValue(EmailProperty, value); }
public string Email { }
get { return (string)GetValue(EmailProperty); }
set { SetValue(EmailProperty, value); } /// <summary>
} /// 下载头像失败时显示的Label属性定义
/// </summary>
/// <summary> public static readonly DependencyProperty FallbackLabelProperty = DependencyProperty.Register(
/// 下载头像失败时显示的Label属性定义 "FallbackLabel",
/// </summary> typeof(string),
public static readonly DependencyProperty FallbackLabelProperty = DependencyProperty.Register( typeof(Avatar),
"FallbackLabel", new PropertyMetadata("?", OnFallbackLabelChanged));
typeof(string),
typeof(Avatar), /// <summary>
new PropertyMetadata("?", OnFallbackLabelChanged)); /// 下载头像失败时显示的Label属性
/// </summary>
/// <summary> public string FallbackLabel {
/// 下载头像失败时显示的Label属性 get { return (string)GetValue(FallbackLabelProperty); }
/// </summary> set { SetValue(FallbackLabelProperty, value); }
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;
private static Dictionary<string, List<Avatar>> requesting = new Dictionary<string, List<Avatar>>();
private static Dictionary<string, BitmapImage> loaded = new Dictionary<string, BitmapImage>(); private int colorIdx = 0;
private static Task loader = null; private FormattedText label = null;
private int colorIdx = 0; public Avatar() {
private FormattedText label = null; SetValue(RenderOptions.BitmapScalingModeProperty, BitmapScalingMode.HighQuality);
SetValue(RenderOptions.ClearTypeHintProperty, ClearTypeHint.Auto);
public Avatar() { Unloaded += (o, e) => Cancel(Email);
SetValue(RenderOptions.BitmapScalingModeProperty, BitmapScalingMode.HighQuality); }
SetValue(RenderOptions.ClearTypeHintProperty, ClearTypeHint.Auto);
Unloaded += (o, e) => Cancel(Email); /// <summary>
} /// 取消一个下载任务
/// </summary>
/// <summary> /// <param name="email"></param>
/// 取消一个下载任务 private void Cancel(string email) {
/// </summary> if (!string.IsNullOrEmpty(email) && requesting.ContainsKey(email)) {
/// <param name="email"></param> if (requesting[email].Count <= 1) {
private void Cancel(string email) { requesting.Remove(email);
if (!string.IsNullOrEmpty(email) && requesting.ContainsKey(email)) { } else {
if (requesting[email].Count <= 1) { requesting[email].Remove(this);
requesting.Remove(email); }
} else { }
requesting[email].Remove(this); }
}
} /// <summary>
} /// 渲染实现
/// </summary>
/// <summary> /// <param name="dc"></param>
/// 渲染实现 protected override void OnRender(DrawingContext dc) {
/// </summary> base.OnRender(dc);
/// <param name="dc"></param>
protected override void OnRender(DrawingContext dc) { if (Source == null && label != null) {
base.OnRender(dc); var corner = Math.Max(2, Width / 16);
var offsetX = (double)0;
if (Source == null && label != null) { if (HorizontalAlignment == HorizontalAlignment.Right) {
var corner = Math.Max(2, Width / 16); offsetX = -Width * 0.5;
var offsetX = (double)0; } else if (HorizontalAlignment == HorizontalAlignment.Left) {
if (HorizontalAlignment == HorizontalAlignment.Right) { offsetX = Width * 0.5;
offsetX = -Width * 0.5; }
} else if (HorizontalAlignment == HorizontalAlignment.Left) {
offsetX = Width * 0.5; Brush brush = BACKGROUND_BRUSHES[colorIdx];
} dc.DrawRoundedRectangle(brush, null, new Rect(-Width * 0.5 + offsetX, -Height * 0.5, Width, Height), corner, corner);
dc.DrawText(label, new Point(label.Width * -0.5 + offsetX, label.Height * -0.5));
Brush brush = BACKGROUND_BRUSHES[colorIdx]; }
dc.DrawRoundedRectangle(brush, null, new Rect(-Width * 0.5 + offsetX, -Height * 0.5, Width, Height), corner, corner); }
dc.DrawText(label, new Point(label.Width * -0.5 + offsetX, label.Height * -0.5));
} /// <summary>
} /// 显示文本变化时触发
/// </summary>
/// <summary> /// <param name="d"></param>
/// 显示文本变化时触发 /// <param name="e"></param>
/// </summary> private static void OnFallbackLabelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
/// <param name="d"></param> Avatar a = d as Avatar;
/// <param name="e"></param> if (a == null) return;
private static void OnFallbackLabelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
Avatar a = d as Avatar; var placeholder = a.FallbackLabel.Length > 0 ? a.FallbackLabel.Substring(0, 1) : "?";
if (a == null) return;
a.colorIdx = 0;
var placeholder = a.FallbackLabel.Length > 0 ? a.FallbackLabel.Substring(0, 1) : "?"; a.label = new FormattedText(
placeholder,
a.colorIdx = 0; CultureInfo.CurrentCulture,
a.label = new FormattedText( FlowDirection.LeftToRight,
placeholder, new Typeface(new FontFamily(Models.Preference.Instance.General.FontFamilyWindow), FontStyles.Normal, FontWeights.Normal, FontStretches.Normal),
CultureInfo.CurrentCulture, a.Width * 0.65,
FlowDirection.LeftToRight, Brushes.White,
new Typeface(new FontFamily(Models.Preference.Instance.General.FontFamilyWindow), FontStyles.Normal, FontWeights.Normal, FontStretches.Normal), VisualTreeHelper.GetDpi(a).PixelsPerDip);
a.Width * 0.65,
Brushes.White, var chars = placeholder.ToCharArray();
VisualTreeHelper.GetDpi(a).PixelsPerDip); foreach (var ch in chars) a.colorIdx += Math.Abs(ch);
a.colorIdx = a.colorIdx % BACKGROUND_BRUSHES.Length;
var chars = placeholder.ToCharArray(); }
foreach (var ch in chars) a.colorIdx += Math.Abs(ch);
a.colorIdx = a.colorIdx % BACKGROUND_BRUSHES.Length; /// <summary>
} /// 邮件变化时触发
/// </summary>
/// <summary> /// <param name="d"></param>
/// 邮件变化时触发 /// <param name="e"></param>
/// </summary> private static void OnEmailChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
/// <param name="d"></param> Avatar a = d as Avatar;
/// <param name="e"></param> if (a == null) return;
private static void OnEmailChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
Avatar a = d as Avatar; a.Cancel(e.OldValue as string);
if (a == null) return; a.Source = null;
a.InvalidateVisual();
a.Cancel(e.OldValue as string);
a.Source = null; var email = e.NewValue as string;
a.InvalidateVisual(); if (string.IsNullOrEmpty(email)) return;
var email = e.NewValue as string; if (loaded.ContainsKey(email)) {
if (string.IsNullOrEmpty(email)) return; a.Source = loaded[email];
return;
if (loaded.ContainsKey(email)) { }
a.Source = loaded[email];
return; if (requesting.ContainsKey(email)) {
} requesting[email].Add(a);
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");
byte[] hash = MD5.Create().ComputeHash(Encoding.Default.GetBytes(email.ToLower().Trim())); md5 = md5.ToLower();
string md5 = "";
for (int i = 0; i < hash.Length; i++) md5 += hash[i].ToString("x2"); string filePath = Path.Combine(CACHE_PATH, md5);
md5 = md5.ToLower(); if (File.Exists(filePath)) {
var img = new BitmapImage(new Uri(filePath));
string filePath = Path.Combine(CACHE_PATH, md5); loaded.Add(email, img);
if (File.Exists(filePath)) { a.Source = img;
var img = new BitmapImage(new Uri(filePath)); return;
loaded.Add(email, img); }
a.Source = img;
return; requesting.Add(email, new List<Avatar>());
} requesting[email].Add(a);
requesting.Add(email, new List<Avatar>()); Action job = () => {
requesting[email].Add(a); if (!requesting.ContainsKey(email)) return;
Action job = () => { try {
if (!requesting.ContainsKey(email)) return; var req = new HttpClient().GetAsync(Models.Preference.Instance.General.AvatarServer + md5 + "?d=404");
req.Wait();
try {
#if NET6_0 var rsp = req.Result;
var req = new HttpClient().GetAsync(Models.Preference.Instance.General.AvatarServer + md5 + "?d=404"); if (rsp != null && rsp.StatusCode == HttpStatusCode.OK) {
req.Wait(); var writer = File.OpenWrite(filePath);
rsp.Content.CopyToAsync(writer).Wait();
var rsp = req.Result; writer.Close();
if (rsp != null && rsp.StatusCode == HttpStatusCode.OK) {
var writer = File.OpenWrite(filePath); a.Dispatcher.Invoke(() => {
rsp.Content.CopyToAsync(writer).Wait(); var img = new BitmapImage(new Uri(filePath));
writer.Close(); loaded.Add(email, img);
a.Dispatcher.Invoke(() => { if (requesting.ContainsKey(email)) {
var img = new BitmapImage(new Uri(filePath)); foreach (var one in requesting[email]) one.Source = img;
loaded.Add(email, img); }
});
if (requesting.ContainsKey(email)) { } else {
foreach (var one in requesting[email]) one.Source = img; if (!loaded.ContainsKey(email)) loaded.Add(email, null);
} }
}); } catch {
} else { if (!loaded.ContainsKey(email)) loaded.Add(email, null);
if (!loaded.ContainsKey(email)) loaded.Add(email, null); }
}
#else requesting.Remove(email);
HttpWebRequest req = WebRequest.CreateHttp(Models.Preference.Instance.General.AvatarServer + md5 + "?d=404"); };
req.Timeout = 2000;
req.Method = "GET"; if (loader != null && !loader.IsCompleted) {
loader = loader.ContinueWith(t => { job(); });
HttpWebResponse rsp = req.GetResponse() as HttpWebResponse; } else {
if (rsp.StatusCode == HttpStatusCode.OK) { loader = Task.Run(job);
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);
}
#endif
} 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);
}
}
}
}

View file

@ -1,32 +1,28 @@
using System.Diagnostics; using System.Diagnostics;
using System.Windows; using System.Windows;
namespace SourceGit.Views { namespace SourceGit.Views {
/// <summary> /// <summary>
/// 新版本提示窗口 /// 新版本提示窗口
/// </summary> /// </summary>
public partial class Upgrade : Controls.Window { public partial class Upgrade : Controls.Window {
public Models.Version Version { get; set; } = new Models.Version(); public Models.Version Version { get; set; } = new Models.Version();
public Upgrade(Models.Version ver) { public Upgrade(Models.Version ver) {
Version = ver; Version = ver;
InitializeComponent(); InitializeComponent();
txtRelease.Text = App.Text("UpdateAvailable.Title", ver.Name); txtRelease.Text = App.Text("UpdateAvailable.Title", ver.Name);
} }
private void Download(object sender, RoutedEventArgs e) { private void Download(object sender, RoutedEventArgs e) {
var url = $"https://github.com/sourcegit-scm/sourcegit/releases/{Version.TagName}"; var url = $"https://github.com/sourcegit-scm/sourcegit/releases/{Version.TagName}";
#if NET48 var info = new ProcessStartInfo("cmd", $"/c start {url}");
Process.Start(url); info.CreateNoWindow = true;
#else Process.Start(info);
var info = new ProcessStartInfo("cmd", $"/c start {url}"); }
info.CreateNoWindow = true;
Process.Start(info); private void Quit(object sender, RoutedEventArgs e) {
#endif Close();
} }
}
private void Quit(object sender, RoutedEventArgs e) { }
Close();
}
}
}

View file

@ -72,15 +72,6 @@ namespace SourceGit.Views.Widgets {
} else { } else {
searching = true; searching = true;
foreach (var c in cachedCommits) { foreach (var c in cachedCommits) {
#if NET48
if (c.SHA.Contains(filter)
|| c.Subject.Contains(filter)
|| c.Message.Contains(filter)
|| c.Author.Name.Contains(filter)
|| c.Committer.Name.Contains(filter)) {
visible.Add(c);
}
#else
if (c.SHA.Contains(filter, StringComparison.Ordinal) if (c.SHA.Contains(filter, StringComparison.Ordinal)
|| c.Subject.Contains(filter, StringComparison.Ordinal) || c.Subject.Contains(filter, StringComparison.Ordinal)
|| c.Message.Contains(filter, StringComparison.Ordinal) || c.Message.Contains(filter, StringComparison.Ordinal)
@ -88,7 +79,6 @@ namespace SourceGit.Views.Widgets {
|| c.Committer.Name.Contains(filter, StringComparison.Ordinal)) { || c.Committer.Name.Contains(filter, StringComparison.Ordinal)) {
visible.Add(c); visible.Add(c);
} }
#endif
} }
} }

View file

@ -215,11 +215,7 @@ namespace SourceGit.Views.Widgets {
if (!added) Changes.Add(c); if (!added) Changes.Add(c);
#if NET48
int sepIdx = c.Path.IndexOf("/", StringComparison.Ordinal);
#else
int sepIdx = c.Path.IndexOf('/', StringComparison.Ordinal); int sepIdx = c.Path.IndexOf('/', StringComparison.Ordinal);
#endif
if (sepIdx < 0) { if (sepIdx < 0) {
GetOrAddTreeNode(Nodes, c.Path, c, false); GetOrAddTreeNode(Nodes, c.Path, c, false);
} else { } else {