refactor: stash (#1420) (#1426)

- supports to use multi-line as stash message
- new style to display stashes

Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
leo 2025-06-18 12:32:13 +08:00
parent b06a4cbb8a
commit 6d682ac409
No known key found for this signature in database
19 changed files with 219 additions and 54 deletions

View file

@ -54,8 +54,8 @@ namespace SourceGit.Views
var color = COLOR[idx];
var hsl = color.ToHsl();
var color2 = ActualThemeVariant == ThemeVariant.Dark
? new HslColor(hsl.A, hsl.H,hsl.S, hsl.L - 0.1).ToRgb()
: new HslColor(hsl.A, hsl.H,hsl.S, hsl.L + 0.1).ToRgb();
? new HslColor(hsl.A, hsl.H, hsl.S, hsl.L - 0.1).ToRgb()
: new HslColor(hsl.A, hsl.H, hsl.S, hsl.L + 0.1).ToRgb();
var background = new LinearGradientBrush
{

View file

@ -21,7 +21,7 @@
<v:CommitMessageTextBox Height="120" Margin="8,5,8,0" Text="{Binding Message, Mode=TwoWay}"/>
<TextBlock Margin="8"
Text="{DynamicResource Text.Reword.Tip}"
Text="{DynamicResource Text.PopupEnterKeyTip}"
TextWrapping="Wrap"
Foreground="{DynamicResource Brush.FG2}"/>
</StackPanel>

View file

@ -31,7 +31,7 @@
<v:CommitMessageTextBox Height="120" Margin="0,4,0,0" Text="{Binding Message, Mode=TwoWay}"/>
<TextBlock Margin="0,6,0,0"
Text="{DynamicResource Text.Reword.Tip}"
Text="{DynamicResource Text.PopupEnterKeyTip}"
TextWrapping="Wrap"
Foreground="{DynamicResource Brush.FG2}"/>
</StackPanel>

View file

@ -12,23 +12,12 @@
<TextBlock FontSize="18"
Classes="bold"
Text="{DynamicResource Text.Stash.Title}"/>
<Grid Margin="8,16,0,0" RowDefinitions="32,32,Auto,Auto,Auto" ColumnDefinitions="120,*">
<Grid Margin="8,20,0,0" RowDefinitions="32,Auto,Auto,Auto,Auto,Auto" ColumnDefinitions="100,356">
<TextBlock Grid.Row="0" Grid.Column="0"
HorizontalAlignment="Right"
Margin="8,0"
Text="{DynamicResource Text.Stash.Message}"/>
<TextBox Grid.Row="0" Grid.Column="1"
Height="26"
CornerRadius="3"
Text="{Binding Message, Mode=TwoWay}"
Watermark="{DynamicResource Text.Stash.Message.Placeholder}"
v:AutoFocusBehaviour.IsEnabled="True"/>
<TextBlock Grid.Row="1" Grid.Column="0"
HorizontalAlignment="Right"
Margin="8,0"
Text="{DynamicResource Text.Stash.Changes}"/>
<ComboBox Grid.Row="1" Grid.Column="1"
Text="{DynamicResource Text.Stash.Mode}"/>
<ComboBox Grid.Row="0" Grid.Column="1"
MinHeight="28"
Padding="8,0"
ItemsSource="{Binding Source={x:Static m:DealWithChangesAfterStashing.Supported}}"
@ -48,15 +37,38 @@
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock Grid.Row="1" Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Top"
Margin="0,8,8,0"
Text="{DynamicResource Text.Stash.Message}"/>
<TextBox Grid.Row="1" Grid.Column="1"
Height="100"
Margin="0,4,0,0"
CornerRadius="3"
Text="{Binding Message, Mode=TwoWay}"
Watermark="{DynamicResource Text.Stash.Message.Placeholder}"
AcceptsReturn="True"
AcceptsTab="True"
VerticalContentAlignment="Top"
Padding="4"
v:AutoFocusBehaviour.IsEnabled="True"/>
<CheckBox Grid.Row="2" Grid.Column="1"
<TextBlock Grid.Row="2" Grid.Column="1"
Classes="small"
Margin="0,2,4,4"
Text="{DynamicResource Text.PopupEnterKeyTip}"
TextWrapping="Wrap"
Foreground="{DynamicResource Brush.FG2}"/>
<CheckBox Grid.Row="3" Grid.Column="1"
Height="32"
Content="{DynamicResource Text.Stash.OnlyStagedChanges}"
IsChecked="{Binding OnlyStaged, Mode=TwoWay}"
IsVisible="{Binding !HasSelectedFiles}"
ToolTip.Tip="--staged"/>
<CheckBox Grid.Row="3" Grid.Column="1"
<CheckBox Grid.Row="4" Grid.Column="1"
Height="32"
Content="{DynamicResource Text.Stash.IncludeUntracked}"
IsChecked="{Binding IncludeUntracked, Mode=TwoWay}"
@ -69,7 +81,7 @@
</CheckBox.IsVisible>
</CheckBox>
<TextBlock Grid.Row="4" Grid.Column="1"
<TextBlock Grid.Row="5" Grid.Column="1"
Margin="0,4,0,0"
Text="{DynamicResource Text.Stash.TipForSelectedFiles}"
TextWrapping="Wrap"

View file

@ -0,0 +1,135 @@
using System;
using System.Globalization;
using System.Text.RegularExpressions;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
namespace SourceGit.Views
{
public partial class StashSubjectPresenter : Control
{
public static readonly StyledProperty<FontFamily> FontFamilyProperty =
AvaloniaProperty.Register<StashSubjectPresenter, FontFamily>(nameof(FontFamily));
public FontFamily FontFamily
{
get => GetValue(FontFamilyProperty);
set => SetValue(FontFamilyProperty, value);
}
public static readonly StyledProperty<double> FontSizeProperty =
AvaloniaProperty.Register<StashSubjectPresenter, double>(nameof(FontSize), 13);
public double FontSize
{
get => GetValue(FontSizeProperty);
set => SetValue(FontSizeProperty, value);
}
public static readonly StyledProperty<IBrush> ForegroundProperty =
AvaloniaProperty.Register<StashSubjectPresenter, IBrush>(nameof(Foreground), Brushes.White);
public IBrush Foreground
{
get => GetValue(ForegroundProperty);
set => SetValue(ForegroundProperty, value);
}
public static readonly StyledProperty<IBrush> PrefixBackgroundProperty =
AvaloniaProperty.Register<StashSubjectPresenter, IBrush>(nameof(PrefixBackground), Brushes.Transparent);
public IBrush PrefixBackground
{
get => GetValue(PrefixBackgroundProperty);
set => SetValue(PrefixBackgroundProperty, value);
}
public static readonly StyledProperty<string> MessageProperty =
AvaloniaProperty.Register<StashSubjectPresenter, string>(nameof(Message));
public string Message
{
get => GetValue(MessageProperty);
set => SetValue(MessageProperty, value);
}
public override void Render(DrawingContext context)
{
base.Render(context);
var message = Message ?? string.Empty;
if (string.IsNullOrEmpty(message))
return;
var subjectIdx = message.IndexOf('\n', StringComparison.Ordinal);
var subject = subjectIdx > 0 ? message.Substring(0, subjectIdx).Trim() : message;
var typeface = new Typeface(FontFamily, FontStyle.Normal, FontWeight.Normal);
var foreground = Foreground;
var x = 0.0;
var h = Bounds.Height;
var prefix = null as FormattedText;
var match = REG_KEYWORD_ON().Match(subject);
if (match.Success)
{
prefix = new FormattedText(match.Groups[1].Value, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, typeface, 11, foreground);
subject = subject.Substring(match.Length);
}
else
{
match = REG_KEYWORD_WIP().Match(subject);
if (match.Success)
{
prefix = new FormattedText($"WIP | {match.Groups[1].Value}", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, typeface, 11, foreground);
subject = subject.Substring(match.Length);
}
}
if (prefix != null)
{
var pw = prefix.WidthIncludingTrailingWhitespace;
var ph = prefix.Height;
var bh = ph + 4;
var bw = pw + 12;
context.DrawRectangle(PrefixBackground, null, new RoundedRect(new Rect(0, (h - bh) * 0.5, bw, bh), new CornerRadius(bh * 0.5)));
context.DrawText(prefix, new Point(6, (h - ph) * 0.5));
x = bw + 4;
}
var body = new FormattedText(subject, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, typeface, FontSize, foreground);
context.DrawText(body, new Point(x, (h - body.Height) * 0.5));
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == MessageProperty ||
change.Property == FontFamilyProperty ||
change.Property == FontSizeProperty ||
change.Property == ForegroundProperty ||
change.Property == PrefixBackgroundProperty)
{
InvalidateVisual();
}
}
protected override Size MeasureOverride(Size availableSize)
{
var typeface = new Typeface(FontFamily, FontStyle.Normal, FontWeight.Normal);
var test = new FormattedText("fgl|", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, typeface, FontSize, Brushes.White);
var h = Math.Max(18, test.Height);
return new Size(availableSize.Width, h);
}
[GeneratedRegex(@"^On ([^\s]+)\: ")]
private static partial Regex REG_KEYWORD_ON();
[GeneratedRegex(@"^WIP on ([^\s]+)\: ([a-f0-9]{6,40}) ")]
private static partial Regex REG_KEYWORD_WIP();
}
}

View file

@ -95,7 +95,12 @@
<TextBlock Grid.Column="1" Classes="primary" Text="{Binding TimeStr}" Foreground="{DynamicResource Brush.FG2}" Margin="8,0,0,0"/>
</Grid>
<TextBlock Grid.Row="1" Classes="primary" Text="{Binding Message}" VerticalAlignment="Bottom"/>
<v:StashSubjectPresenter Grid.Row="1"
Message="{Binding Message}"
Foreground="{DynamicResource Brush.FG1}"
FontSize="{Binding Source={x:Static vm:Preferences.Instance}, Path=DefaultFontSize}"
PrefixBackground="{DynamicResource Brush.InlineCode}"
VerticalAlignment="Bottom"/>
</Grid>
</Border>
</DataTemplate>