mirror of
https://fastgit.cc/github.com/sourcegit-scm/sourcegit
synced 2026-04-23 02:12:25 +08:00
refactor: rewrite the commit message editor
Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
@@ -179,10 +179,8 @@
|
||||
<x:String x:Key="Text.CommitDetail.Info.SHA" xml:space="preserve">SHA</x:String>
|
||||
<x:String x:Key="Text.CommitDetail.Info.Signer" xml:space="preserve">Signer:</x:String>
|
||||
<x:String x:Key="Text.CommitDetail.Info.WebLinks" xml:space="preserve">Open in Browser</x:String>
|
||||
<x:String x:Key="Text.CommitMessageTextBox.MessagePlaceholder" xml:space="preserve">Description</x:String>
|
||||
<x:String x:Key="Text.CommitMessageTextBox.PasteAndReplaceAll" xml:space="preserve">Paste (Replace all)</x:String>
|
||||
<x:String x:Key="Text.CommitMessageTextBox.Placeholder" xml:space="preserve">Enter commit message. Please use an empty-line to seperate subject and description!</x:String>
|
||||
<x:String x:Key="Text.CommitMessageTextBox.SubjectCount" xml:space="preserve">SUBJECT</x:String>
|
||||
<x:String x:Key="Text.CommitMessageTextBox.SubjectPlaceholder" xml:space="preserve">Enter commit subject</x:String>
|
||||
<x:String x:Key="Text.Configure" xml:space="preserve">Repository Configure</x:String>
|
||||
<x:String x:Key="Text.Configure.CommitMessageTemplate" xml:space="preserve">COMMIT TEMPLATE</x:String>
|
||||
<x:String x:Key="Text.Configure.CommitMessageTemplate.BuiltinVars" xml:space="preserve">Built-in parameters:
|
||||
|
||||
@@ -183,10 +183,8 @@
|
||||
<x:String x:Key="Text.CommitDetail.Info.SHA" xml:space="preserve">提交指纹</x:String>
|
||||
<x:String x:Key="Text.CommitDetail.Info.Signer" xml:space="preserve">签名者 :</x:String>
|
||||
<x:String x:Key="Text.CommitDetail.Info.WebLinks" xml:space="preserve">浏览器中查看</x:String>
|
||||
<x:String x:Key="Text.CommitMessageTextBox.MessagePlaceholder" xml:space="preserve">详细描述</x:String>
|
||||
<x:String x:Key="Text.CommitMessageTextBox.PasteAndReplaceAll" xml:space="preserve">粘贴(替换全部)</x:String>
|
||||
<x:String x:Key="Text.CommitMessageTextBox.Placeholder" xml:space="preserve">请输入提交的信息。注意:主题与具体描述中间需要空白行分隔!</x:String>
|
||||
<x:String x:Key="Text.CommitMessageTextBox.SubjectCount" xml:space="preserve">主题</x:String>
|
||||
<x:String x:Key="Text.CommitMessageTextBox.SubjectPlaceholder" xml:space="preserve">填写提交信息主题</x:String>
|
||||
<x:String x:Key="Text.Configure" xml:space="preserve">仓库配置</x:String>
|
||||
<x:String x:Key="Text.Configure.CommitMessageTemplate" xml:space="preserve">提交信息模板</x:String>
|
||||
<x:String x:Key="Text.Configure.CommitMessageTemplate.BuiltinVars" xml:space="preserve">内置变量:
|
||||
|
||||
@@ -183,10 +183,8 @@
|
||||
<x:String x:Key="Text.CommitDetail.Info.SHA" xml:space="preserve">提交編號</x:String>
|
||||
<x:String x:Key="Text.CommitDetail.Info.Signer" xml:space="preserve">簽署人:</x:String>
|
||||
<x:String x:Key="Text.CommitDetail.Info.WebLinks" xml:space="preserve">在瀏覽器中檢視</x:String>
|
||||
<x:String x:Key="Text.CommitMessageTextBox.MessagePlaceholder" xml:space="preserve">詳細描述</x:String>
|
||||
<x:String x:Key="Text.CommitMessageTextBox.PasteAndReplaceAll" xml:space="preserve">貼上 (全部取代)</x:String>
|
||||
<x:String x:Key="Text.CommitMessageTextBox.Placeholder" xml:space="preserve">請輸入提交訊息。注意:主題與詳細訊息之間必須留一行空行。</x:String>
|
||||
<x:String x:Key="Text.CommitMessageTextBox.SubjectCount" xml:space="preserve">標題</x:String>
|
||||
<x:String x:Key="Text.CommitMessageTextBox.SubjectPlaceholder" xml:space="preserve">填寫提交訊息標題</x:String>
|
||||
<x:String x:Key="Text.Configure" xml:space="preserve">存放庫設定</x:String>
|
||||
<x:String x:Key="Text.Configure.CommitMessageTemplate" xml:space="preserve">提交訊息範本</x:String>
|
||||
<x:String x:Key="Text.Configure.CommitMessageTemplate.BuiltinVars" xml:space="preserve">內建參數:
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
</Grid>
|
||||
|
||||
<StackPanel Grid.Row="1" Orientation="Vertical" Margin="8">
|
||||
<v:CommitMessageTextBox x:Name="Editor" Height="400"/>
|
||||
<v:CommitMessageToolBox x:Name="Editor" Height="400"/>
|
||||
<Button Classes="flat primary"
|
||||
Width="80"
|
||||
Margin="0,8,0,4"
|
||||
|
||||
@@ -43,11 +43,7 @@ namespace SourceGit.Views
|
||||
_onSave = msg => File.WriteAllText(file, msg);
|
||||
_shouldExitApp = true;
|
||||
|
||||
var content = File.ReadAllText(file).ReplaceLineEndings("\n").Trim();
|
||||
var parts = content.Split('\n', 2);
|
||||
Editor.SubjectEditor.Text = parts[0];
|
||||
if (parts.Length > 1)
|
||||
Editor.DescriptionEditor.Text = parts[1].Trim();
|
||||
Editor.CommitMessage = File.ReadAllText(file).ReplaceLineEndings("\n").Trim();
|
||||
}
|
||||
|
||||
public void AsBuiltin(string conventionalTypesOverride, string msg, Action<string> onSave)
|
||||
@@ -57,10 +53,7 @@ namespace SourceGit.Views
|
||||
_onSave = onSave;
|
||||
_shouldExitApp = false;
|
||||
|
||||
var parts = msg.Split('\n', 2);
|
||||
Editor.SubjectEditor.Text = parts[0];
|
||||
if (parts.Length > 1)
|
||||
Editor.DescriptionEditor.Text = parts[1].Trim();
|
||||
Editor.CommitMessage = msg;
|
||||
}
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
@@ -73,7 +66,7 @@ namespace SourceGit.Views
|
||||
|
||||
private void SaveAndClose(object _1, RoutedEventArgs _2)
|
||||
{
|
||||
_onSave?.Invoke(Editor.Text);
|
||||
_onSave?.Invoke(Editor.CommitMessage);
|
||||
_exitCode = 0;
|
||||
Close();
|
||||
}
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
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:c="using:SourceGit.Converters"
|
||||
xmlns:vm="using:SourceGit.ViewModels"
|
||||
xmlns:v="using:SourceGit.Views"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="SourceGit.Views.CommitMessageTextBox"
|
||||
x:Name="ThisControl">
|
||||
<Border Background="{DynamicResource Brush.Contents}"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{DynamicResource Brush.Border2}"
|
||||
CornerRadius="4">
|
||||
<Grid RowDefinitions="Auto,1,*,1,24">
|
||||
<v:EnhancedTextBox Grid.Row="0"
|
||||
x:Name="SubjectEditor"
|
||||
Classes="no_border"
|
||||
Margin="0"
|
||||
Padding="4"
|
||||
CornerRadius="4,4,0,0"
|
||||
BorderThickness="0"
|
||||
Background="Transparent"
|
||||
AcceptsReturn="False"
|
||||
TextWrapping="Wrap"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Disabled"
|
||||
Text="{Binding #ThisControl.Subject, Mode=TwoWay}"
|
||||
Watermark="{DynamicResource Text.CommitMessageTextBox.SubjectPlaceholder}"
|
||||
PreviewKeyDown="OnSubjectTextBoxPreviewKeyDown"
|
||||
Tag="{Binding Source={x:Static v:StealHotKey.Enter}}"/>
|
||||
|
||||
<Rectangle Grid.Row="1"
|
||||
Height="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
IsHitTestVisible="False"
|
||||
Fill="{DynamicResource Brush.Border2}"/>
|
||||
|
||||
<v:EnhancedTextBox Grid.Row="2"
|
||||
x:Name="DescriptionEditor"
|
||||
Classes="no_border"
|
||||
Margin="0"
|
||||
Padding="4"
|
||||
BorderThickness="0"
|
||||
Background="Transparent"
|
||||
VerticalContentAlignment="Top"
|
||||
AcceptsReturn="True"
|
||||
AcceptsTab="True"
|
||||
TextWrapping="Wrap"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||
Text="{Binding #ThisControl.Description, Mode=TwoWay}"
|
||||
Watermark="{DynamicResource Text.CommitMessageTextBox.MessagePlaceholder}"
|
||||
PreviewKeyDown="OnDescriptionTextBoxPreviewKeyDown"
|
||||
Tag="{Binding Source={x:Static v:StealHotKey.Enter}}"/>
|
||||
|
||||
<Rectangle Grid.Row="3"
|
||||
Height="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
IsHitTestVisible="False"
|
||||
Fill="{DynamicResource Brush.Border2}"/>
|
||||
|
||||
<Border Grid.Row="4"
|
||||
Background="{DynamicResource Brush.Window}"
|
||||
BorderThickness="0"
|
||||
CornerRadius="0,0,4,4">
|
||||
<Grid ColumnDefinitions="Auto,Auto,Auto,Auto,Auto,*" Margin="0,4">
|
||||
<Button Grid.Column="0"
|
||||
Classes="icon_button"
|
||||
Width="24"
|
||||
Margin="0,0,4,0" Padding="0"
|
||||
Click="OnOpenCommitMessagePicker"
|
||||
IsVisible="{Binding #ThisControl.ShowAdvancedOptions}"
|
||||
ToolTip.Tip="{DynamicResource Text.WorkingCopy.CommitMessageHelper}">
|
||||
<Path Width="12" Height="12" Data="{StaticResource Icons.Menu}"/>
|
||||
</Button>
|
||||
|
||||
<Button Grid.Column="1"
|
||||
Classes="icon_button"
|
||||
Width="24"
|
||||
Margin="0,0,4,0" Padding="0"
|
||||
Click="OnOpenOpenAIHelper"
|
||||
IsVisible="{Binding #ThisControl.ShowAdvancedOptions}"
|
||||
ToolTip.Tip="{DynamicResource Text.AIAssistant.Tip}">
|
||||
<Path Width="13" Height="13" Data="{StaticResource Icons.AIAssist}"/>
|
||||
</Button>
|
||||
|
||||
<Button Grid.Column="2"
|
||||
Classes="icon_button"
|
||||
Width="24"
|
||||
Margin="0,0,4,0" Padding="0"
|
||||
Click="OnOpenConventionalCommitHelper"
|
||||
ToolTip.Tip="{DynamicResource Text.ConventionalCommit}">
|
||||
<Path Width="13" Height="13" Margin="0,1,0,0" Data="{StaticResource Icons.CommitMessageGenerator}"/>
|
||||
</Button>
|
||||
|
||||
<Button Grid.Column="3"
|
||||
Classes="icon_button"
|
||||
Width="24"
|
||||
Margin="0,0,4,0" Padding="0"
|
||||
Click="CopyAllText"
|
||||
ToolTip.Tip="{DynamicResource Text.CopyAllText}">
|
||||
<Path Width="13" Height="13" Data="{StaticResource Icons.Copy}"/>
|
||||
</Button>
|
||||
|
||||
<Button Grid.Column="4"
|
||||
Classes="icon_button"
|
||||
Width="24"
|
||||
Margin="0,0,4,0" Padding="0"
|
||||
Click="PasteAndReplaceAllText"
|
||||
ToolTip.Tip="{DynamicResource Text.CommitMessageTextBox.PasteAndReplaceAll}">
|
||||
<Path Width="13" Height="13" Data="{StaticResource Icons.Paste}"/>
|
||||
</Button>
|
||||
|
||||
<StackPanel Grid.Column="5"
|
||||
Margin="0,0,6,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock Classes="info_label" FontSize="13" HorizontalAlignment="Left" Text="{DynamicResource Text.CommitMessageTextBox.SubjectCount}"/>
|
||||
<TextBlock Margin="8,0,0,0" FontSize="11" Text="{Binding #ThisControl.Subject.Length}" IsVisible="{Binding #ThisControl.Subject.Length, Converter={x:Static c:IntConverters.IsSubjectLengthGood}}" VerticalAlignment="Center"/>
|
||||
<TextBlock Margin="8,0,0,0" FontSize="11" Foreground="DarkGoldenrod" Text="{Binding #ThisControl.Subject.Length}" IsVisible="{Binding #ThisControl.Subject.Length, Converter={x:Static c:IntConverters.IsSubjectLengthBad}}" VerticalAlignment="Center"/>
|
||||
<TextBlock FontSize="11" Text="/" VerticalAlignment="Center"/>
|
||||
<TextBlock FontSize="11" Text="{Binding Source={x:Static vm:Preferences.Instance}, Path=SubjectGuideLength}" VerticalAlignment="Center"/>
|
||||
<Path Width="10" Height="10" Margin="4,0,0,0" Data="{StaticResource Icons.Error}" Fill="DarkGoldenrod" IsVisible="{Binding #ThisControl.Subject.Length, Converter={x:Static c:IntConverters.IsSubjectLengthBad}}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
</UserControl>
|
||||
82
src/Views/CommitMessageToolBox.axaml
Normal file
82
src/Views/CommitMessageToolBox.axaml
Normal file
@@ -0,0 +1,82 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
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:c="using:SourceGit.Converters"
|
||||
xmlns:vm="using:SourceGit.ViewModels"
|
||||
xmlns:v="using:SourceGit.Views"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="SourceGit.Views.CommitMessageToolBox"
|
||||
x:Name="ThisControl">
|
||||
<Border Background="{DynamicResource Brush.Contents}"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{DynamicResource Brush.Border2}"
|
||||
CornerRadius="4">
|
||||
<Grid RowDefinitions="*,1,24">
|
||||
<v:CommitMessageTextEditor Grid.Row="0"
|
||||
x:Name="Editor"
|
||||
CommitMessage="{Binding #ThisControl.CommitMessage, Mode=TwoWay}"
|
||||
SubjectLineBrush="{DynamicResource Brush.Border2}"
|
||||
Foreground="{DynamicResource Brush.FG1}"
|
||||
FontFamily="{DynamicResource Fonts.Default}"
|
||||
FontSize="{Binding Source={x:Static vm:Preferences.Instance}, Path=EditorFontSize}"
|
||||
Tag="{Binding Source={x:Static v:StealHotKey.Enter}}"/>
|
||||
|
||||
<Rectangle Grid.Row="1"
|
||||
Height="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
IsHitTestVisible="False"
|
||||
Fill="{DynamicResource Brush.Border2}"/>
|
||||
|
||||
<Border Grid.Row="2"
|
||||
Background="{DynamicResource Brush.Window}"
|
||||
BorderThickness="0"
|
||||
CornerRadius="0,0,4,4">
|
||||
<Grid ColumnDefinitions="Auto,Auto,Auto,*" Margin="0,4">
|
||||
<Button Grid.Column="0"
|
||||
Classes="icon_button"
|
||||
Width="24"
|
||||
Margin="0,0,4,0" Padding="0"
|
||||
Click="OnOpenCommitMessagePicker"
|
||||
IsVisible="{Binding #ThisControl.ShowAdvancedOptions}"
|
||||
ToolTip.Tip="{DynamicResource Text.WorkingCopy.CommitMessageHelper}">
|
||||
<Path Width="12" Height="12" Data="{StaticResource Icons.Menu}"/>
|
||||
</Button>
|
||||
|
||||
<Button Grid.Column="1"
|
||||
Classes="icon_button"
|
||||
Width="24"
|
||||
Margin="0,0,4,0" Padding="0"
|
||||
Click="OnOpenOpenAIHelper"
|
||||
IsVisible="{Binding #ThisControl.ShowAdvancedOptions}"
|
||||
ToolTip.Tip="{DynamicResource Text.AIAssistant.Tip}">
|
||||
<Path Width="13" Height="13" Data="{StaticResource Icons.AIAssist}"/>
|
||||
</Button>
|
||||
|
||||
<Button Grid.Column="2"
|
||||
Classes="icon_button"
|
||||
Width="24"
|
||||
Margin="0,0,4,0" Padding="0"
|
||||
Click="OnOpenConventionalCommitHelper"
|
||||
ToolTip.Tip="{DynamicResource Text.ConventionalCommit}">
|
||||
<Path Width="13" Height="13" Margin="0,1,0,0" Data="{StaticResource Icons.CommitMessageGenerator}"/>
|
||||
</Button>
|
||||
|
||||
<StackPanel Grid.Column="3"
|
||||
Margin="0,0,6,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock Classes="info_label" FontSize="13" HorizontalAlignment="Left" Text="{DynamicResource Text.CommitMessageTextBox.SubjectCount}"/>
|
||||
<TextBlock Margin="8,0,0,0" FontSize="11" Text="{Binding #Editor.SubjectLength}" IsVisible="{Binding #Editor.SubjectLength, Converter={x:Static c:IntConverters.IsSubjectLengthGood}}" VerticalAlignment="Center"/>
|
||||
<TextBlock Margin="8,0,0,0" FontSize="11" Foreground="DarkGoldenrod" Text="{Binding #Editor.SubjectLength}" IsVisible="{Binding #Editor.SubjectLength, Converter={x:Static c:IntConverters.IsSubjectLengthBad}}" VerticalAlignment="Center"/>
|
||||
<TextBlock FontSize="11" Text="/" VerticalAlignment="Center"/>
|
||||
<TextBlock FontSize="11" Text="{Binding Source={x:Static vm:Preferences.Instance}, Path=SubjectGuideLength}" VerticalAlignment="Center"/>
|
||||
<Path Width="10" Height="10" Margin="4,0,0,0" Data="{StaticResource Icons.Error}" Fill="DarkGoldenrod" IsVisible="{Binding #Editor.SubjectLength, Converter={x:Static c:IntConverters.IsSubjectLengthBad}}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
</UserControl>
|
||||
@@ -1,75 +1,246 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Media;
|
||||
|
||||
using AvaloniaEdit;
|
||||
using AvaloniaEdit.Document;
|
||||
using AvaloniaEdit.Editing;
|
||||
using AvaloniaEdit.Rendering;
|
||||
|
||||
namespace SourceGit.Views
|
||||
{
|
||||
public class EnhancedTextBox : TextBox
|
||||
public class CommitMessageTextEditor : TextEditor
|
||||
{
|
||||
public static readonly RoutedEvent<KeyEventArgs> PreviewKeyDownEvent =
|
||||
RoutedEvent.Register<EnhancedTextBox, KeyEventArgs>(nameof(KeyEventArgs), RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
|
||||
public static readonly StyledProperty<string> CommitMessageProperty =
|
||||
AvaloniaProperty.Register<CommitMessageTextEditor, string>(nameof(CommitMessage), string.Empty);
|
||||
|
||||
public event EventHandler<KeyEventArgs> PreviewKeyDown
|
||||
public string CommitMessage
|
||||
{
|
||||
add { AddHandler(PreviewKeyDownEvent, value); }
|
||||
remove { RemoveHandler(PreviewKeyDownEvent, value); }
|
||||
get => GetValue(CommitMessageProperty);
|
||||
set => SetValue(CommitMessageProperty, value);
|
||||
}
|
||||
|
||||
protected override Type StyleKeyOverride => typeof(TextBox);
|
||||
public static readonly StyledProperty<int> SubjectLengthProperty =
|
||||
AvaloniaProperty.Register<CommitMessageTextEditor, int>(nameof(SubjectLength), 0);
|
||||
|
||||
public void Paste(string text)
|
||||
public int SubjectLength
|
||||
{
|
||||
OnTextInput(new TextInputEventArgs() { Text = text });
|
||||
get => GetValue(SubjectLengthProperty);
|
||||
set => SetValue(SubjectLengthProperty, value);
|
||||
}
|
||||
|
||||
protected override void OnKeyDown(KeyEventArgs e)
|
||||
public static readonly StyledProperty<IBrush> SubjectLineBrushProperty =
|
||||
AvaloniaProperty.Register<CommitMessageTextEditor, IBrush>(nameof(SubjectLineBrush), Brushes.Gray);
|
||||
|
||||
public IBrush SubjectLineBrush
|
||||
{
|
||||
var dump = new KeyEventArgs()
|
||||
get => GetValue(SubjectLineBrushProperty);
|
||||
set => SetValue(SubjectLineBrushProperty, value);
|
||||
}
|
||||
|
||||
protected override Type StyleKeyOverride => typeof(TextEditor);
|
||||
|
||||
public CommitMessageTextEditor() : base(new TextArea(), new TextDocument())
|
||||
{
|
||||
IsReadOnly = false;
|
||||
WordWrap = true;
|
||||
ShowLineNumbers = false;
|
||||
HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
|
||||
VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
|
||||
|
||||
TextArea.TextView.Margin = new Thickness(4, 2);
|
||||
TextArea.TextView.Options.EnableHyperlinks = false;
|
||||
TextArea.TextView.Options.EnableEmailHyperlinks = false;
|
||||
TextArea.TextView.Options.AllowScrollBelowDocument = false;
|
||||
}
|
||||
|
||||
public override void Render(DrawingContext context)
|
||||
{
|
||||
base.Render(context);
|
||||
|
||||
var w = Bounds.Width;
|
||||
var pen = new Pen(SubjectLineBrush) { DashStyle = DashStyle.Dash };
|
||||
|
||||
if (SubjectLength == 0 || CommitMessage.Trim().Length == 0)
|
||||
{
|
||||
RoutedEvent = PreviewKeyDownEvent,
|
||||
Route = RoutingStrategies.Direct,
|
||||
Source = e.Source,
|
||||
Key = e.Key,
|
||||
KeyModifiers = e.KeyModifiers,
|
||||
PhysicalKey = e.PhysicalKey,
|
||||
KeySymbol = e.KeySymbol,
|
||||
var placeholder = new FormattedText(
|
||||
App.Text("CommitMessageTextBox.Placeholder"),
|
||||
CultureInfo.CurrentCulture,
|
||||
FlowDirection.LeftToRight,
|
||||
new Typeface(FontFamily),
|
||||
FontSize,
|
||||
Brushes.Gray);
|
||||
|
||||
context.DrawText(placeholder, new Point(4, 2));
|
||||
|
||||
var y = 6 + placeholder.Height;
|
||||
context.DrawLine(pen, new Point(0, y), new Point(w, y));
|
||||
return;
|
||||
}
|
||||
|
||||
if (TextArea.TextView is not { VisualLinesValid: true } view)
|
||||
return;
|
||||
|
||||
var lines = new List<VisualLine>();
|
||||
foreach (var line in view.VisualLines)
|
||||
{
|
||||
if (line.IsDisposed || line.FirstDocumentLine == null || line.FirstDocumentLine.IsDeleted)
|
||||
continue;
|
||||
|
||||
lines.Add(line);
|
||||
}
|
||||
|
||||
if (lines.Count == 0)
|
||||
return;
|
||||
|
||||
lines.Sort((l, r) => l.StartOffset - r.StartOffset);
|
||||
|
||||
var lastSubjectLine = lines[0];
|
||||
for (var i = 1; i < lines.Count; i++)
|
||||
{
|
||||
if (lines[i].StartOffset > SubjectLength)
|
||||
break;
|
||||
|
||||
lastSubjectLine = lines[i];
|
||||
}
|
||||
|
||||
var endY = lastSubjectLine.GetTextLineVisualYPosition(lastSubjectLine.TextLines[^1], VisualYPosition.LineBottom) - view.VerticalOffset + 4;
|
||||
context.DrawLine(pen, new Point(0, endY), new Point(w, endY));
|
||||
}
|
||||
|
||||
protected override void OnLoaded(RoutedEventArgs e)
|
||||
{
|
||||
base.OnLoaded(e);
|
||||
|
||||
TextArea.TextView.LayoutUpdated += OnTextViewLayoutUpdated;
|
||||
TextArea.TextView.ContextRequested += OnTextViewContextRequested;
|
||||
}
|
||||
|
||||
protected override void OnUnloaded(RoutedEventArgs e)
|
||||
{
|
||||
TextArea.TextView.ContextRequested -= OnTextViewContextRequested;
|
||||
TextArea.TextView.LayoutUpdated -= OnTextViewLayoutUpdated;
|
||||
|
||||
base.OnUnloaded(e);
|
||||
}
|
||||
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||
{
|
||||
base.OnPropertyChanged(change);
|
||||
|
||||
if (change.Property == CommitMessageProperty)
|
||||
{
|
||||
if (!_isEditing)
|
||||
Text = CommitMessage;
|
||||
|
||||
var chars = CommitMessage.ToCharArray();
|
||||
var lastLinebreakIndex = 0;
|
||||
var lastLinebreakCount = 0;
|
||||
var foundSubjectEnd = false;
|
||||
for (var i = 0; i < chars.Length; i++)
|
||||
{
|
||||
var ch = chars[i];
|
||||
if (ch == '\r')
|
||||
continue;
|
||||
|
||||
if (ch == '\n')
|
||||
{
|
||||
if (lastLinebreakCount > 0)
|
||||
{
|
||||
SetCurrentValue(SubjectLengthProperty, lastLinebreakIndex);
|
||||
foundSubjectEnd = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastLinebreakIndex = i;
|
||||
lastLinebreakCount = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lastLinebreakCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundSubjectEnd)
|
||||
SetCurrentValue(SubjectLengthProperty, CommitMessage?.Length ?? 0);
|
||||
|
||||
InvalidateVisual();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnTextChanged(EventArgs e)
|
||||
{
|
||||
base.OnTextChanged(e);
|
||||
|
||||
_isEditing = true;
|
||||
SetCurrentValue(CommitMessageProperty, Text);
|
||||
_isEditing = false;
|
||||
}
|
||||
|
||||
private void OnTextViewContextRequested(object sender, ContextRequestedEventArgs e)
|
||||
{
|
||||
var selection = TextArea.Selection;
|
||||
var hasSelected = selection is { IsEmpty: false };
|
||||
|
||||
var copy = new MenuItem();
|
||||
copy.Header = App.Text("Copy");
|
||||
copy.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copy.IsEnabled = hasSelected;
|
||||
copy.Click += (o, ev) =>
|
||||
{
|
||||
Copy();
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
RaiseEvent(dump);
|
||||
var cut = new MenuItem();
|
||||
cut.Header = App.Text("Cut");
|
||||
cut.Icon = App.CreateMenuIcon("Icons.Cut");
|
||||
cut.IsEnabled = hasSelected;
|
||||
cut.Click += (o, ev) =>
|
||||
{
|
||||
Cut();
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
if (dump.Handled)
|
||||
e.Handled = true;
|
||||
else
|
||||
base.OnKeyDown(e);
|
||||
var paste = new MenuItem();
|
||||
paste.Header = App.Text("Paste");
|
||||
paste.Icon = App.CreateMenuIcon("Icons.Paste");
|
||||
paste.Click += (o, ev) =>
|
||||
{
|
||||
Paste();
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var menu = new ContextMenu();
|
||||
menu.Items.Add(copy);
|
||||
menu.Items.Add(cut);
|
||||
menu.Items.Add(paste);
|
||||
menu.Open(TextArea.TextView);
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void OnTextViewLayoutUpdated(object sender, EventArgs e)
|
||||
{
|
||||
InvalidateVisual();
|
||||
}
|
||||
|
||||
private bool _isEditing = false;
|
||||
}
|
||||
|
||||
public partial class CommitMessageTextBox : UserControl
|
||||
public partial class CommitMessageToolBox : UserControl
|
||||
{
|
||||
public enum TextChangeWay
|
||||
{
|
||||
None,
|
||||
FromSource,
|
||||
FromEditor,
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<bool> ShowAdvancedOptionsProperty =
|
||||
AvaloniaProperty.Register<CommitMessageTextBox, bool>(nameof(ShowAdvancedOptions));
|
||||
|
||||
public static readonly StyledProperty<string> TextProperty =
|
||||
AvaloniaProperty.Register<CommitMessageTextBox, string>(nameof(Text), string.Empty);
|
||||
|
||||
public static readonly StyledProperty<string> SubjectProperty =
|
||||
AvaloniaProperty.Register<CommitMessageTextBox, string>(nameof(Subject), string.Empty);
|
||||
|
||||
public static readonly StyledProperty<string> DescriptionProperty =
|
||||
AvaloniaProperty.Register<CommitMessageTextBox, string>(nameof(Description), string.Empty);
|
||||
AvaloniaProperty.Register<CommitMessageToolBox, bool>(nameof(ShowAdvancedOptions));
|
||||
|
||||
public bool ShowAdvancedOptions
|
||||
{
|
||||
@@ -77,102 +248,20 @@ namespace SourceGit.Views
|
||||
set => SetValue(ShowAdvancedOptionsProperty, value);
|
||||
}
|
||||
|
||||
public string Text
|
||||
public static readonly StyledProperty<string> CommitMessageProperty =
|
||||
AvaloniaProperty.Register<CommitMessageToolBox, string>(nameof(CommitMessage), string.Empty);
|
||||
|
||||
public string CommitMessage
|
||||
{
|
||||
get => GetValue(TextProperty);
|
||||
set => SetValue(TextProperty, value);
|
||||
get => GetValue(CommitMessageProperty);
|
||||
set => SetValue(CommitMessageProperty, value);
|
||||
}
|
||||
|
||||
public string Subject
|
||||
{
|
||||
get => GetValue(SubjectProperty);
|
||||
set => SetValue(SubjectProperty, value);
|
||||
}
|
||||
|
||||
public string Description
|
||||
{
|
||||
get => GetValue(DescriptionProperty);
|
||||
set => SetValue(DescriptionProperty, value);
|
||||
}
|
||||
|
||||
public CommitMessageTextBox()
|
||||
public CommitMessageToolBox()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||
{
|
||||
base.OnPropertyChanged(change);
|
||||
|
||||
if (change.Property == TextProperty && _changingWay == TextChangeWay.None)
|
||||
{
|
||||
_changingWay = TextChangeWay.FromSource;
|
||||
var normalized = Text.ReplaceLineEndings("\n");
|
||||
var parts = normalized.Split("\n\n", 2);
|
||||
if (parts.Length != 2)
|
||||
parts = [normalized, string.Empty];
|
||||
SetCurrentValue(SubjectProperty, parts[0].ReplaceLineEndings(" "));
|
||||
SetCurrentValue(DescriptionProperty, parts[1]);
|
||||
_changingWay = TextChangeWay.None;
|
||||
}
|
||||
else if ((change.Property == SubjectProperty || change.Property == DescriptionProperty) && _changingWay == TextChangeWay.None)
|
||||
{
|
||||
_changingWay = TextChangeWay.FromEditor;
|
||||
SetCurrentValue(TextProperty, $"{Subject}\n\n{Description}");
|
||||
_changingWay = TextChangeWay.None;
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnSubjectTextBoxPreviewKeyDown(object _, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Enter || (e.Key == Key.Right && SubjectEditor.CaretIndex == Subject.Length))
|
||||
{
|
||||
DescriptionEditor.Focus();
|
||||
DescriptionEditor.CaretIndex = 0;
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == Key.V && e.KeyModifiers == (OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control))
|
||||
{
|
||||
e.Handled = true;
|
||||
|
||||
var text = await App.GetClipboardTextAsync();
|
||||
if (!string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
text = text.Trim();
|
||||
|
||||
if (SubjectEditor.CaretIndex == Subject.Length)
|
||||
{
|
||||
var parts = text.Split('\n', 2);
|
||||
if (parts.Length != 2)
|
||||
{
|
||||
SubjectEditor.Paste(text);
|
||||
}
|
||||
else
|
||||
{
|
||||
SubjectEditor.Paste(parts[0]);
|
||||
DescriptionEditor.Focus();
|
||||
DescriptionEditor.CaretIndex = 0;
|
||||
DescriptionEditor.Paste(parts[1].Trim());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SubjectEditor.Paste(text.ReplaceLineEndings(" "));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDescriptionTextBoxPreviewKeyDown(object _, KeyEventArgs e)
|
||||
{
|
||||
if ((e.Key == Key.Back || e.Key == Key.Left) && DescriptionEditor.CaretIndex == 0)
|
||||
{
|
||||
SubjectEditor.Focus();
|
||||
SubjectEditor.CaretIndex = Subject.Length;
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnOpenCommitMessagePicker(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is Button button && DataContext is ViewModels.WorkingCopy vm && ShowAdvancedOptions)
|
||||
@@ -351,39 +440,11 @@ namespace SourceGit.Views
|
||||
_ => string.Empty
|
||||
};
|
||||
|
||||
var vm = new ViewModels.ConventionalCommitMessageBuilder(conventionalTypesOverride, text => Text = text);
|
||||
var vm = new ViewModels.ConventionalCommitMessageBuilder(conventionalTypesOverride, text => CommitMessage = text);
|
||||
var builder = new ConventionalCommitMessageBuilder() { DataContext = vm };
|
||||
await builder.ShowDialog(owner);
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private async void CopyAllText(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await App.CopyTextAsync(Text);
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private async void PasteAndReplaceAllText(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var text = await App.GetClipboardTextAsync();
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
var parts = text.ReplaceLineEndings("\n").Split("\n", 2);
|
||||
var subject = parts[0];
|
||||
Text = parts.Length > 1 ? $"{subject}\n\n{parts[1].Trim()}" : subject;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore exceptions.
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private TextChangeWay _changingWay = TextChangeWay.None;
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,7 @@
|
||||
CornerRadius="0,0,8,8"
|
||||
ClipToBounds="True"
|
||||
IsVisible="{Binding Popup, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||
<ContentControl Content="{Binding Popup}" Background="{DynamicResource Brush.Popup}">
|
||||
<ContentControl x:Name="PopupPanel" Content="{Binding Popup}" Background="{DynamicResource Brush.Popup}">
|
||||
<ContentControl.DataTemplates>
|
||||
<DataTemplate DataType="vm:Popup">
|
||||
<StackPanel Orientation="Vertical" Background="{DynamicResource Brush.Popup}">
|
||||
|
||||
@@ -15,23 +15,27 @@ namespace SourceGit.Views
|
||||
|
||||
private async void OnPopupSureByHotKey(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var children = this.GetLogicalDescendants();
|
||||
var children = PopupPanel.GetLogicalDescendants();
|
||||
foreach (var child in children)
|
||||
{
|
||||
if (child is TextBox { IsFocused: true, Tag: StealHotKey steal } textBox &&
|
||||
if (child is Control { IsKeyboardFocusWithin: true, Tag: StealHotKey steal } control &&
|
||||
steal is { Key: Key.Enter, KeyModifiers: KeyModifiers.None })
|
||||
{
|
||||
var fake = new KeyEventArgs()
|
||||
{
|
||||
RoutedEvent = KeyDownEvent,
|
||||
Route = RoutingStrategies.Direct,
|
||||
Source = textBox,
|
||||
Source = control,
|
||||
Key = Key.Enter,
|
||||
KeyModifiers = KeyModifiers.None,
|
||||
PhysicalKey = PhysicalKey.Enter,
|
||||
};
|
||||
|
||||
textBox.RaiseEvent(fake);
|
||||
if (control is AvaloniaEdit.TextEditor editor)
|
||||
editor.TextArea.TextView.RaiseEvent(fake);
|
||||
else
|
||||
control.RaiseEvent(fake);
|
||||
|
||||
e.Handled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -275,7 +275,7 @@
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<v:CommitMessageTextBox Margin="0,4,0,0" Height="200" Text="{Binding Content, Mode=TwoWay}"/>
|
||||
<v:CommitMessageToolBox Margin="0,4,0,0" Height="200" CommitMessage="{Binding Content, Mode=TwoWay}"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ContentControl.DataTemplates>
|
||||
|
||||
@@ -18,6 +18,6 @@
|
||||
<TextBlock Text="{Binding Head.SHA, Converter={x:Static c:StringConverters.ToShortSHA}}" Foreground="DarkOrange"/>
|
||||
</StackPanel>
|
||||
|
||||
<v:CommitMessageTextBox Height="120" Margin="8,5,8,0" Text="{Binding Message, Mode=TwoWay}"/>
|
||||
<v:CommitMessageToolBox Height="120" Margin="8,5,8,0" CommitMessage="{Binding Message, Mode=TwoWay}"/>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
|
||||
@@ -27,6 +27,6 @@
|
||||
<TextBlock Grid.Column="3" Margin="8,0,0,0" Text="{Binding Target.Subject}" TextTrimming="CharacterEllipsis"/>
|
||||
</Grid>
|
||||
|
||||
<v:CommitMessageTextBox Height="120" Margin="0,4,0,0" Text="{Binding Message, Mode=TwoWay}"/>
|
||||
<v:CommitMessageToolBox Height="120" Margin="0,4,0,0" CommitMessage="{Binding Message, Mode=TwoWay}"/>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
|
||||
@@ -239,7 +239,7 @@
|
||||
Background="Transparent"/>
|
||||
|
||||
<!-- Commit Message -->
|
||||
<v:CommitMessageTextBox Grid.Row="2" ShowAdvancedOptions="True" Text="{Binding CommitMessage, Mode=TwoWay}"/>
|
||||
<v:CommitMessageToolBox Grid.Row="2" ShowAdvancedOptions="True" CommitMessage="{Binding CommitMessage, Mode=TwoWay}"/>
|
||||
|
||||
<!-- Commit Options -->
|
||||
<Grid Grid.Row="3" Margin="0,6,0,0" ColumnDefinitions="Auto,Auto,Auto,Auto,*,Auto,Auto,Auto">
|
||||
|
||||
Reference in New Issue
Block a user