diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index cc5c56c5..165afaa4 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -105,8 +105,8 @@ PARENTS REFS SHA - Enter commit subject & message - Git uses an empty line to separate the subject and extra message body. + Enter commit subject + Description Repository Configure Email Address Email address diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 543eb8ea..59a1f3ba 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -108,8 +108,8 @@ 父提交 相关引用 提交指纹 - 填写提交信息 - Git使用空白行来划分提交信息中的主题与内容 + 填写提交信息主题 + 详细描述 仓库配置 电子邮箱 邮箱地址 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index c97d994d..90476146 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -108,8 +108,8 @@ 父提交 相關引用 提交指紋 - 填寫提交資訊 - Git使用空白行來劃分提交資訊中的主題與內容 + 填寫提交信息主題 + 詳細描述 倉庫配置 電子郵箱 郵箱地址 diff --git a/src/Views/CommitMessageTextBox.axaml b/src/Views/CommitMessageTextBox.axaml index 779d9c7b..96d902b7 100644 --- a/src/Views/CommitMessageTextBox.axaml +++ b/src/Views/CommitMessageTextBox.axaml @@ -2,9 +2,9 @@ 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:ae="using:AvaloniaEdit" 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"> @@ -12,52 +12,67 @@ BorderThickness="1" BorderBrush="{DynamicResource Brush.Border2}" CornerRadius="4"> - - - + + + + - - - + + + + + + + - - - - - - - - - - - - - - - - - - + + + diff --git a/src/Views/CommitMessageTextBox.axaml.cs b/src/Views/CommitMessageTextBox.axaml.cs index afefacb2..f87275ee 100644 --- a/src/Views/CommitMessageTextBox.axaml.cs +++ b/src/Views/CommitMessageTextBox.axaml.cs @@ -2,122 +2,137 @@ using System; using Avalonia; using Avalonia.Controls; - -using AvaloniaEdit.Document; -using AvaloniaEdit.Rendering; +using Avalonia.Input; +using Avalonia.Interactivity; namespace SourceGit.Views { + public class EnhancedTextBox : TextBox + { + public static readonly RoutedEvent PreviewKeyDownEvent = + RoutedEvent.Register(nameof(KeyEventArgs), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); + + public event EventHandler PreviewKeyDown + { + add { AddHandler(PreviewKeyDownEvent, value); } + remove { RemoveHandler(PreviewKeyDownEvent, value); } + } + + protected override Type StyleKeyOverride => typeof(TextBox); + + protected override void OnKeyDown(KeyEventArgs e) + { + var dump = new KeyEventArgs() + { + RoutedEvent = PreviewKeyDownEvent, + Route = RoutingStrategies.Direct, + Source = e.Source, + Key = e.Key, + KeyModifiers = e.KeyModifiers, + PhysicalKey = e.PhysicalKey, + KeySymbol = e.KeySymbol, + }; + + RaiseEvent(dump); + + if (dump.Handled) + e.Handled = true; + else + base.OnKeyDown(e); + } + } + public partial class CommitMessageTextBox : UserControl { + public enum TextChangeWay + { + None, + FromSource, + FromEditor, + } + public static readonly StyledProperty TextProperty = AvaloniaProperty.Register(nameof(Text), string.Empty); - public static readonly DirectProperty SubjectLengthProperty = - AvaloniaProperty.RegisterDirect(nameof(SubjectLength), o => o.SubjectLength); + public static readonly StyledProperty SubjectProperty = + AvaloniaProperty.Register(nameof(Subject), string.Empty); + + public static readonly StyledProperty DescriptionProperty = + AvaloniaProperty.Register(nameof(Description), string.Empty); - public static readonly DirectProperty TotalLengthProperty = - AvaloniaProperty.RegisterDirect(nameof(TotalLength), o => o.TotalLength); - public string Text { get => GetValue(TextProperty); set => SetValue(TextProperty, value); } - public int SubjectLength + public string Subject { - get => _subjectLength; - private set => SetAndRaise(SubjectLengthProperty, ref _subjectLength, value); + get => GetValue(SubjectProperty); + set => SetValue(SubjectProperty, value); } - public int TotalLength + public string Description { - get => _totalLength; - private set => SetAndRaise(TotalLengthProperty, ref _totalLength, value); + get => GetValue(DescriptionProperty); + set => SetValue(DescriptionProperty, value); } - public TextDocument Document - { - get; - } - public CommitMessageTextBox() { - Document = new TextDocument(Text); InitializeComponent(); } - + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { base.OnPropertyChanged(change); - if (change.Property == TextProperty && !_isDocumentTextChanging) - Document.Text = Text; - } - - private void OnTextEditorLayoutUpdated(object sender, EventArgs e) - { - var view = TextEditor.TextArea?.TextView; - if (view is { VisualLinesValid: true }) + if (change.Property == TextProperty && _changingWay == TextChangeWay.None) { - if (_subjectEndLineNumber == 0) + _changingWay = TextChangeWay.FromSource; + var normalized = Text.ReplaceLineEndings("\n").Trim(); + var subjectEnd = normalized.IndexOf("\n\n", StringComparison.Ordinal); + if (subjectEnd == -1) { - SubjectGuideLine.Margin = new Thickness(0, view.DefaultLineHeight + 3, 0, 0); - SubjectGuideLine.IsVisible = true; - return; - } - - foreach (var line in view.VisualLines) - { - var lineNumber = line.FirstDocumentLine.LineNumber; - if (lineNumber == _subjectEndLineNumber) - { - var y = line.GetTextLineVisualYPosition(line.TextLines[^1], VisualYPosition.LineBottom) - view.VerticalOffset + 3; - SubjectGuideLine.Margin = new Thickness(0, y, 0, 0); - SubjectGuideLine.IsVisible = true; - return; - } - } - } - - SubjectGuideLine.IsVisible = false; - } - - private void OnTextEditorTextChanged(object sender, EventArgs e) - { - var text = Document.Text; - _isDocumentTextChanging = true; - SetCurrentValue(TextProperty, text); - TotalLength = text.Trim().Length; - _isDocumentTextChanging = false; - - var foundData = false; - for (var i = 0; i < Document.LineCount; i++) - { - var line = Document.Lines[i]; - if (line.Length == 0) - { - if (foundData) - { - SubjectLength = text[..line.Offset].ReplaceLineEndings(" ").Trim().Length; - return; - } + SetCurrentValue(SubjectProperty, normalized.ReplaceLineEndings(" ")); + SetCurrentValue(DescriptionProperty, string.Empty); } else { - foundData = true; + SetCurrentValue(SubjectProperty, normalized.Substring(0, subjectEnd).ReplaceLineEndings(" ")); + SetCurrentValue(DescriptionProperty, normalized.Substring(subjectEnd + 2)); } - - _subjectEndLineNumber = line.LineNumber; + _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; } - - SubjectLength = text.ReplaceLineEndings(" ").Trim().Length; } - private bool _isDocumentTextChanging = false; - private int _subjectEndLineNumber = 0; - private int _totalLength = 0; - private int _subjectLength = 0; + private void OnSubjectTextBoxPreviewKeyDown(object sender, KeyEventArgs e) + { + if ((e.Key == Key.Enter && e.KeyModifiers == KeyModifiers.None) + || (e.Key == Key.Right && SubjectEditor.CaretIndex == Subject.Length)) + { + DescriptionEditor.Focus(); + DescriptionEditor.CaretIndex = 0; + e.Handled = true; + } + } + + private void OnDescriptionTextBoxPreviewKeyDown(object sender, KeyEventArgs e) + { + if ((e.Key == Key.Back || e.Key == Key.Left) && DescriptionEditor.CaretIndex == 0) + { + SubjectEditor.Focus(); + SubjectEditor.CaretIndex = Subject.Length; + e.Handled = true; + } + } + + private TextChangeWay _changingWay = TextChangeWay.None; } }